Cloudflare Zero Trust and SASE - Lab Guide

⬇ Course syllabus (PDF)
⚠ Read this first: you'll need it every time you change networks

The lab's AWS Security Groups allow inbound RDP (Windows) and SSH (Ubuntu) only from the public IP of the network you deployed from. The moment you move to a different network (home to office, office to classroom, or your ISP rotates your address), RDP and SSH to the lab VMs stop working and you get connection timeouts.

You RDP into the Windows desktop and SSH into the Ubuntu Server throughout the labs, so refresh your IP before you start Lab 1.

There is one command for everything. Run the same lab installer for your operating system any time you need it: to deploy the lab the first time, to update your IP, or to tear the lab down at the end of class. You never need to know where the lab files live or type any other command.

Windows (PowerShell):

irm https://psbc-lab-deploy.cloudflareacademy.com/win | iex

macOS / Linux:

curl -fsSL https://psbc-lab-deploy.cloudflareacademy.com/mac | bash

The installer sets up your toolchain, finds your lab in AWS, then shows a short menu:

  • If your lab is already deployed: [1] Update my IP   [2] Destroy lab   [3] Quit. Choose 1 to unlock RDP and SSH from wherever you are now: it detects your current public IP, confirms the change, and rewrites the RDP and SSH rules on the security groups.
  • If no lab exists yet: [1] Deploy new lab   [2] Quit.

Works from any computer. Because your lab's state lives in your own AWS account, running the same command on a second machine finds your deployed lab automatically and offers the same menu, so you can update your IP or destroy the lab without getting back to the machine you deployed from. The first time you run it on a new computer it will ask for your AWS access key, secret, and region.

Only RDP and SSH are IP-locked. The AcmeCorp website's HTTP on ports 80 and 8000 stays open to the public internet until Lab 11, so the website itself keeps working across network changes.

The AcmeCorp Scenario

If you came from the Application Security course, you already secured everything the public touches at AcmeCorp: the website and the orders API, sitting behind WAF, bot management, rate limiting, and API Shield, with the origin sealed so nobody can walk around it. This course is the other half of the same company. AcmeCorp still runs like an old flat network on the inside: employees connect over a VPN, and once they're on it they're trusted by default, internal apps answer to anyone who can reach them by IP, unmanaged devices join without anyone checking their health, and nothing inspects what leaves the building. You've just been hired to run AcmeCorp's Zero Trust rollout, and your job is to rebuild the inside of the company around one rule: trust nothing by default, verify every user and every device, every time, and control every byte in and out.

Over the next 18 labs you'll give every employee a verifiable identity, put the Cloudflare One Client on every device, check each device's health before it's allowed near anything, publish internal apps without opening a single inbound port, put an access decision in front of every one of them, filter traffic in both directions, then prove the old direct paths are gone and extend the same controls to servers. Reading the lab titles top to bottom is the whole build. Every lab opens with a "This lab" box, highlighted in yellow, that says which piece you're building and why, and roughly how long it takes.

Lab Environment Overview

The lab has two endpoints on separate networks. They cannot reach each other without a Cloudflare Tunnel.

Ubuntu origin at 10.91.0.91 (called "Ubuntu Server" throughout this guide):

Windows desktop at 10.90.0.90:

Credentials

The password for every account in this lab is #Savetheinternet. It does not change.

VM console logins (Windows and Ubuntu)

Use these to sign in to either lab VM: RDP to the Windows VM, SSH to the Ubuntu VM. Find each VM's public IP in the AWS Console under EC2 > Instances (the instances are named lab-windows and lab-ubuntu).

MachineUsernamePassword
Windows desktop (10.90.0.90)cloudflare#Savetheinternet
Ubuntu origin (10.91.0.91)cloudflare#Savetheinternet

SAML identity provider users

Use these when Cloudflare prompts for SAML authentication during Cloudflare One Client enrollment, Access app login, or App Launcher access.

UsernamePasswordPurpose
itadmin@acmecorp.com#SavetheinternetAdmin user; SAML test (Step 1.4) and initial Cloudflare One Client enrollment (Step 3.3)
usera@acmecorp.com#SavetheinternetPrimary ZTNA test user (Labs 1, 2, 3)
userb@acmecorp.com#SavetheinternetSecondary user for multi-user-on-one-device testing (Step 9.3)

Local lab user accounts (UserA / UserB)

Two additional local accounts already exist on both the Windows desktop and the Ubuntu Server. Their names match the SAML identities usera@acmecorp.com and userb@acmecorp.com so identity-based policies map cleanly to local OS sessions. Step 9.3 verifies they exist; you do not need to create them.

UsernamePasswordAvailable on
UserA#SavetheinternetWindows desktop, Ubuntu Server
UserB#SavetheinternetWindows desktop, Ubuntu Server

General Tips

In browser-based VM consoles, paste using right-click, not Ctrl+V. Ctrl+V will not work.

When copying multi-line commands, paste them one line at a time. Concatenated paste often breaks the installer.

Throughout the guide, <team-name> auto-substitutes once you fill it into the orange Fill in here form in Step 1.1.

Throughout the guide, <your-domain> auto-substitutes once you fill it into the orange Fill in here form in Step 1.7.

Lab 1 - Identity & Account Baseline

Before Zero Trust can bind a policy to anyone, AcmeCorp has to agree on who vouches for identities and turn its inspection engine on. You connect Cloudflare to the shared lab SAML identity provider so every login checks against one trusted source, then flip on the account-wide baseline the rest of the course depends on: the proxy, TLS decryption, protocol detection, host selectors, and malware scanning. It's the least glamorous lab in the course, and every lab after it assumes it's already done. You'll also confirm you can read the eight log datasets under Insights & Logs > Logs, because when something breaks later, that's where you'll be looking. Estimated time: 20-30 minutes.
Prerequisites:
  • Network access to the Windows and Ubuntu lab VMs (RDP and SSH respectively)
  • Your team name (you will find it in Step 1.1)

1.1 Identify your team name

Why: Almost every URL, MSI parameter, and SAML configuration in this lab embeds your team name. Capture it now so you do not stop later to look it up.

Open the Cloudflare dashboard at https://dash.cloudflare.com and sign in.

In the account sidebar, scroll down to the Protect & Connect section and click Zero Trust. You arrive at the Cloudflare One Overview page.

In the Zero Trust sidebar (left side), click Settings.

On the Settings page, the default filter is All. The first card shown is Team name and domain. Copy the Team name value and paste it into the field below.

Fill in here: your team name

From this point on, every <team-name> placeholder across the entire guide is replaced with your value, including inside copy blocks, so when you click Copy you get the real command. Values save to your browser only; nothing crosses to other students.

Verify: The team URL https://<team-name>.cloudflareaccess.com loads a Cloudflare-branded sign-in page.

1.2 Create a SAML identity provider in the shared lab IdP

Why: The lab IdP needs to know about your specific Zero Trust org before it can issue assertions to you. Registering your team name at the shared lab IdP creates a SAML provider tied to your team.

Open a new browser tab and go to https://labs.cloudflare.com/saml/register

In the registration form, paste your team name in the format <team-name>.cloudflareaccess.com.

Click the register button to create the SAML provider.

On the confirmation page, click the Download link for the SAML metadata file. Save it somewhere you can find it (Downloads is fine).

Verify: The downloaded file is an XML document containing <EntityDescriptor> and <IDPSSODescriptor> elements.

1.3 Add the SAML provider to your Zero Trust account

Why: Downloading the metadata file is half the integration. Importing it into Zero Trust completes the trust relationship in both directions so your users can authenticate through the lab IdP.

Return to the Zero Trust dashboard tab.

In the sidebar, click Integrations to expand it.

Click Identity providers.

Click Add an identity provider.

From the list of identity provider types, click SAML.

In the SAML configuration form, drag and drop the SAML metadata XML file you downloaded in Step 1.2, or click the import area and select the file.

Cloudflare automatically populates the SAML configuration fields from the metadata. Leave them as imported.

Click Save.

Verify: The SAML provider appears in the Identity provider integrations list with the Test button next to it.

1.4 Test SAML authentication

Why: Catch IdP setup mistakes now. Every subsequent lab assumes this works.

Still on the Integrations > Identity providers page, find the SAML provider you just created in the list.

Click Test in the row.

A new browser tab opens to the shared lab IdP sign-in page.

Sign in with:

FieldValue
Usernameitadmin@acmecorp.com
Password#Savetheinternet

The browser returns to Cloudflare showing Your connection works! or equivalent success message.

Verify: You see a success page after authentication. If you see an error, the metadata file may have been corrupted or the team name may be misspelled. Repeat Steps 1.2 and 1.3.

1.5 Enable the account-wide baseline settings

Why: Many later labs assume Cloudflare Gateway is proxying traffic, TLS is being decrypted, protocol detection is on, and egress host selectors are available. Turn these on now so you do not chase mysterious failures later.

The settings live on two filter views of the Traffic settings page: Proxy and inspection holds proxy/TLS/protocol toggles, and Policy settings holds host selectors and malware scanning.

In the Zero Trust sidebar, click Traffic controls to expand it.

Click Traffic settings.

At the top of the page, click the Proxy and inspection filter button. Configure these toggles:

SettingValueNotes
Allow Secure Web Gateway to proxy trafficOnConfirm TCP, UDP, and ICMP checkboxes are all checked under "Select protocols to forward"
Inspect HTTPS requests with TLS decryptionOn
Allow protocol detectionOn
Manage HTTP inspection by portDefaultLeave as "Inspect on 80 and 443 only"

Click the Policy settings filter button. Configure these toggles:

SettingValueNotes
Allow egress policy host selectorsOn
Scan files for malwareOnAlso enable Scan on file upload and Scan on file download under the toggle
Verify: Reload the page. Both filter views (Proxy and inspection and Policy settings) show the toggles still set to On.

1.6 Confirm log access via Cloudflare One Logs page

Why: Later labs verify policy hits, blocked traffic, and posture results by reading logs. The Zero Trust dashboard's Logs page gives filterable access to all 8 datasets. This step doubles as the reference the later labs point back to: it names the exact result column and filter each dataset uses, so when a lab says "confirm the block in the logs," you already know which viewer to open and what to look for.

In the Zero Trust sidebar, click Insights & Logs to expand it, then click Logs. The Logs landing page shows 8 dataset cards. Click a card to open that dataset's viewer.

New Logs experience: The dashboard now opens the redesigned Logs viewers by default, and every step in this guide is written for them. Each viewer has a Return to old logs button in the top-right if you ever need the legacy layout, but you shouldn't need it. Two things carry across all the Gateway and Access viewers:
  • The enforcement result is in the Action column (Gateway: DNS, Network, HTTP) or the Decision column (Access), shown as a colored pill.
  • The policy that matched is in the Policy name column. A request only shows a policy name when it matched a named policy you created; implicit/default allows show --, which is normal.

The eight datasets, and what each one is for:

Dataset cardProductWhat it shows / when you use itResult column (values)
Admin activity logsAuditConfiguration changes: who created, updated, or deleted settings.n/a (audit trail)
Access authentication logsAccessLogin attempts to Access-protected apps. Used in the Access labs (self-hosted apps, App Launcher, private-network ZTNA).Decision (Allowed / Blocked)
DNS query logsGatewayDNS queries inspected by Gateway and the DNS policy that matched. Used in the internal-DNS, DNS-location, and DNS/HTTP-policy labs.Action (Allow / Block / Override / Bypass)
Network logsGatewayL3/L4 traffic inspected by Gateway and the network policy that matched. Used in the private-network ZTNA and network-policy labs.Action (Allow / Block / Bypass / Override)
HTTP request logsGatewayHTTP requests inspected by Gateway, plus HTTP-policy, isolation, and DLP matches. Used in the DNS/HTTP-policy, order-of-enforcement, and SWG/DLP labs.Action (Allow / Block / Isolate / Bypass / Override)
SSH command logsAccessSSH commands a user ran on a target through Access for Infrastructure.n/a (command record)
SCIM provisioning logsAccessIdentity sync between Access and your identity provider (90-day retention).n/a (sync record)
Posture logsDevice clientDevice posture check results. Used in the posture lab. This one opens a separate "Device posture logs" viewer (no Action column, no summary chart).Conditions met (Success / Failure)
How filtering works (it matches exactly): In every Gateway and Access viewer, + Add filter builds a Field / Operator / Value condition, and the only operators are equals and does not equal (there is no "contains"). So later labs filter by exact values:
  • By result: Field Action (Gateway) or Decision (Access) equals the value you want, e.g. Action equals Block.
  • By policy: Field Policy equals, then pick the exact policy name from the Value list.
  • By user: Field Email equals the exact user email.
Verify: All 8 dataset cards are visible on the Logs landing page. Open DNS query logs: the table shows Action and Policy name columns, and the summary counts Allow / Block / Override / Bypass appear across the top. Click + Add filter and confirm it offers Field / Operator / Value selectors with the Operator limited to equals / does not equal (results may be empty until later labs generate traffic).

1.7 Register your lab domain and create the website A record

Why: Most labs from Lab 6 onward publish services at custom hostnames like web.<your-domain> for the AcmeCorp public website. Cloudflare Access, Cloudflare Tunnel, and Cloudflare Gateway all need a real domain you control to do their work. This step gets that domain in place and creates the first DNS record: an A record for the public website that proves the lab environment is reachable end-to-end before you start layering Zero Trust on top.

Register a domain (and get it onto Cloudflare DNS)

If you don't already have a domain you can use for this lab, buy one. Any TLD works: .com, .dev, .cloud, whatever you like.

Recommended: Cloudflare Registrar. Cloudflare charges only the wholesale TLD cost (no markup), so it's typically the cheapest registrar. Some TLDs are surprisingly cheap; a .uk domain runs about $5.30 USD/year, for example. It's also the simplest path here: a domain registered through Cloudflare is automatically on Cloudflare DNS, so you can skip the nameserver-update step entirely. From the Cloudflare dashboard, in the account sidebar, click Domain Registration > Register Domains, search for the name you want, and check out. The domain is ready to use in your account immediately.

Already have a domain at another registrar? You can keep it. Add it to Cloudflare instead:

From the Cloudflare dashboard account home page, click + Add at the top of the account page (or follow Cloudflare's own walk-through at developers.cloudflare.com/fundamentals/setup/account/add-site/). Enter the domain, click Continue, and complete the setup wizard.

Cloudflare displays two nameservers (something like tim.ns.cloudflare.com and frances.ns.cloudflare.com). Log into your registrar's control panel, find the nameserver section, and replace the existing nameservers with the two Cloudflare gave you. Save the change at the registrar.

Return to the Cloudflare dashboard and click Done, check nameservers. Wait for the domain's status banner to flip from "Pending nameserver update" to Active, usually a few minutes to a couple of hours depending on the registrar.

Plan note: The lab assumes your domain is on the Enterprise plan with the Zero Trust Premier Bundle enabled (the pre-class prerequisites). If the zone is sitting on Free or Pro and you can't self-upgrade in the dashboard, contact your Cloudflare account team to flip it to Enterprise before Lab 6.
Verify: In the main Cloudflare dashboard sidebar (not the Zero Trust sidebar; if you're inside Zero Trust, click "Back to <your account>" at the top of the left sidebar first), click Domains > Overview. Find the row for your domain in the table:
  • The Status column shows Active.
  • The Plan column shows Enterprise.
Do not continue until both are true. If Status is "Pending" or Plan is anything other than Enterprise, the zone isn't ready for the labs.
Fill in here: your lab domain

Type the domain you just added to Cloudflare. From here on, every <your-domain> across the entire guide substitutes to your real domain, including the table below, every Access app, every tunnel route, and every link.

Find the Ubuntu Server's public IP

In the AWS Console, go to EC2 > Instances, click the instance named lab-ubuntu, and copy the Public IPv4 address from the Details panel into the field below.

Fill in here: Ubuntu Server public IP

Create the website A record

In the Cloudflare dashboard for your domain, click DNS > Records in the left sidebar. Click Add record and configure:

FieldValue
TypeA
Nameweb
IPv4 addressThe Ubuntu Server public IP you just copied
Proxy statusDNS only (gray cloud; click the orange cloud to toggle it to gray)
TTLAuto

Click Save. The new record appears in the list as web.<your-domain> with the gray "DNS only" cloud.

Why DNS only and not proxied? A proxied (orange cloud) record routes traffic through Cloudflare's edge before reaching the origin. Later labs are explicitly about layering Cloudflare in front of this server, through Cloudflare Tunnel (Lab 6) and Cloudflare Access (Lab 8). Keeping this initial record DNS only proves the underlying environment works without Cloudflare in the path; in Lab 8 you'll replace this A record with a tunneled CNAME and see the difference.
Verify: Open http://web.<your-domain> in a browser. The AcmeCorp public website loads. You can also run curl http://web.<your-domain> from PowerShell or a terminal; it returns the AcmeCorp HTML. If the request times out, your current public IP isn't in the Ubuntu Security Group's allowlist. Run the lab installer from the top of this guide and choose Update my IP to refresh it.

Lab 1 Wrap-up

By the end of Lab 1 you should have:

Milestone
☐A working SAML IdP integrated with your Zero Trust org
☐Account-wide baseline on (proxy, TLS decryption, protocol detection, egress host selectors, malware scan)
☐Log access confirmed via the Cloudflare One Logs page (8 dataset cards)
☐Domain registered, added to Cloudflare, and Active; web.<your-domain> A record (DNS only) pointing to the Ubuntu Server's public IP; http://web.<your-domain> loads the AcmeCorp website

Lab 1 Troubleshooting

SymptomLikely causeFix
SAML test in Step 1.4 fails with "invalid issuer"Metadata file was downloaded for a different team nameRepeat Steps 1.2 and 1.3 with the correct team name

Lab 1 to Lab 2

Lab 2 turns the account-wide baseline into a device-specific one: a Cloudflare One Client device profile scoped to the three lab users, with split-tunnel, local-domain-fallback, and enrollment-policy settings tuned for production.

Lab 2 - Device Profile & Enrollment Setup

Now Zero Trust knows who to trust, but not which devices are allowed to enroll or how the client should behave once it does. Before you hand out the Cloudflare One Client in the next lab, you configure it on paper: the global client settings, plus a device profile (Lab-Profile) scoped to your three lab identities with production-shaped behavior (users can't leave the org, auto-updates off, auto-connect on, split tunnel adjusted for Microsoft 365), and the enrollment policy that says only a verified SAML login from your IdP may join. Nothing enrolls yet. You're writing the rules the client will follow the moment it's installed. Estimated time: 20-30 minutes.
Prerequisites:
  • Lab 1 complete (SAML provider integrated, account-wide baseline on)

2.1 Configure Cloudflare One Client global settings

Why: The global settings apply to every Cloudflare One Client across the org. The three settings below are recommended baselines for any production deployment.

Return to the Zero Trust dashboard.

In the sidebar, click Team & Resources to expand it.

Click Devices.

Click the Management tab at the top of the page.

Click the Global device client settings filter button. Locate and configure the following toggles:

SettingValueWhy
Install CA to system certificate storeOnInstalls available certificates to the certificate store of enrolled Windows, macOS, and Linux devices. Devices need the certificate to inspect HTTPS traffic, display custom block pages, and apply other advanced features.
Assign a unique IP address to each deviceOnOverrides the device's existing local interface IP with a unique IP address randomly assigned from the Carrier Grade NAT space (100.64/10). On by default for MASQUE devices and required for WARP Connector tunnels.

Now click the Mesh connectivity filter button. Configure:

SettingValueWhy
Allow all Cloudflare One traffic to reach enrolled devicesOnWhen on, all traffic sent from your organization to Cloudflare can reach your enrolled devices. Manage traffic security through Gateway network policies. Update your split tunnel configurations to ensure traffic flows as expected.
Verify: All three settings are on across both filter tabs. Reload the page to confirm.

2.2 Create a new device profile

Why: The default device profile is reasonable for a quick test, but the lab requires settings that the default does not have. Creating a new profile with a matching expression scopes it to your lab users and preserves the Default profile as a fallback for any device that does not match.

Still under Team & Resources > Devices, click the Device profiles tab at the top of the page.

You arrive at the "View all device profiles" page. Confirm you see the Default profile in the list with a Profile name column and Status toggle.

Click + Add profile.

Give the profile a name: Lab-Profile.

Build the matching expression

Under Build an expression, configure two conditions joined by AND. Add every value as its own entry: click Copy on a block below, paste it into the Value field, press Enter, then move to the next block.

Condition 1 — Selector User email, Operator in. Add each address on its own:

itadmin@acmecorp.com
usera@acmecorp.com
userb@acmecorp.com

AND — Condition 2 — Selector Operating system, Operator in. Add each OS on its own:

windows
mac
linux
ios
android
chromeos
Check: Condition 1 shows three separate email entries and condition 2 shows six separate OS entries. If a Value shows one long entry instead, delete it and re-add each value on its own.

Click Save.

Open the new profile by clicking its name.

Verify: Lab-Profile appears in the device profiles list at position #1 (above Default at #2). The matching expression shows both conditions.

2.3 Configure the device profile settings

Why: These settings define how the Cloudflare One Client behaves on every enrolled device matched by this profile: whether users can disconnect, whether the client auto-updates, and whether it auto-connects on boot.

With your new profile open, scroll down to the Configure settings section.

Find Allow device to leave organization and turn it Off. This prevents users from unenrolling.

Find Allow updates and turn it Off. Updates will be managed via MSI in production; auto-update is not desirable in a controlled lab.

Find Auto connect and turn it On. Set the Timeout to 30 minutes.

Why 30 minutes and not 1? Auto connect forces the client back on after the timeout expires. In a lab environment you need to be able to disconnect the client for testing (e.g., verifying behavior with the client off, switching modes in Step 16.4). Setting 1 minute means the client reconnects before you can finish a test. 30 minutes gives you enough time to run exercises with the client disconnected. In production, 1 minute is the tightest setting; for the lab, 30 minutes is practical.
Why not 0? Setting Auto connect to 0 does not mean "reconnect immediately." Per the Cloudflare docs, 0 means "allow the switch to stay in the off position indefinitely until the user turns it back on." That is the least aggressive setting; it never auto-reconnects. Values 1 through 1440 are the ones that actually auto-reconnect.

Do not save yet. Steps 2.4 and 6.5 make more changes to this profile.

2.4 Adjust local domain fallback

Why: Local domain fallback tells Cloudflare One Client to send DNS queries for certain domains to the local resolver instead of through Cloudflare. The default list includes corp. Removing it ensures all DNS queries (including any .corp domains) route through Cloudflare Gateway, which is the expected behavior for a managed Zero Trust deployment.

Still on the profile page, scroll down to the Local Domain Fallback section (it appears above Split Tunnels) and click Manage. This opens a separate Local Domain Fallback page showing a list of 14 default domain entries.

Find the entry corp in the list. Click the three-dot menu (...) on the right side of the corp row and select Delete.

Verify: The Local Domain Fallback entries list shows 13 items (down from 14). Corp is gone.

Click Back to Profile at the top left to return to the device profile.

2.5 Configure split tunnel for the device profile

Why: Cloudflare One Client uses split tunnel in Exclude mode by default - traffic destined to listed IP ranges bypasses the Cloudflare tunnel and goes directly through the local network adapter. The default list excludes RFC1918 and CGNAT ranges. Adding Microsoft 365 domains to the exclude list ensures O365 traffic bypasses Cloudflare for performance reasons (a common production pattern).

Back on the profile page, scroll down to the Split Tunnels section (below Local Domain Fallback). You see four radio button options: Include IPs and domains, Exclude IPs and domains (should already be selected), Include IPs, and Exclude IPs. Confirm Exclude IPs and domains is selected.

Keep RDP connected in the AWS lab: The Cloudflare One Client will run inside the Windows EC2 VM you reach by RDP. When it connects, replies to your computer can enter the client tunnel and drop your RDP session. The VM's EC2 public IP does not change. Exclude your RDP computer's public IP from the tunnel before you connect the client in Step 3.3.

On the computer you use to RDP into the Windows VM, outside the VM, open https://checkip.amazonaws.com and note the public IPv4 address shown. Do not check the IP from a browser inside the Windows VM.

Back on Lab-Profile, click Manage in the Split Tunnels section. Select IP Address, enter your RDP computer's public IPv4 address with /32 appended (for example, 203.0.113.42/32), and click Save destination. Return to the profile. Do not enter the Windows VM's EC2 public IP or its Cloudflare egress IP.

Below the Split Tunnels section, find the Directly route Microsoft 365 traffic toggle. Turn it On. This automatically excludes Microsoft 365 IPs and domains from going through Cloudflare Zero Trust. The entries are appended every hour and will not be visible in the split tunnel list UI.

Click Save profile. This saves everything from Steps 2.3 and 6.5.

Verify: Reopen the profile. Allow device to leave organization is Off, Allow updates is Off, Auto connect is On with Timeout 30 minutes, Split Tunnels is set to Exclude IPs and domains with your RDP computer's public IPv4 address as a /32 exclusion, and Directly route Microsoft 365 traffic is On.

2.6 Configure Device Enrollment Permissions

Why: Enrollment Permissions decide which users are allowed to install and authenticate the Cloudflare One Client against your org. Without an enrollment policy, no one can enroll.

In the Zero Trust sidebar, click Team & Resources > Devices.

Click the Management tab at the top of the page.

Click the Device enrollment filter button.

In the Device enrollment permissions section, click the Manage button. This opens the Device enrollment permissions page.

You land on the All tab showing four filter tabs: All, Policies, Policy tester, Login methods. In the Device enrollment policies section, click Create new policy (or click Builder to switch to the policy builder view).

In the Policy details panel on the right side, configure:

FieldValue
Policy NameLab Enrollment
ActionAllow
Policy session durationSame as application session duration (default)

In the Policy rules panel on the left side, under Include:

FieldValue
SelectorLogin Methods
ValueSelect the SAML provider you created in Step 1.3

Click Save policy.

Scroll down to the Authentication section (marked NEW). Configure:

SettingValueEffect
Accept all available identity providersOffIn the Choose available identity providers for this application dropdown, select only your SAML provider. This forces SAML and prevents One-time PIN (OTP) from appearing as an enrollment option.
Apply instant authenticationOnAllows users to skip identity provider selection when only one login method is available, giving a smoother enrollment experience.

Click Save at the bottom of the page.

Verify: The Lab Enrollment policy appears in the Device enrollment policies list with action Allow. The Authentication section shows only your SAML provider selected and Apply instant authentication is On.

Lab 2 Wrap-up

By the end of Lab 2 you should have:

Milestone
☐Global Cloudflare One Client settings configured (CA install, unique IP per device, all-traffic-to-enrolled-devices)
☐A device profile (Lab-Profile) matching the three lab SAML identities and all six OSes
☐Profile lock-downs: no leave-org, no auto-updates, auto-connect with 30-minute timeout, split tunnel pointed at Microsoft 365
☐Local Domain Fallback trimmed (corp removed)
☐Device enrollment policy Lab Enrollment accepting SAML with instant authentication

Lab 2 Troubleshooting

SymptomLikely causeFix
Lab-Profile not appearing at the top of the profile listProfile was saved but never dragged into priority positionDrag Lab-Profile above Default on the Device profiles page
Enrollment policy in Step 2.6 still allows OTPAccept all available identity providers left OnToggle it Off and in the dropdown select only your SAML provider

Lab 2 to Lab 3

Lab 3 installs the Cloudflare One Client on the Windows desktop using the silent MSI flow that an MDM (SCCM, Group Policy, or Intune) would use to push the client to thousands of devices in production.

Lab 3 - Deploy the Cloudflare One Client

The rules are written, so now you actually deploy the client. Real companies don't ask thousands of employees to install it by hand or type the team name on each machine, so you push it the way SCCM, Group Policy, or Intune would: a silent MSI install with the ORGANIZATION parameter baked into the command. You enroll the Windows desktop with a SAML login as itadmin, confirm Gateway is now processing the device's internet traffic, and seed mdm.xml with the multi_user and pre_login keys you'll switch on in Lab 9. From here the device is enrolled, and every request it makes is attributable. Estimated time: 45-60 minutes.
Prerequisites:
  • Labs 1 and 2 complete
  • Network access to the Windows desktop (you will RDP in during Step 3.1)

3.1 Sign in to the Windows desktop console

Why: Subsequent steps install and configure the Cloudflare One Client directly on the Windows client. You need a logged-in session for that.

To find the Windows VM's public IP: in the AWS Console, go to EC2 > Instances, click the instance named lab-windows, and copy the Public IPv4 address from the Details panel.

RDP to that public IP. Sign in as cloudflare / #Savetheinternet (the credentials table at the top of this guide has these). If RDP can't connect right after the deploy finishes, give the VM a minute or two to finish booting, then retry.

Verify: You are on the Windows desktop with the taskbar and start menu visible. The system tray does not show a Cloudflare WARP icon yet; you'll install it in the next step.

3.2 Install Cloudflare One Client via MSI with the organization parameter

Why: Production deployments use the silent MSI flag with the ORGANIZATION parameter so the client knows which org to enroll against without prompting the user. This is the pattern an MDM (SCCM, Group Policy, or Intune) would use to push the client to thousands of devices.

On the Windows desktop, open a web browser.

Download the Cloudflare One Client MSI installer from https://downloads.cloudflareclient.com/v1/download/windows/ga. Save to Downloads.

Right-click the Start menu and select Terminal (Admin) or Windows PowerShell (Admin).

Approve the UAC prompt.

In the admin terminal, navigate to Downloads:

cd C:\Users\cloudflare\Downloads

Check your Downloads folder for the actual MSI filename (the Cloudflare One Client version number changes with each release). Fill it in here so the install command below picks it up:

Fill in here

Run the MSI install with the ORGANIZATION parameter:

msiexec /i "<warp_msi_filename>" /qn ORGANIZATION="<team-name>" SUPPORT_URL="http://support.example.com"

For example, if the downloaded file is Cloudflare_WARP_2026.4.1390.0.msi and your team name is popular-oracle:

msiexec /i "Cloudflare_WARP_2026.4.1390.0.msi" /qn ORGANIZATION="popular-oracle" SUPPORT_URL="http://support.example.com"

The /qn flag makes the install silent. The ORGANIZATION parameter pre-configures the client with your team name so the user is not prompted for it. Wait for the install to complete. The Cloudflare One Client window appears within 30-60 seconds showing "What would you like to use WARP for?" with two options: Private browsing (the consumer 1.1.1.1 product) and Cloudflare One Client (the Zero Trust product).

If the MSI fails to install on this Windows Server VM: Open Server Manager > Manage > Add Roles and Features > Features and install Wireless LAN Service. Make sure WLAN AutoConfig is running, then retry the same MSI command above.

Select Cloudflare One Client by clicking Continue on the right side.

The next screen says "Enroll your device with Cloudflare One Client" and asks "What is your team name?". The field may already be pre-filled with your team name from the ORGANIZATION parameter in the MSI command. If not, type your team name (e.g., popular-oracle). Click Continue.

Verify: The Cloudflare One Client icon appears in the system tray. A browser tab opens for SAML authentication.

3.3 Authenticate Cloudflare One Client via SAML

Why: The MSI install and team name entry configured which org the client enrolls against. Now the user authenticates via SAML to prove their identity.

After clicking Continue in Step 3.2, a browser tab opens to the Trusted SAML Server login page. The page title shows "Login to continue to Trusted SAML Server | <team-name>."

Sign in with:

Email or Username: itadmin@acmecorp.com

Click Log in.

Password: #Savetheinternet

If sign-in fails on this Windows Server VM: Check whether Windows has classified the VM's network as Guest/Public. If so, change the network category to Private while keeping Windows Defender Firewall enabled, then retry the sign-in.

The browser shows a "Successfully logged in!" notification and redirects to a Cloudflare Access success page with confetti and a Success! heading. You may see a prompt to "Open Cloudflare WARP"; click it if it appears.

Switch back to the Cloudflare One Client window. It now shows Disconnected with "Your Internet is not protected." and a Connect button. The left sidebar shows Home, Connectivity, Settings, Profile, and About.

Click the Connect button.

The client connects and shows Connected with "Your Internet is protected." and an orange ring around the Cloudflare cloud icon with a shield checkmark.

If RDP drops when the client connects:
  • On your own computer, outside the VM, check your current public IPv4 address at https://checkip.amazonaws.com. In the Zero Trust dashboard, open Team & Resources > Devices > Device profiles > Lab-Profile > Split Tunnels > Manage. Confirm Exclude IPs and domains is selected and that your computer's current IPv4 address is excluded as /32. Add it if missing and save. Allow up to 10 minutes for the Windows client to receive the change, then RDP to the same EC2 public IP.
  • If your own computer's public IP also changed, use the installer menu's Update my IP option from Read this first: changing networks to refresh the AWS RDP security-group rule. Run it on your own computer, not inside the VM.
Verify: The Cloudflare One Client shows Connected / "Your Internet is protected." Click Settings in the left sidebar to confirm the team name matches yours.

3.4 Confirm the device appears in the dashboard

Why: A connected client must also appear in the Zero Trust dashboard before it can match posture policies or receive Gateway rules.

Return to the Zero Trust dashboard in your browser.

In the sidebar, click Team & Resources.

Click Devices.

Click the Your devices tab.

Find the Windows device in the list. It is identified by the Windows hostname (something like win-desktop).

Verify: The device row shows status Connected, the assigned user is itadmin@acmecorp.com, and the OS is Windows.

3.5 Confirm Gateway is processing internet traffic

Why: The whole point of the Cloudflare One Client is to route the device's traffic through Cloudflare Gateway. A simple "what is my IP" check confirms the routing works before any policy gets in the way.

On the Windows desktop, open the browser.

Navigate to https://www.whatismyip.com

The page displays your public IP address.

Verify: The IP address shown belongs to Cloudflare (the ASN reported on the page is Cloudflare or AS13335). If it shows your VM's public IP instead, Cloudflare One Client is not tunneling correctly. Recheck Step 3.3.

3.6 Override the MDM configuration file

Why: Some Cloudflare One Client features are not yet exposed in the dashboard's device profile UI. The mdm.xml file overrides client behavior locally. The lab uses it to enable two things that the GUI cannot yet: multi_user (multiple Windows users share one device enrollment) and pre_login (a service-token-authenticated device session before any human user logs in to Windows). The pre_login placeholders are filled in during Lab 9 (Step 9.8) after browser-rendered RDP is set up. The multi-user and pre-login flows are tested in Lab 9 Steps 9.3-9.9.

On the Windows desktop, open PowerShell in admin mode and navigate to:

cd C:\ProgramData\Cloudflare

If a file mdm.xml exists, back it up:

copy mdm.xml mdm.xml.bak

Open Notepad as administrator. Right-click the Start menu, click Run, type notepad, then Ctrl+Shift+Enter to elevate.

In Notepad, open C:\ProgramData\Cloudflare\mdm.xml.

Replace the entire file contents with the following XML. Replace <team-name> (in two places) with your actual team name.

<dict>
  <key>multi_user</key>
  <true/>
  <key>pre_login</key>
  <dict>
    <key>organization</key>
    <string><team-name></string>
    <key>auth_client_id</key>
    <string>placeholder-will-fill-in-step-9.8</string>
    <key>auth_client_secret</key>
    <string>placeholder-will-fill-in-step-9.8</string>
  </dict>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string><team-name></string>
      <key>display_name</key>
      <string>Default</string>
      <key>support_url</key>
      <string>http://support.example.com</string>
      <key>onboarding</key>
      <false/>
      <key>allow_updates</key>
      <false/>
    </dict>
  </array>
</dict>

Save the file: click File > Save As, navigate to C:\ProgramData\Cloudflare, change Save as type to All files, set the file name to mdm.xml, set Encoding to UTF-8, and click Save. If Notepad defaults to "Text Documents (*.txt)", it will save as mdm.xml.txt which the client will not read. Close Notepad.

The Cloudflare One Client detects the new mdm.xml automatically and restarts itself. No manual restart is needed.

Verify: The Cloudflare One Client returns to Connected state within a minute. (No visible feature change yet; the multi-user and pre-login behaviors are tested in Lab 9 Steps 9.3-9.9, after browser-rendered RDP is set up.)

Lab 3 Wrap-up

By the end of Lab 3 you should have:

Milestone
☐Cloudflare One Client deployed via silent MSI to the Windows desktop with the ORGANIZATION parameter
☐SAML-authenticated enrollment as itadmin@acmecorp.com
☐Device visible in the dashboard at Team & Resources > Devices
☐Gateway processing internet traffic (whatismyip.com shows a Cloudflare IP)
☐mdm.xml deployed with multi_user and pre_login placeholders (real values land in Lab 9)

Lab 3 Troubleshooting

SymptomLikely causeFix
MSI install in Step 3.2 hangsEarlier uninstall did not fully completeReboot the Windows VM. Re-run msiexec
Client MSI fails to install on the AWS Windows Server VM (Step 3.2)The Wireless LAN Service feature is missing, so WLAN AutoConfig is unavailable to this client buildIn Server Manager > Manage > Add Roles and Features > Features, install Wireless LAN Service. Make sure the WLAN AutoConfig service is running, then retry the MSI install.
Client sign-in fails on the AWS Windows Server VM (Step 3.3)The VM may be using the Guest/Public Windows network profileIf Windows shows Guest/Public for the VM's network, change its network category to Private, leave Windows Defender Firewall enabled, and retry sign-in.
Cloudflare One Client shows Disconnected after sign-inDefault browser had a stale session or the MSI did not pick up ORGANIZATIONRight-click the WARP icon > Re-authenticate. If that fails, uninstall and re-run the MSI
whatismyip.com shows the VM's public IP instead of a Cloudflare IP (Step 3.5)Cloudflare One Client is not tunneling correctlyRecheck Step 3.3 (SAML auth) and confirm the client is Connected
Cloudflare One Client doesn't pick up the new mdm.xmlFile saved as mdm.xml.txt instead of mdm.xmlSave As with Save as type: All files and UTF-8 encoding

Lab 3 to Lab 4

Lab 4 layers visibility on top of the enrolled client: a Firewall posture check, four DEX synthetic tests, three DEX notifications, and the two ways to collect warp-diag output for support cases.

Lab 4 - Posture, DEX & warp-diag

A device that's connected isn't the same as a device that's healthy. You set up the ways to measure and troubleshoot it: a Firewall posture check that flips to Fail the moment the firewall goes off (you'll weaponize that in Lab 13), four DEX synthetic tests that tell you whether a slow app is the device, the network, the Cloudflare edge, or the destination, three DEX notifications covering fleet connectivity and application latency and availability, and warp-diag collected both ways, remotely from the dashboard and locally on the box for when a user calls support. Estimated time: 30-40 minutes.
Prerequisites:
  • Labs 1 through 3 complete
  • Cloudflare One Client connected on the Windows desktop, enrolled as itadmin@acmecorp.com

4.1 Create device posture checks

Why: Posture checks measure attributes of the enrolled device (OS version, firewall state, disk encryption, domain membership, etc.) and feed those results into Access and Gateway policies. The lab uses one check: Firewall to detect a disabled Windows Firewall.

In the Zero Trust sidebar, click Reusable components to expand it.

Click Posture checks.

Confirm you are on the Cloudflare One Client checks tab (the default).

Click Add a check.

On the Add device posture attribute page, click the Firewall tile.

The "Add Firewall posture check" page opens with three fields:

Name (Required): enter Firewall Check

Operating system: select Windows

ENABLE FIREWALL CHECK: toggle this On

Click Save.

Verify: The Firewall Check appears in the Posture checks list. Navigate to Insights & Logs > Logs > Posture logs. The Firewall Check result shows Pass (the Windows Firewall is on by default).
Polling frequency: By default, the Cloudflare One Client polls posture checks every 5 minutes. Gateway adds its own 5-minute cache on top, so at the default schedule, Gateway policies may take up to 10 minutes to reflect a posture change. The polling frequency can be adjusted via the API using the schedule parameter on the Update device posture rule endpoint (minimum 1m); there is no dashboard UI for this. In production deployments, shorter polling intervals mean faster enforcement but more device overhead.

4.2 Create DEX synthetic tests

Why: DEX (Digital Experience Monitoring) runs synthetic probes from enrolled devices to specified endpoints. The probes generate latency, success rate, and traceroute data that helps you identify whether problems are on the device, the network, the Cloudflare edge, or the destination.

In the Zero Trust sidebar, click Insights & Logs to expand it.

Click Digital experience.

Click the Tests tab at the top of the page.

Click Create a test. The "Add a Test" page opens. Five sections are shown: Basic information, Resource configuration, HTTP request configuration (optional), Advanced settings, and DEX rules. For this lab, only Basic information and Resource configuration need values; leave the rest at defaults.

Create all four tests below. For each one, fill in the Name, Description (optional), Test type, Test frequency, Resource (External target), and Target fields. Test type defaults to HTTP Get and cannot be changed once the test is created. Test frequency defaults to 30 minutes; change it to 5 for every test. When done, click Add test at the bottom right, then click Create a test for the next.

#NameTest typeFrequencyResourceTarget
1Traceroute - InternetTraceroute5 minExternal target1.1.1.1
2Traceroute - Ubuntu ServerTraceroute5 minExternal target10.91.0.91
3HTTP - InternetHTTP Get5 minExternal targethttps://www.cloudflare.com
4HTTP - Ubuntu ServerHTTP Get5 minExternal targethttp://10.91.0.91

Tests 2 and 4 target the Ubuntu Server private IP. They will show errors until Lab 6 builds the tunnel; this is expected.

Verify: Four DEX tests appear in the Tests list. The two internet-facing tests begin reporting data. The two Ubuntu Server tests show errors until Lab 6 completes; this is expected.

4.3 Configure DEX notifications

Why: Synthetic tests are useful in isolation but more useful when they alert. DEX notifications fire when a test crosses a threshold (high latency, low availability, connectivity anomaly). DEX notifications are configured at the account level (outside Cloudflare One).

If the Zero Trust sidebar has a Back to <your account> link at the top, click it to return to the account-level dashboard. If the account-level sections already appear above Zero Trust in the same sidebar, stay there.

In the account sidebar, expand Manage account (near the bottom) and click Alerts. If your account instead shows Observability (above Build), expand Observability > Alerts and click All alerts; on the account-level Alerts page, this link may read Overview.

On the Manage account > Alerts page, click Add, filter Product to Digital Experience, and click Select next to the alert type. In the Observability > Alerts layout, click Create an alert, search for DEX tests or Device connectivity, and select the alert type.

Create all three alerts below. For each one, enter the Alert name, leave the alert threshold or configuration at its default, click + Add email recipient under Notification email to add itadmin@acmecorp.com, then click Save. Return to the alerts list and click Add (or Create an alert) for the next one.

#Alert typeAlert name
1DEX tests availability (beta)DEX - Application availability
2DEX tests latency (beta)DEX - Application latency
3Device connectivity (beta)DEX - Fleet connectivity

Some older alert tables label these types Application availability, Application latency, and Fleet connectivity, respectively.

Verify: Three DEX alerts appear in the account-level Alerts or Notifications list, each tied to a different Digital Experience alert type.

When done, navigate back to Cloudflare One via Protect & Connect > Zero Trust in the account sidebar.

4.4 Collect warp-diag from the dashboard (remote collection)

Why: warp-diag is the equivalent of a "show tech-support" for the Cloudflare One Client. It bundles client logs, configuration, network state, and DNS state into a zip. Remote collection means you trigger it from the dashboard without needing the user to do anything.

If you are still on the account-level Alerts or Notifications page from Step 4.3, navigate back to the Zero Trust dashboard: in the account sidebar, click Protect & Connect > Zero Trust.

In the Zero Trust sidebar, click Insights & Logs > Digital experience.

Click the Diagnostics tab at the top of the page.

Click Run diagnostics. The Run diagnostics page opens.

In the Select devices list, check your Windows device. The list shows Device name, User name, Status, User Email, Client version, and Last seen. Note that devices on client versions older than 2024.12.492.0 won't appear here.

Under Capture configuration, Device speed test and Device diagnostic logs are checked by default. Leave both checked; Device diagnostic logs is the one that initiates the warp-diag command remotely (file size up to 100 MB).

Optionally also check Test all routes for broader diagnostics, or Packet captures (PCAP) if you want a pcap as well.

Click Run diagnostics at the bottom right.

Wait for the collection to complete. The diagnostic appears in the Diagnostics list with status changing from In progress to Completed.

Click the completed diagnostic to download the zip file.

Verify: The downloaded zip contains warp-diag output files (warp-cli output, network state, DNS state).

4.5 Collect warp-diag locally

Why: When a device is offline or you cannot reach the dashboard, the same diagnostic is collectible from the device itself. This is the path support most often uses.

On the Windows desktop, open PowerShell in admin mode.

Run:

warp-diag

Wait for the command to finish. It writes a zip file to the current directory and prints the path.

Take note of the file path. You can leave it on the desktop or copy it elsewhere.

Verify: The terminal output reports the zip file location. The zip exists on disk and is non-empty.

4.6 Analyze warp-diag with online tools

Why: The raw zip is several text files. The community-maintained log viewer parses them into something readable.

On your local machine (not the lab VM), open a browser to https://warp-diag-log-viewer.pages.dev/. The visible heading is Warp Diag Log Explorer (the browser tab says Warp Diag Viewer).

Click the Choose File button and select the warp-diag zip you collected in Step 4.4 from your local Downloads folder.

Review the parsed sections: device info, network state, recent log entries, DNS queries, gateway IPs.

Verify: The viewer renders the warp-diag contents in a navigable UI without errors.

Lab 4 Wrap-up

By the end of Lab 4 you should have:

Milestone
☐Firewall Check posture rule created and reporting Pass for the Windows desktop
☐Four DEX synthetic tests configured (Traceroute / HTTP × Internet / Ubuntu Server)
☐Three DEX notifications wired to itadmin@acmecorp.com (Fleet connectivity, Application latency, Application availability)
☐warp-diag collected remotely from the dashboard
☐warp-diag collected locally on the Windows desktop
☐warp-diag output parsed in the community log viewer

Lab 4 Troubleshooting

SymptomLikely causeFix
DEX tests in Step 4.2 stay in "No data" foreverTest frequency lower bound varies by entitlementWait 15-20 minutes. Confirm the Cloudflare One Client is Connected and reporting health metrics
Posture Firewall Check (Step 4.1) doesn't appear in device resultsPosture check scoped to the wrong OSOpen the check; confirm OS is Windows and platform is All
warp-diag command not recognized (Step 4.5)Cloudflare One Client install path not on PATH for the admin terminalUse full path "C:\Program Files\Cloudflare\Cloudflare WARP\warp-diag.exe" or open a fresh elevated terminal

Lab 4 to Lab 5

Lab 5 stands up the lab's internal DNS namespace (cf1pslabs.int) so later labs can reach internal hosts by name. It also verifies the device profile's lock-down behaviors before unlocking what subsequent labs need.

Lab 5 - Internal DNS & Profile Lockdown

Internal hostnames like web.cf1pslabs.int don't exist in public DNS, so an enrolled device still can't find AcmeCorp's private destinations. You build a private namespace out of three primitives: an internal DNS zone (cf1pslabs.int) holding web and ssh records for the Ubuntu Server, a view that only enrolled users can see, and a Gateway resolver policy that routes those queries to that view. Then you lock the profile so a user can't disconnect their own client, because every lab after this one assumes it stays on. Estimated time: 25-35 minutes.
Prerequisites:
  • Labs 1 through 4 complete
  • Cloudflare One Client connected on the Windows desktop

5.1 Create an internal DNS zone

Why: Internal DNS (also called private DNS) lets you publish DNS records that resolve only for clients on your overlay network. This is how web.cf1pslabs.int resolves to 10.91.0.91 for a Cloudflare One Client user but stays unresolvable from the public internet.

Internal DNS is an account-level feature (not inside Cloudflare One). The dashboard's own info card on the Internal DNS page says it best: three parts work together: zones hold records, views group zones for delivery to specific users, and resolver policies in Gateway send queries to the right view. Two optional pieces show up later for production deployments: a reference zone on a zone (a fallback the resolver tries when a record isn't found, up to five hops deep), and a Fallback through public DNS toggle on the resolver policy (re-runs the query through 1.1.1.1 if the internal lookup returns REFUSED, NXDOMAIN, or a CNAME). The lab doesn't use either, but you'll see both options in the UI.

Return to the account-level dashboard. Click Back to... at the top of the Zero Trust sidebar.

In the account sidebar, expand Networking and click Internal DNS.

The Internal DNS page has two tabs: Internal zones and Internal DNS views. You start on the Internal zones tab.

Create the zone

Click + Create an internal zone.

A dialog appears: "Name your internal DNS zone." Enter cf1pslabs.int and click Save.

The zone detail page opens showing the zone name, Zone ID, and three tabs: DNS Records, Linked views, Reference Zone.

Add the DNS records

Click + Add a record. A side panel opens with a list of record types, alphabetical from the top. A is the first card. Click Create under A.

The form has two sections: Record Configuration (Name, IPv4 address, TTL) and Record Attributes (Comment, Tags; reference only, doesn't affect resolution). Create both records:

#NameIPv4 addressTTL
1web10.91.0.91 (your Ubuntu Server IP)Auto
2ssh10.91.0.91 (same IP)Auto

For each record: fill in Name and IPv4 address, leave TTL as Auto, and click Save record. Then click + Add a record again for the second one.

Verify: The DNS Records tab shows two A records: web and ssh, both pointing to the Ubuntu Server IP.

5.2 Create a DNS view linked to Cloudflare One Client users

Why: A view is the bridge between a zone and the users who should resolve it. Without a view, the zone exists but no Gateway resolver policy can target it.

Navigate back to the Internal DNS page by clicking Internal DNS in the breadcrumb at the top.

Click the Internal DNS views tab.

Click + Create a view. Enter the name Cloudflare One Client Users and click Save.

The view detail page opens showing the View ID, a Linked zones section (currently empty), and a Resolver policies referencing this view section (currently N/A).

Click Manage linked zones. The "Link zones to Cloudflare One Client Users" page opens showing your available zones.

Check the box next to cf1pslabs.int and click Save.

Verify: Back on the view detail page, the Linked zones section now shows cf1pslabs.int with its Zone ID and "Cloudflare One Client Users" in the Linked views column.

5.3 Create a Gateway resolver policy for the internal DNS zone

Why: The resolver policy tells Cloudflare Gateway which view to consult for queries to cf1pslabs.int. Without it, Gateway falls back to public DNS and the queries fail.

Navigate back to the Zero Trust dashboard: in the account sidebar, click Protect & Connect > Zero Trust.

In the Zero Trust sidebar, expand Traffic controls and click Resolver policies (New).

Click Add a resolver policy. The "Create Resolver policy" page opens with two sections: Scope (left: IF Conditions, right: THEN Resolver) and Details (bottom).

Scope: IF Conditions

Under "And traffic matches...":

Selector: Domain, Operator: is, Value: cf1pslabs.int

Scope: THEN Resolver

Select Use Internal DNS (Beta).

In the view selector that appears, choose Cloudflare One Client Users.

Leave Fallback through public DNS toggled Off. (On would re-run the query through 1.1.1.1 if cf1pslabs.int comes back REFUSED, NXDOMAIN, or a CNAME. We want a clean miss to surface in the lab.)

Details

Policy name: Internal DNS - cf1pslabs.int

Set policy as active: On

Click Create policy.

Verify: The policy appears in the Resolver policies list, enabled and active.

5.4 Test the lock-down behaviors in the device profile

Why: Production deployments often need to prevent users from disconnecting or switching modes. The profile has two locks. Toggling them and then trying the prohibited action proves the lockdown works.

In the Zero Trust dashboard, navigate to Team & Resources > Devices. Click the Device profiles tab. Under General profiles, click Lab-Profile to open it.

Scroll down to Configure settings and make the following change:

SettingSet toEffect
Lock device client switchOnPrevents users from disconnecting the client
Mode switchConfirm OffPrevents users from switching between Traffic and DNS mode and DNS only mode

Click Save profile.

On the Windows desktop, open the Cloudflare One Client. Try two things:

Attempt to click the Disconnect button.

Attempt to change the mode under Settings.

Verify: Both attempts fail or are visibly disabled. The Disconnect button is greyed out or missing. The mode cannot be changed. Document what you observed.

Unlock the client switch for lab use

Now that you have verified the lockdown works, turn Lock device client switch back to Off. Later lab exercises require you to disconnect and reconnect the client for testing. Leave Mode switch as Off for now (Step 16.4 will unlock it temporarily).

Click Save profile.

Verify: On the Windows desktop, the Cloudflare One Client Disconnect button is now clickable again. Do not disconnect yet; just confirm the button is available.

Lab 5 Wrap-up

By the end of Lab 5 you should have:

Milestone
☐Internal DNS zone cf1pslabs.int with web and ssh A records pointing to 10.91.0.91
☐Internal DNS view Cloudflare One Client Users linked to the zone
☐Gateway resolver policy Internal DNS - cf1pslabs.int active
☐Profile lock-down behaviors verified: Lock device client switch blocks disconnect, then unlocked again for later labs

Lab 5 Troubleshooting

SymptomLikely causeFix
nslookup web.cf1pslabs.int returns NXDOMAIN at the end of Lab 5Resolver policy not active or view not linked to the zoneRe-check Step 5.2 and Step 5.3
Disconnect button is still clickable after locking the switchProfile didn't sync to the client yetWait up to 60 seconds for the device to pull profile changes; if still wrong, refresh the client

Lab 5 to Lab 6

Lab 6 builds the Cloudflare Tunnel from the Ubuntu Server to Cloudflare's edge so the internal DNS records you just created can actually resolve to a reachable origin.

Lab 6 - Cloudflare Tunnel & Routing

The private namespace now hands out addresses for destinations nobody can actually reach yet, because AcmeCorp's internal network has no inbound door and Zero Trust says keep it that way. You build the outbound-only path: a Cloudflare Tunnel (Lab-Tunnel) from the Ubuntu Server to Cloudflare's edge, route the lab's private range (10.91.0.0/24) through it, confirm IPv6 is off on the Windows adapter so internal DNS resolves over IPv4, and watch the records from Lab 5 finally resolve to something reachable. You'll also add a tunnel health alert and turn on debug-level cloudflared logging. The origin never opens an inbound hole; the tunnel only opens from the inside. Estimated time: 45-60 minutes.
Prerequisites:
  • Labs 1 through 5 complete
  • Internal DNS zone cf1pslabs.int from Lab 5 (records will resolve once the tunnel is up)
  • SSH access to the Ubuntu Server

6.1 Plan the tunnel

Why: Cloudflare Tunnel creates an outbound-only encrypted connection from your infrastructure to Cloudflare's edge. In this lab you have one Ubuntu Server.
Tunnel namecloudflared runs onRoutes private CIDR
Lab-TunnelUbuntu origin (10.91.0.91)10.91.0.0/24

Confirm you have console access to the Ubuntu Server before proceeding.

6.2 Create the Lab-Tunnel in the dashboard

Why: Tunnel creation in the dashboard generates a tunnel token (a one-time secret) that cloudflared uses to authenticate against Cloudflare. Each cloudflared instance running this tunnel uses the same token.

In the Zero Trust sidebar, expand Networks and click Tunnels & Mesh.

Click Add a tunnel or the Create a tunnel link at the top of the page.

Click Select Cloudflared. The Name your tunnel step opens.

Tunnel name: Lab-Tunnel. Click Save tunnel.

On the connector install screen, leave the page open. The tunnel token is in the install commands shown on this page.

Click the Linux tab. Under Choose your environment, select Debian. Then under Choose your architecture, select 64-bit. The three install commands appear on screen.

You don't need to copy all three commands manually; Step 6.3 below has them ready to paste with one-click Copy buttons. Just grab the tunnel token from the third command (the long string after --token) using the form below.

Verify: The tunnel appears in the Tunnels list with status Inactive (it is created but no cloudflared instance has connected yet).
Fill in here: your tunnel token

The third install command on the connector install screen ends with --token <long-string>. Copy just that long string and paste it below. The debug-logging step (Step 6.9) will substitute it into a configuration file automatically.

6.3 Install cloudflared on the Ubuntu Server

Why: Running cloudflared on the Ubuntu Server with the Lab-Tunnel token establishes the outbound-only tunnel from that machine to the Cloudflare edge. From here on, anything Cloudflare needs to reach inside the lab network travels through this connection.

To find the Ubuntu Server's public IP: in the AWS Console, go to EC2 > Instances, click the instance named lab-ubuntu, and copy the Public IPv4 address from the Details panel.

SSH to that public IP. Sign in as cloudflare / #Savetheinternet.

If prompted, log in with cloudflare / #Savetheinternet.

Open a terminal. (Most lab consoles open one automatically.)

Run these three commands one at a time. Click Copy on each block, paste into the SSH session, press Enter, and wait for each one to finish before moving to the next. Running all three pasted together will break the install; dpkg would run before the .deb finishes downloading.

Download the cloudflared package:

curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb

Install the package:

sudo dpkg -i cloudflared.deb

Register cloudflared as a system service with your tunnel token. The token below auto-fills from the form you completed in Step 6.2:

sudo cloudflared service install <your-token>
Why: Installing cloudflared as a system service (rather than running it in the foreground) registers it with systemd, so the tunnel starts automatically on boot and keeps running after you close the SSH session. This is how a connector runs in production.

Verify the service is running:

sudo systemctl status cloudflared

The output should show Active: active (running).

Before the ICMP ping test in Step 6.5, check the cloudflared binary's raw-socket capability on the Ubuntu Server. The command resolves any symlink first. For example, /usr/local/bin/cloudflared may point to the real binary at /usr/bin/cloudflared:

getcap "$(readlink -f "$(command -v cloudflared)")"

If the command prints nothing, grant cap_net_raw to the real binary and restart the service so the running process picks it up. Applying setcap to the symlink instead returns Invalid file:

sudo setcap cap_net_raw+ep "$(readlink -f "$(command -v cloudflared)")"
sudo systemctl restart cloudflared

Run the capability check again using the copy block below. Expect a line such as /usr/bin/cloudflared cap_net_raw=ep; the path may differ. If getcap or setcap is unavailable, install libcap2-bin first with sudo apt-get install -y libcap2-bin. Recheck after replacing or upgrading the binary because file capabilities can be lost. This host-side check complements the Gateway ICMP setting you verified in Lab 1.

getcap "$(readlink -f "$(command -v cloudflared)")"
Verify: Return to the dashboard tab. The Lab-Tunnel now shows status Active with one connector. Refresh the page if it still says Inactive.

6.4 Add the private network route

Why: Tunnel creation establishes the connection from cloudflared to Cloudflare. The network route tells Cloudflare which destination IP ranges to send through the tunnel.

In the Zero Trust dashboard, expand Networks and click Routes. On the CIDR tab, click Add CIDR route. Configure:

FieldValue
CIDR10.91.0.0/24
TunnelSelect Lab-Tunnel from the dropdown
DescriptionLab private network
Virtual networkExpand Additional settings and select default

Click Create.

Verify: The Routes list shows one entry mapping 10.91.0.0/24 to Lab-Tunnel.

6.5 Test connectivity from the Windows desktop to the Ubuntu Server

Why: Verify that IP-level reachability works before adding complexity.

On the Windows desktop (signed in as cloudflare). Confirm Cloudflare One Client status is Connected.

Open PowerShell (does not need to be Administrator).

Continuous ping to the Ubuntu Server. If it does not work immediately after adding the private route, allow up to 10 minutes for pings to start working before troubleshooting. Press Ctrl+C to stop when you have several successful replies:

ping -t 10.91.0.91
Verify: Pings succeed. If they fail, check:
  • Lab-Tunnel shows Healthy in the dashboard.
  • The route 10.91.0.0/24 -> Lab-Tunnel exists.
  • 10.91.0.0/24 is NOT in the split-tunnel Exclude list.
  • In Traffic controls > Traffic settings > Proxy and inspection settings, "Allow Secure Web Gateway to proxy traffic" has ICMP checked under Select protocols to forward. Without ICMP forwarding, ping can return "Destination host unreachable" from a Cloudflare IP even while HTTP and SSH work.
  • On the Ubuntu Server, getcap "$(readlink -f "$(command -v cloudflared)")" shows cap_net_raw=ep. If it does not, follow the capability and service-restart steps in Step 6.3.
  • On the Ubuntu Server, net.ipv4.ping_group_range includes the group of the user running cloudflared. If the checks above pass but ping still fails, test the lab workaround below.

If ping still fails after those checks, on the Ubuntu Server inspect the current allowed ICMP ping group range:

sysctl net.ipv4.ping_group_range

For this lab, temporarily allow all group IDs to create ICMP ping sockets, then restart cloudflared so the running service picks up the change:

sudo sysctl -w net.ipv4.ping_group_range="0 2147483647" && sudo systemctl restart cloudflared

Retry ping -t 10.91.0.91 from the Windows desktop. Only if it now works, make the Ubuntu Server setting survive a reboot:

printf 'net.ipv4.ping_group_range = 0 2147483647\n' | sudo tee /etc/sysctl.d/99-cloudflared-icmp.conf
Production note: The range above allows all groups to open ping sockets for this lab. On a production connector, use the narrower range that includes the group ID of the user running cloudflared.

6.6 Verify the IPv4-only lab setup on Windows

Why: Terraform disables IPv6 binding on the AWS Windows VM at first boot so this lab's internal DNS tests use IPv4 consistently. Earlier lab testing saw nslookup errors or timeouts with IPv6 enabled. That observation does not establish that the Cloudflare One Client generally fails to handle IPv6 DNS.
Lab versus production:
  • In this lab: Verify the IPv4-only adapter setting below so everyone tests the same path to the Ubuntu Server. Disabling IPv6 here is a lab workaround, not a production requirement.
  • In production: Keep IPv6 enabled where it is part of your network design. If private DNS fails, check the client mode, DNS resolver path, and Split Tunnels before changing adapter bindings.
  • Documented Windows caveats: In DNS only mode, nslookup may fail when it uses the client's IPv4-mapped IPv6 local DNS proxy address; try Resolve-DnsName or specify the IPv4 proxy address 127.0.2.2. In Traffic only mode, IPv6 DNS servers may need Split Tunnel exclusions. Neither caveat establishes a blanket IPv6 failure in the Traffic and DNS mode used here. See Cloudflare One Client known limitations.
  • AWS VM support: This lab uses Windows Server 2022, which Cloudflare lists as unsupported for the Cloudflare One Client. Do not treat this training VM as a supported production endpoint design.

On the Windows desktop, open PowerShell and run:

Get-NetAdapterBinding -ComponentID ms_tcpip6 | Format-Table Name, Enabled

For this lab, every row should read Enabled = False.

Verify: All adapters show IPv6 binding disabled. If any row still reads Enabled = True, run Disable-NetAdapterBinding -Name "<adapter-name>" -ComponentID ms_tcpip6 to fix it, or click through Control Panel > Network and Internet > Network and Sharing Center > Change adapter settings, right-click the adapter, choose Properties, and uncheck Internet Protocol Version 6 (TCP/IPv6). No reboot needed.

6.7 Test FQDN-based connectivity using internal DNS

Why: Step 6.5 verified IP-level connectivity. Now verify that internal DNS hostnames resolve correctly.

On the Windows desktop (signed in as cloudflare). Confirm Cloudflare One Client is Connected.

Open PowerShell.

Resolve the internal DNS records:

nslookup ssh.cf1pslabs.int
nslookup web.cf1pslabs.int

Each should return the Ubuntu Server's private IP (10.91.0.91). Then test reachability:

ping ssh.cf1pslabs.int
ping web.cf1pslabs.int
Verify: Both pings succeed. If nslookup fails, revisit Steps 5.1-5.3.

6.8 Configure a tunnel health alert

Why: When both connectors of a tunnel fail simultaneously (the tunnel goes fully down), you want to know immediately. The tunnel health alert fires an email notification when this happens. Notifications are configured at the account level (outside Cloudflare One).

Return to the account-level dashboard. Click Back to... at the top of the Zero Trust sidebar to leave the Zero Trust scope.

In the account sidebar, expand Manage account and click Alerts. The Alerts page has Alerts and Destinations tabs. Click Add.

Page 1 of 2: pick the alert type

The Create an Alert page lists alert types by product. Choose Tunnel in the Product dropdown. The table filters to two rows: Tunnel Creation or Deletion Event and Tunnel Health Alert.

Click Select on the Tunnel Health Alert row.

Page 2 of 2 (first half): name and recipient

Fill in:

FieldValue
Alert nameLab-Tunnel Health Alert
Description (optional)Leave blank, or add a one-liner like Alerts when Lab-Tunnel goes down
Notification emailClick + Add email recipient and enter an address you can check

Click Next.

Page 2 of 2 (second half): scope and trigger

Under Add tunnel(s), type Lab-Tunnel in the search field and click Search. Tick the checkbox next to the matching row. The footer should read 1 tunnels selected.

Leave Include future tunnels off. (Toggle it on if you want the alert to also fire for any tunnel created in this account later; useful in production, not for the lab.)

Under Health status trigger, select Becomes down. The other four radio options (Becomes either healthy/degraded/down, Becomes either degraded or down, Becomes degraded, Becomes healthy) are for different operational pages; "Becomes down" matches the production scenario this lab simulates.

Click Create.

Verify: The alert appears in the Alerts list with name Lab-Tunnel Health Alert.

When done, navigate back to Cloudflare One via Protect & Connect > Zero Trust in the account sidebar.

6.9 Enable debug logging on cloudflared

Why: Default cloudflared logging is at the info level, which captures normal operation but not the protocol-level detail you sometimes need when debugging connection issues. Setting log level to debug writes every TLS handshake, every QUIC frame, and every routing decision to the cloudflared log.

SSH to the Ubuntu Server (where Lab-Tunnel runs). Sign in as cloudflare / #Savetheinternet.

Open a terminal as the cloudflare user.

The lab pod installs cloudflared as a systemd service. To enable debug logging, you need to edit the service unit file directly (not a config.yml). Run:

sudo systemctl edit cloudflared --full

This opens the full service unit file in nano. Make two changes:

Find the Type= line under [Service]. Change it to:

Type=simple
Why: The service install in Step 6.3 created the unit with Type=notify, which expects cloudflared to signal systemd when it is ready. Running it in the foreground with an explicit tunnel run line does not send that readiness signal, so systemd would time out and mark the service failed. Type=simple tells systemd to treat the process as running as soon as it starts.

Find the ExecStart= line. Add --loglevel debug between --no-autoupdate and tunnel run. The line should look like:

ExecStart=/usr/bin/cloudflared --no-autoupdate --loglevel debug tunnel run --token <your-token>

Save the file (Ctrl+O, Enter, Ctrl+X).

Reload the systemd daemon and restart cloudflared:

sudo systemctl daemon-reload
sudo systemctl restart cloudflared

Verify the new log level is active by following the logs in real time:

sudo journalctl -u cloudflared -f

You should see verbose DEBUG-level log entries. Press Ctrl+C to stop following.

Verify: The cloudflared logs contain DEBUG entries in addition to INFO. The tunnel in the dashboard still shows Healthy.

Lab 6 Wrap-up

By the end of Lab 6 you should have:

Milestone
☐Lab-Tunnel created and active (one connector on the Ubuntu Server)
☐Private network route 10.91.0.0/24 → Lab-Tunnel
☐Ubuntu Server reachable by IP from the Windows desktop
☐IPv4-only adapter setting verified for this lab (Terraform pre-disabled IPv6; checked in Step 6.6)
☐FQDN resolution working for cf1pslabs.int
☐Tunnel health alert configured for Lab-Tunnel
☐Debug-level cloudflared logging on Lab-Tunnel

Lab 6 Troubleshooting

SymptomLikely causeFix
sudo cloudflared service install ... returns "invalid token"Token expired (lab tokens can expire faster than 30 days)Return to Networks > Connectors, click the tunnel, click Configure, get a fresh token
ping 10.91.0.91 fails after Lab-Tunnel is HealthyRoute is wrong, split tunnel excludes the destination, Gateway is not forwarding ICMP, or the cloudflared host lacks raw-socket capability or an allowed ping groupCheck the route and split-tunnel Exclude list; confirm ICMP is checked in Traffic controls > Traffic settings > Proxy and inspection settings; on the Ubuntu Server, check getcap "$(readlink -f "$(command -v cloudflared)")" and apply the capability fix in Step 6.3 if missing. If ping still fails, test ping_group_range in Step 6.5.
nslookup web.cf1pslabs.int returns NXDOMAINInternal DNS zone, view, or resolver policy from Lab 5 misconfiguredRe-check Lab 5 Steps 5.1-5.3
Debug logging from Step 6.9 not visibleService file not reloadedRun sudo systemctl daemon-reload && sudo systemctl restart cloudflared

Lab 6 to Lab 7

Lab 7 sends the Ubuntu Server's DNS queries through Gateway. The server doesn't run the Cloudflare One Client, but a DNS location plus dnsproxy makes its queries reach Gateway. You will add DNS blocking policies in Lab 14.

Lab 7 - DNS Location for Server-Side Filtering

Not everything at AcmeCorp can run the client. Servers, printers, and IoT gear have no user to sign in, but you still want their DNS queries to reach Gateway. You give the Ubuntu Server its own DNS over HTTPS endpoint (a Gateway DNS location named Ubuntu Server), point a dnsproxy instance on the server at it, then confirm its queries appear in the DNS query log. DNS blocking policies come later in Lab 14. Estimated time: 20-30 minutes.
Prerequisites:
  • Lab 6 complete (Lab-Tunnel routing private traffic)
  • SSH access to the Ubuntu Server

Create a DNS location for the Ubuntu Server

Why: DNS locations let you apply Gateway DNS policies to networks that don't run the Cloudflare One Client. By pointing the Ubuntu Server's DNS resolver at a Gateway DNS location endpoint, you can filter DNS queries from the server itself.

a. Create the DNS location in the dashboard

In the Zero Trust sidebar, expand Traffic controls and click Resolvers & Proxies.

Click the DNS locations tab.

Click Add a location. On the Add DNS endpoints step, configure:

FieldValue
Location nameUbuntu Server
DNS over HTTPS (DoH)On

Click Continue. On Protect your endpoints, leave DoH endpoint filtering & authentication Off and click Continue again. Cloudflare briefly shows Review setup details, then Confirmation. This creates the location; there is no separate Save button. If this is your first DNS location, it becomes the default automatically.

On the Confirmation screen, note the DNS over HTTPS (DoH) endpoint URL (looks like https://<unique-id>.cloudflare-gateway.com/dns-query). The <unique-id> piece is what you'll fill in below.

Fill in here: your DoH endpoint ID

From the URL Cloudflare just showed (https://<unique-id>.cloudflare-gateway.com/dns-query), paste only the leading slug below.

b. Point the Ubuntu Server at the DoH endpoint

Note: cloudflared proxy-dns was removed in cloudflared 2026.2.0. Use dnsproxy (a lightweight Go DNS forwarder) instead.

SSH into the Ubuntu Server (cloudflare / #Savetheinternet).

Download and install dnsproxy:

curl -sL https://github.com/AdguardTeam/dnsproxy/releases/download/v0.81.4/dnsproxy-linux-amd64-v0.81.4.tar.gz | sudo tar xzf - -C /usr/local/bin/ ./linux-amd64/dnsproxy --strip-components=2 && sudo chmod +x /usr/local/bin/dnsproxy

Start dnsproxy on port 5053, forwarding to your Gateway DoH endpoint:

sudo /usr/local/bin/dnsproxy -l 127.0.0.1 -p 5053 -u https://<unique-id>.cloudflare-gateway.com/dns-query &

(Replace the URL with your DoH endpoint from the dashboard. Port 5053 avoids conflicting with the Ubuntu Server's local DNS resolver on port 53.)

c. Test a DNS query through the proxy

dig @127.0.0.1 -p 5053 malware.testcategory.com

You have not created Gateway DNS blocking policies yet. Those come in Step 14.1. This query may resolve normally, and that is expected; a block is not required in Lab 7. After you create and enable a DNS policy that blocks the Malware security category, you can repeat this query to test the block.

Verify: Check Insights & Logs > Logs > DNS query logs. Click + Add filter, select DNS location, and filter by Ubuntu Server. Find the server's query for malware.testcategory.com. Its presence shows that the DNS location sends queries to Gateway without the Cloudflare One Client. A blocked response is not required until you add a matching DNS policy in Lab 14.

Lab 7 Wrap-up

By the end of Lab 7 you should have:

Milestone
☐Gateway DNS location Ubuntu Server created with its DoH endpoint
☐dnsproxy on the Ubuntu Server forwarding DNS to the DoH endpoint on 127.0.0.1:5053
☐DNS query log entry visible at Insights & Logs > Logs > DNS query logs with location filter Ubuntu Server

Lab 7 Troubleshooting

SymptomLikely causeFix
dnsproxy command not found after installtar extraction landed in a different directoryConfirm /usr/local/bin/dnsproxy exists and is executable (chmod +x)
Port 5053 already in useLab leftover process from a prior runFind and kill the stale dnsproxy (sudo lsof -i :5053) then restart
DNS query log doesn't show the Ubuntu Server locationWrong filter; queries hit the system resolver instead of dnsproxyVerify the test query uses dig @127.0.0.1 -p 5053

Lab 7 to Lab 8

Lab 8 publishes self-hosted Access apps for the Ubuntu Server's web and SSH services. The Lab-Tunnel you built in Lab 6 is the routing fabric; the Allow Lab Users policy you build in Step 8.1 is the access rule.

Lab 8 - Self-Hosted Web & SSH Apps

The tunnel can carry traffic, but there's still nothing deciding who's allowed down it. You put the first access decisions in place: a reusable Allow Lab Users policy (usera and itadmin), plus a Ubuntu Web app and a Ubuntu SSH app both published through Lab-Tunnel with SAML in front of each. Browser-rendered SSH means a user doesn't even need an SSH client, just a browser. You test all three identities end to end, including userb, who gets turned away because she's not in the policy. Estimated time: 45-60 minutes.
Prerequisites:
  • Labs 1 through 7 complete
  • Lab-Tunnel active
  • Your domain Active in Cloudflare (Step 1.7)

8.1 Create a baseline Access policy for lab users

Why: All the Access apps in this lab share the same policy. Creating a reusable policy now lets you attach it to each app without re-defining the logic. The policy must include both usera@acmecorp.com (the primary test user) and itadmin@acmecorp.com (the account you enrolled the Cloudflare One Client with in Step 3.3). Without itadmin, WARP-enrolled traffic to private-network Access apps will be denied.

In the Zero Trust sidebar, click Access controls.

Click Access policies (or Policies depending on dashboard build).

Click + Add a policy.

Configure:

FieldValue
Policy nameAllow Lab Users
ActionAllow
Session duration24 hours (default)
Include rule: SelectorEmails
Include rule: Valueadd each address below as its own entry

Include rule — Value. Add each address as its own entry: click Copy, paste into the Value field, press Enter, then add the next.

usera@acmecorp.com
itadmin@acmecorp.com

Save the policy.

Verify: The policy appears in the Access policies list with the name Allow Lab Users, action Allow, and includes both email addresses.

8.2 Configure an Access HTTPS app for the Ubuntu Server web service

Why: This is the most common Access pattern: take an internal HTTP application and publish it to the internet behind Cloudflare authentication. Users get a public hostname they can reach from anywhere; Cloudflare authenticates them before passing traffic to the origin.

Navigate to Access controls > Applications > + Add an application > Self-hosted > Select.

Configure the application using the values below:

FieldValue
Application nameUbuntu Web
Session duration24 hours
Application domainSubdomain: web, Domain: your domain. Full FQDN: web.<your-domain>
Identity providersSAML only (uncheck all others)

Click Next. On the Policies page, click Select existing policies, check Allow Lab Users (from Step 8.1), and click Confirm.

Click Next. On Additional settings, leave defaults. Click Next.

On the Connect a server step (some dashboard builds show this; others skip directly to save), leave as-is; the tunnel side gets configured in Step 8.3.

Click Add application or Save.

Verify: Ubuntu Web appears in the Applications list with the FQDN web.<your-domain> and policy Allow Lab Users attached.

8.3 Publish the Ubuntu Server web service through Lab-Tunnel

Why: Creating the Access app published the hostname but did not connect it to the origin. The tunnel side needs a published application route that maps web.<your-domain> to http://10.91.0.91:80 (or whichever port the Ubuntu Server's web service is on).

First, delete the existing web A record. In the Cloudflare dashboard for your domain, click DNS > Records, find the record you created in Step 1.7, and delete it. Cloudflare cannot publish a tunneled CNAME for a hostname that already has an A record at the same name. Once you save the published route below, the only path to web.<your-domain> is through the tunnel.

In the Zero Trust dashboard, expand Networks and click Tunnels & Mesh.

Click Lab-Tunnel to open its detail page.

Click the Published application routes tab.

Click + Add a published application route. Configure:

FieldValue
Subdomainweb
DomainSelect <your-domain> from the dropdown
Path(leave blank)
Service typeHTTP
URL10.91.0.91:80 (check with curl http://10.91.0.91 from the Ubuntu Server console to find the listening port)

Click Save hostname.

Verify: The published route appears in Lab-Tunnel's route list mapping web.<your-domain> to http://10.91.0.91:80

8.4 Test the HTTPS Access app

Why: Confirm that the entire end-to-end path works: browser to web.<your-domain> → Cloudflare → Access prompts for SAML → SAML authenticates → Cloudflare proxies to 10.91.0.91:80 through the Lab-Tunnel.

On the Windows desktop (or any browser, since this is testing the non-WARP user experience), open an InPrivate or incognito browser window.

Navigate to https://web.<your-domain>

Cloudflare's Access challenge page loads. Click your SAML provider.

The shared lab IdP sign-in page loads. Authenticate with:

Username: usera@acmecorp.com

Password: #Savetheinternet

After successful auth, the browser is redirected back to web.<your-domain> and the Ubuntu Server's web page renders.

Verify: You see the AcmeCorp website (or whichever web content the Ubuntu Server is serving). The browser address bar shows your domain with a green/secure lock icon. Repeat the test with itadmin@acmecorp.com: open another InPrivate window, navigate to the same URL, authenticate as itadmin. It should also succeed (itadmin is in the Allow Lab Users policy). Then try userb@acmecorp.com; this user should be blocked with a Cloudflare Access denial page.

8.5 Configure an Access browser-rendered SSH app

Why: Browser-rendered SSH lets users SSH into a server without needing an SSH client installed. Cloudflare renders the SSH session in a Browser Isolation tab. The user authenticates once via SAML; the SSH session opens immediately after.
Same wizard as Step 8.2: Access controls > Applications > + Add an application > Self-hosted.
FieldValue
Application nameUbuntu SSH
Session duration24 hours
Application domainSubdomain: ssh. Full FQDN: ssh.<your-domain>
Browser renderingSSH (switches the app from a normal HTTPS proxy to a browser-rendered terminal session)
Identity providersSAML only
PolicyAllow Lab Users (from Step 8.1)

Click Next through each wizard page, then Add application.

Verify: Ubuntu SSH appears in the Applications list with FQDN ssh.<your-domain> and browser rendering set to SSH.

8.6 Publish SSH through Lab-Tunnel

Why: Browser-rendered SSH lets users SSH into a server without an SSH client. This step publishes the SSH service through the same Lab-Tunnel that serves the web app.

In the Zero Trust dashboard, click Networks > Connectors and open Lab-Tunnel.

Click the Published application routes tab.

Click + Add a published application route. Configure:

FieldValue
Subdomainssh
DomainYour domain
Path(leave blank)
Service typeSSH
URL10.91.0.91:22

Save the hostname.

Verify: Lab-Tunnel now lists ssh.<your-domain> in its published routes mapped to ssh://10.91.0.91:22.

8.7 Test browser-rendered SSH

Why: Confirm the full flow: browser to ssh.<your-domain> → SAML auth → browser-rendered terminal opens → local credentials authenticate against the Ubuntu Server's SSH daemon.

On the Windows desktop, open an InPrivate browser window.

Navigate to https://ssh.<your-domain>

Authenticate via SAML as usera@acmecorp.com / #Savetheinternet.

After SAML auth, a browser-rendered terminal page loads. The terminal prompts for the SSH username.

Enter SSH credentials:

FieldValue
Usernamecloudflare
Password#Savetheinternet

The remote shell prompt appears (cloudflare@ubuntu-server:~$ or similar).

Run a few commands to confirm the session is live: whoami, hostname, cat /etc/os-release.

Verify: Commands execute and return output in the browser-rendered terminal. The session is fully interactive (you can type more commands).

8.8 Confirm the Access decisions in the logs

Why: Testing from the browser shows you the user's experience, but the authoritative record of who was allowed and who was turned away lives in the Access authentication logs. Access decisions are recorded in the Decision column (Allowed / Blocked), which is separate from the Gateway Action column you used in the DNS, Network, and HTTP labs. Reading the log now builds the habit of confirming every policy decision against the record instead of trusting the browser alone.

In the Zero Trust dashboard, click Insights & Logs > Logs > Access authentication logs.

Click + Add filter, set Field Application, Operator equals, and pick Ubuntu Web from the Value list. (The Operator only offers equals / does not equal, so there is no substring match.)

Read the Decision column and the User email column for the login attempts you made in Step 8.4.

Verify: The Access authentication logs show Decision = Allowed for usera@acmecorp.com and itadmin@acmecorp.com, and Decision = Blocked for userb@acmecorp.com, all against the Ubuntu Web application.

Lab 8 Wrap-up

By the end of Lab 8 you should have:

Milestone
☐Reusable Allow Lab Users Access policy (usera + itadmin)
☐Ubuntu Web self-hosted Access app published via Lab-Tunnel (HTTPS on port 80)
☐Ubuntu SSH self-hosted Access app published via Lab-Tunnel (browser-rendered SSH on port 22)

Lab 8 Troubleshooting

SymptomLikely causeFix
Access HTTPS app returns 502 Bad GatewayPublic hostname on the tunnel points to wrong port or origin is downCheck the Ubuntu Server is serving HTTP on the port you specified (Step 8.3 URL field). curl http://10.91.0.91 from the Ubuntu Server console
Browser-rendered SSH terminal stuck on "Connecting..."SSH port (22) on the Ubuntu Server is not reachable through the tunnelConfirm Lab-Tunnel has 10.91.0.0/24 as a route (Lab 6 Step 6.4); confirm the SSH published route uses ssh:// scheme

Lab 8 to Lab 9

Lab 9 adds browser-rendered RDP to the mix, then walks through multi-user enrollment and pre-login: the production patterns for shared Windows devices and lock-screen connectivity.

Lab 9 - Browser-Rendered RDP, Multi-User & Pre-Login

Real access isn't one user at a time on personal hardware, so this lab handles the messy reality of shared Windows devices. RDP rendered in a browser means nobody installs a client. Multi-user enrollment lets a shared Windows device attribute each request to whoever's logged in, so the helpdesk can tell whose session did what. And pre-login lets the machine reach domain controllers at the lock screen, before any human signs in. You wire up all three: an RDP Workstation app published via a new rdp-tunnel on the Windows client, the local UserA and UserB accounts the multi_user setting attributes traffic to, and a pre-login service token (Client ID and Client Secret) that drops into mdm.xml, with the enrollment policy updated to accept Service Auth on top of SAML. Estimated time: 60-75 minutes.
Prerequisites:
  • Labs 1 through 8 complete
  • Cloudflare One Client connected on the Windows desktop
  • Windows admin terminal access for installing cloudflared as a service

9.1 Set up Browser-Rendered RDP via the Get Started wizard

Why: Browser-rendered RDP lets users connect to a Windows desktop via their browser without an RDP client. The Get Started wizard is the only working dashboard path for browser-rendered RDP; the manual "Add an application" flow creates the wrong app type. This step installs cloudflared on the Windows client to create an rdp-tunnel, then publishes the Windows desktop as an RDP app.

3.8a. Confirm the Windows desktop's LAN IP

The lab pins the Windows desktop's private IP to 10.90.0.90. To sanity-check, open PowerShell on the Windows desktop and run:

ipconfig

The IPv4 Address of the active Ethernet adapter reads 10.90.0.90. You'll plug that value into the wizard in the next sub-step.

3.8b. Open the wizard

In the Zero Trust sidebar, click Overview.

Click the Get started tab at the top.

On the Set up secure access to private apps from any browser card, click Get started (or View details if the wizard was previously started).

Click Continue next to Enable in-browser remote desktop sessions to Windows hosts.

3.8c. Define your application

FieldValue
Name your applicationRDP Workstation
Local IP address of your RDP target10.90.0.90
RDP port3389

Click Continue.

3.8d. Select a public domain

FieldValue
Select domain<your-domain>
Enter a subdomainrdp

Click Continue.

3.8e. Add your first Access policy

The wizard creates a one-time-pin (OTP) policy by default. Instead of using OTP, attach the existing Allow Lab Users policy from Step 8.1. If the wizard does not allow selecting an existing policy, type usera@acmecorp.com in the approved emails field and also add userb@acmecorp.com (both users are needed for the multi-user test in Step 9.4).

Click Continue.

3.8f. Assign a Tunnel

Click the Choose or create a Tunnel dropdown and type rdp-tunnel.

Click the Create "rdp-tunnel" option that appears.

Click Continue.

3.8g. Deploy the Tunnel on the Windows client

Select Windows and 64-bit.

On the Windows desktop, click the download the cloudflared service link. Depending on the download offered, you may get cloudflared-windows-amd64.msi or cloudflared-windows-amd64.exe. Follow the matching option:

In the dashboard tab, click the copy icon on the Your Tunnel command block.

Back in the elevated PowerShell window on the Windows desktop, paste the command you just copied from the dashboard's Your Tunnel command block (the cloudflared.exe service install ... line that contains the tunnel token) and press Enter. For the .exe option, use the same PowerShell window where you ran the setup command above.

Switch back to the dashboard. Wait for rdp-tunnel has been deployed (green checkmark).

Click Continue, then close the success page.

Verify:
WhereWhat you should see
Access controls > ApplicationsRDP Workstation, type RDP, URL rdp.<your-domain>
Networks > Connectorsrdp-tunnel, Status HEALTHY
Networks > Routes > CIDR10.90.0.90/32 on rdp-tunnel

9.2 Test the browser-rendered RDP session

Why: Confirm the full flow: browser → SAML auth → RDP session renders inside the browser → Windows desktop is usable.

Open an InPrivate browser window.

Open your https://<team-name>.cloudflareaccess.com App Launcher. If it is not enabled yet, use the enable and policy instructions in Step 12.1 now, allowing usera@acmecorp.com and itadmin@acmecorp.com, then return here. Select the RDP Workstation tile for the Windows desktop target at 10.90.0.90, not the bare rdp.<your-domain> application address.

The target tile opens a URL that includes /rdp/<vnet-id>/<target-ip>/3389. If you see Unable to find your RDP target!, go back to the App Launcher and click the Windows target tile. The bare application address does not specify which Windows machine to connect to.

Authenticate via SAML as usera@acmecorp.com / #Savetheinternet.

At the Windows credential prompt, enter: cloudflare / #Savetheinternet.

The Windows desktop renders inside the browser.

Verify: You can see the Windows desktop, move the mouse, and open applications inside the browser-rendered RDP session.

9.3 Verify local Windows users UserA and UserB

Why: Real Windows devices have multiple user profiles. The multi_user setting in mdm.xml (Step 3.6) allows each Windows user account to enroll independently against the same Cloudflare One Client install, so you need at least two non-cloudflare local Windows users to exercise that flow.
Pre-staged on the VM: UserA and UserB already exist on the Windows VM (both in the Administrators and Remote Desktop Users groups, password #Savetheinternet). This step just confirms they're present before the multi-user-enrollment exercise in Step 9.4.

In a PowerShell window inside the Windows VM (RDP session from Step 9.2), run:

Get-LocalUser UserA, UserB | Format-Table Name, Enabled, Description -AutoSize
Get-LocalGroupMember Administrators | Where-Object Name -match 'UserA|UserB'

The first command lists both accounts and confirms they're enabled. The second confirms both are members of the local Administrators group.

If you'd rather check via Computer Management (GUI)

Right-click the Start menu → Computer Management → expand System Tools > Local Users and Groups > Users. UserA and UserB should both appear in the list. Open the Administrators group (under Groups) to confirm both are members.

Verify: Both UserA and UserB are present and enabled, and both appear in the Administrators group. If either is missing or disabled, open Computer Management > Local Users and Groups > Users, create the missing user with password #Savetheinternet, then right-click the user, choose Properties > Member Of > Add, and add the Administrators group.

9.4 Enroll the Cloudflare One Client as UserA and UserB

Why: With multi_user enabled in mdm.xml (Step 3.6), each Windows user who logs in gets their own Cloudflare One Client enrollment. Traffic from each user is attributed to that user in the dashboard logs. This is how production deployments track per-user activity on shared Windows devices. Your BYO-AWS environment supports the full multi-user enrollment flow, so you'll exercise it for real.

Inside the browser-rendered RDP session, sign out of the current cloudflare Windows session.

Sign in as UserA with password #Savetheinternet. Confirm the desktop loads.

The Cloudflare One Client prompts for enrollment. Click Log in with your team name, enter your team name when asked, then complete SAML as usera@acmecorp.com / #Savetheinternet. The client transitions to Connected under usera@acmecorp.com.

Sign out of UserA. Sign in as UserB. Confirm the desktop loads.

The Cloudflare One Client prompts for enrollment again. Complete SAML as userb@acmecorp.com / #Savetheinternet. The client transitions to Connected under userb@acmecorp.com.

Sign out of UserB. Sign back in as cloudflare (the original lab account). The client is still Connected under itadmin@acmecorp.com (its original enrollment).

Verify: In the Zero Trust dashboard at Team & Resources > Devices > Your devices, the same Windows device now appears with multiple enrolled users: itadmin@acmecorp.com, usera@acmecorp.com, and userb@acmecorp.com. Each row is the same machine, listed once per enrolled identity.

9.5 Verify per-user log attribution

Why: Now that each user is enrolled, Gateway tags every request with the identity that generated it. This step confirms that attribution actually appears in the logs: the canonical helpdesk move when someone asks "who generated this request?"

Inside the browser-rendered RDP session, sign in to Windows as UserA. Confirm the Cloudflare One Client shows Connected under usera@acmecorp.com.

Open a browser inside the UserA Windows session and navigate to https://www.bbc.com

Sign out UserA. Sign in as UserB. Confirm the Cloudflare One Client shows Connected under userb@acmecorp.com.

Open a browser and navigate to https://www.cnn.com

Sign out UserB. Sign back in as cloudflare.

In the Zero Trust dashboard, navigate to Insights & Logs > Logs > HTTP request logs.

Click + Add filter, set Host to www.bbc.com, and apply. The Email column for the matching entries shows usera@acmecorp.com.

Change the filter to Host = www.cnn.com. The Email column shows userb@acmecorp.com.

Verify: Per-user attribution is real in the logs: the same machine generated requests under two different identities, and the Email column proves it. The same attribution appears in DNS query logs and Network logs whenever multi-user mode is on.

9.6 Create a service token for pre-login

Why: Pre-login authentication happens before a Windows user logs in. A service token is a machine identity (client ID + secret) that authenticates the device so Cloudflare One Client can establish a connected session before any human user signs in.

In the Zero Trust sidebar, click Access controls.

Click Service credentials.

Confirm you are on the Service Tokens tab.

Click Add a service token.

FieldValue
Service token nameCloudflare One Client Pre-Login
Service Token DurationNon-expiring (or the longest available)

Click Generate token.

Cloudflare displays the Client ID and Client Secret exactly once. Copy both to a safe place immediately. You will not see the secret again.

Verify: The service token appears in the Service Tokens list. Both Client ID and Client Secret are captured outside the dashboard.
Fill in here: service token credentials

Cloudflare just showed the Client ID and Client Secret on the previous screen exactly once. Paste both below now; the mdm.xml example in Step 3.6 will substitute these values into the copy block in Step 9.8 automatically.

9.7 Add enrollment policy for the service token

Why: The default enrollment policy only allows SAML logins (Step 2.6). For pre-login to work, enrollment must also accept the service token.

Navigate to Team & Resources > Devices.

Click the Management tab.

Click the Device enrollment filter button, then click Manage.

Click Add a rule.

FieldValue
NamePre-Login Service Auth
ActionService Auth
Rule typeInclude
SelectorService Token
ValueCloudflare One Client Pre-Login (pick the service token you created in Step 9.6 from the dropdown)

Save the rule.

Verify: The enrollment permissions list now has two rules: the SAML rule from Step 2.6 and the new Service Auth rule.

9.8 Update mdm.xml with service token credentials

Why: The placeholder values in mdm.xml (Step 3.6) need real values. The Cloudflare One Client reads the pre-login section at Windows boot and uses the service token to authenticate the device.

On the Windows desktop (signed in as cloudflare), open Notepad as administrator.

Open C:\ProgramData\Cloudflare\mdm.xml.

Locate the <key>auth_client_id</key> line. Replace the placeholder string with your service token's Client ID.

Locate the <key>auth_client_secret</key> line. Replace the placeholder string with your service token's Client Secret.

Save the file. Close Notepad.

Verify: Re-open the file. Both auth_client_id and auth_client_secret contain real values (not the placeholder text).

9.9 Verify the pre-login flow

Why: With the service token credentials now in mdm.xml and the enrollment policy accepting Service Auth, the Cloudflare One Client can authenticate the device at Windows boot before any human user logs in. In production, this is what lets the machine reach domain controllers, file shares, and other resources over Zero Trust at the lock screen: the things Group Policy and remote login depend on. You'll verify it here by rebooting the Windows VM and watching the pre-login machine identity show up in the dashboard.

Inside the browser-rendered RDP session, open PowerShell as Administrator and reboot the Windows VM:

Restart-Computer -Force

The browser-rendered RDP session disconnects. Wait 60-90 seconds for the EC2 instance to boot back up. During this window, the Cloudflare One Client service starts at the lock screen, reads the service token from mdm.xml, and authenticates against Cloudflare as a machine identity.

In the Zero Trust dashboard, navigate to Team & Resources > Devices > Your devices. Look for an entry under the user non_identity@<team-name>.cloudflareaccess.com; that's the pre-login enrollment, posted by the device while the lock screen was up and no human user was signed in.

Reconnect to the Windows VM from the https://<team-name>.cloudflareaccess.com App Launcher. Select the Windows desktop target tile as in Step 9.2, authenticate via SAML as itadmin@acmecorp.com, then sign in to Windows as cloudflare / #Savetheinternet. If the tile is missing for itadmin, check that the App Launcher policy allows that user.

The Cloudflare One Client transitions to Connected under itadmin@acmecorp.com (the human user that just signed in). Refresh the Devices > Your devices list in the dashboard.

Verify: The same Windows device now appears under two identities in Team & Resources > Devices > Your devices: non_identity@<team-name>.cloudflareaccess.com for the pre-login machine identity, and the human-user identity (itadmin@acmecorp.com) that the client re-enrolled under once you signed in. The pre-login enrollment is the production hook for machine-level Zero Trust at the lock screen.

Lab 9 Wrap-up

By the end of Lab 9 you should have:

Milestone
☐RDP Workstation Access app published via the new rdp-tunnel on the Windows client
☐Local Windows users UserA and UserB verified present and able to log in
☐Multi-user enrollment concept understood (per-user log attribution at Insights & Logs > Logs > HTTP request logs)
☐Service token Cloudflare One Client Pre-Login created
☐Enrollment policy accepting the pre-login service token (Service Auth)
☐mdm.xml populated with the real service-token Client ID and Client Secret
☐Pre-login flow understood (non_identity@<team-name>.cloudflareaccess.com entry appears at the lock screen)

Lab 9 Troubleshooting

SymptomLikely causeFix
RDP wizard Continue stays disabled on Step 9.1cloudflared has not connected back to CloudflareOn the Windows client, run Get-Service cloudflared; should be Running. Re-run the install command if missing
UserA or UserB missing (Step 9.3)The user wasn't created on the VMOpen Computer Management > Local Users and Groups > Users; create the missing user with password #Savetheinternet; right-click the user, choose Properties > Member Of > Add, and add the Administrators group
Pre-login section in mdm.xml reports invalid credentialsWrong Client ID / Client Secret pasted, or extra whitespaceRegenerate the service token in Step 9.6 and paste exactly; do not include quotes

Lab 9 to Lab 10

Lab 10 publishes the intranet service as a second Access app through the same Lab-Tunnel, demonstrating the one-tunnel / many-services production pattern.

Lab 10 - Intranet App Through Lab-Tunnel

Companies never have just one internal app: there's a website, an intranet, an HR portal, a wiki. The production pattern is one tunnel serving many. You add the intranet as a second service on the same Lab-Tunnel from Lab 6, pointing the intranet hostname at port 8000, gate it behind the same Allow Lab Users policy, and confirm it renders as a visibly different site from the public website, because nginx is serving two separate sites off one server. Estimated time: 25-35 minutes.
Prerequisites:
  • Labs 1 through 9 complete
  • Lab-Tunnel still publishing the Ubuntu Web and Ubuntu SSH apps from Lab 8

10.1 Publish the intranet service through Lab-Tunnel

Why: The Ubuntu Server's nginx serves two sites on different ports: the AcmeCorp public website on port 80 (which you just published as Ubuntu Web) and the intranet on port 8000. Publishing the intranet as a second service through the same tunnel demonstrates a common production pattern: one tunnel, multiple services on different ports.

In the Zero Trust dashboard, click Networks > Connectors and open Lab-Tunnel.

Click the Published application routes tab.

Click + Add a published application route. Configure:

FieldValue
Subdomainintranet
DomainSelect <your-domain> from the dropdown
Path(leave blank)
Service typeHTTP
URL10.91.0.91:8000 (the Ubuntu Server's nginx serves the intranet on port 8000)

Click Save hostname.

Verify: Lab-Tunnel now lists two published routes: web.<your-domain> on port 80 and intranet.<your-domain> on port 8000.

10.2 Create an Access app for the intranet

Why: The intranet is a separate application from the public website. Giving it its own Access app means you can apply different policies to it (e.g., restrict to internal employees only while the public website is open to partners).
Same wizard as Steps 8.2 and 8.5: Access controls > Applications > + Add an application > Self-hosted.
FieldValue
Application nameIntranet
Session duration24 hours
Application domainSubdomain: intranet. Full FQDN: intranet.<your-domain>
Identity providersSAML only
PolicyAllow Lab Users (from Step 8.1)

Click Next through each wizard page, then Add application.

Verify: Intranet appears in the Applications list with FQDN intranet.<your-domain>.

10.3 Test the intranet app

Why: Confirm the full end-to-end flow for the second service: browser → Access auth → tunnel → nginx on port 8000. The intranet page should look different from the public website, confirming you are hitting the right nginx server block.

On the Windows desktop, open an InPrivate browser window.

Navigate to https://intranet.<your-domain>

Cloudflare's Access challenge page loads. Click your SAML provider.

Authenticate as usera@acmecorp.com / #Savetheinternet.

After auth, the intranet page renders. It should look different from the AcmeCorp website you saw in Step 8.4 (nginx is serving different content on port 8000).

Verify: The intranet page loads successfully. The content is distinct from web.<your-domain>. Both Access apps (Ubuntu Web and Intranet) are now accessible through the same Lab-Tunnel but serve different content on different ports.

10.4 Confirm the Intranet access decision in the logs

Why: Same habit as Step 8.8: every Access app you publish should be confirmed against the Access authentication logs, not just the browser. This proves the Intranet app is authenticating through the same Allow Lab Users policy and recording an Allowed decision for the user who reached it.

In the Zero Trust dashboard, click Insights & Logs > Logs > Access authentication logs.

Click + Add filter, set Field Application, Operator equals, and pick Intranet from the Value list.

Read the Decision and User email columns for the login you made in Step 10.3.

Verify: The Access authentication logs show Decision = Allowed for usera@acmecorp.com against the Intranet application.

Lab 10 Wrap-up

By the end of Lab 10 you should have:

Milestone
☐Lab-Tunnel publishing a third application route: intranet.<your-domain> → 10.91.0.91:8000
☐Intranet self-hosted Access app gated by Allow Lab Users
☐Intranet content visible after SAML auth and distinct from web.<your-domain>

Lab 10 Troubleshooting

SymptomLikely causeFix
Intranet page in Step 10.3 returns 502Wrong port; intranet container maps host 8000 -> container 80Confirm the tunnel published route uses 10.91.0.91:8000, not :80
Intranet page renders the same content as web.<your-domain>Wrong port; you accidentally targeted port 80Re-open the tunnel's published application routes in Networks > Connectors > Lab-Tunnel and correct the port

Lab 10 to Lab 11

Lab 11 closes the loop on the tunnel-only access pattern: it locks down the Ubuntu Server's public IP at the AWS Security Group so the cloudflared tunnel becomes the single inbound path.

Lab 11 - Public-IP Lockdown: Proving the Tunnel Is the Only Path

AcmeCorp's internal content still has two ways in: through the tunnel, and directly across the public internet via the server's public IP. That second path is exactly the inbound exposure Zero Trust exists to kill, and the whole point of the tunnel is that you don't need it. In this short lab you close it: delete the inbound port 80 and 8000 rules at the AWS Security Group, watch a direct curl to the public IP time out, and watch the tunnel hostnames keep working anyway. Estimated time: 15-25 minutes.
Prerequisites:
  • Lab 10 complete (web and intranet apps both reachable through the tunnel)
  • AWS Console access to the lab security group (this step assumes an AWS-hosted pod)

Lock down the Ubuntu Server's public IP to prove the tunnel is the only path

Why: Up to this point, the AcmeCorp content has been reachable two ways: directly via the Ubuntu Server's public IP (used during initial setup verification) and through the Cloudflare Tunnel via web.<your-domain> / intranet.<your-domain>. Removing inbound 80 and 8000 from the AWS Security Group proves the key Cloudflare Tunnel property: cloudflared is outbound-only, so the tunnel keeps working even when nothing can connect inbound to the origin. After this step, the only way to reach the AcmeCorp content is through Cloudflare's edge.

Prerequisites: web.<your-domain> and intranet.<your-domain> both work through the tunnel (Steps 8.4 and 10.3 verified this).

Note Ubuntu's public IP. In the AWS Console, go to EC2 > Instances, click lab-ubuntu, and copy the Public IPv4 address.

From your Windows desktop, before the lockdown, confirm both services are reachable on the public IP. Open PowerShell and run:

curl http://<ubuntu_public_ip>
curl http://<ubuntu_public_ip>:8000
Both return AcmeCorp HTML. This is what you are about to break.

In a new browser tab, sign in to the AWS Console and navigate to EC2 > Security Groups.

Click the security group named ubuntu-sg.

Click the Inbound rules tab, then click Edit inbound rules.

Find the two rules with descriptions starting with Website (port 80) from student for setup verification and Intranet (port 8000) from student for setup verification. Click Delete on each. Leave the SSH rule (port 22) in place so you can still SSH into Ubuntu for the rest of the labs.

Click Save rules.

From your Windows desktop, re-run the curls:

curl http://<ubuntu_public_ip>
curl http://<ubuntu_public_ip>:8000
Both should now time out. The public IP is dark.

In an InPrivate browser, re-test the tunnel hostnames: