⚠ 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.
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
The story
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):
Ubuntu 24.04 LTS
nginx serving the AcmeCorp website on port 80 and the AcmeCorp intranet on port 8000
SSH listening on port 22 for student admin access
Has outbound internet access (needed to install cloudflared in Lab 6 and Cloudflare Mesh in Lab 18)
Windows desktop at 10.90.0.90:
Windows, hostname win-desktop
Default gateway 10.90.0.1, DNS via the VPC's DNS resolver
Has internet access; this is your primary working machine
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).
Machine
Username
Password
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.
Username
Password
Purpose
itadmin@acmecorp.com
#Savetheinternet
Admin user; SAML test (Step 1.4) and initial Cloudflare One Client enrollment (Step 3.3)
usera@acmecorp.com
#Savetheinternet
Primary ZTNA test user (Labs 1, 2, 3)
userb@acmecorp.com
#Savetheinternet
Secondary 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.
Username
Password
Available on
UserA
#Savetheinternet
Windows desktop, Ubuntu Server
UserB
#Savetheinternet
Windows 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
This labBefore 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.
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.
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.
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:
Field
Value
Username
itadmin@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:
Setting
Value
Notes
Allow Secure Web Gateway to proxy traffic
On
Confirm TCP, UDP, and ICMP checkboxes are all checked under "Select protocols to forward"
Inspect HTTPS requests with TLS decryption
On
Allow protocol detection
On
Manage HTTP inspection by port
Default
Leave as "Inspect on 80 and 443 only"
Click the Policy settings filter button. Configure these toggles:
Setting
Value
Notes
Allow egress policy host selectors
On
Scan files for malware
On
Also 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 card
Product
What it shows / when you use it
Result column (values)
Admin activity logs
Audit
Configuration changes: who created, updated, or deleted settings.
n/a (audit trail)
Access authentication logs
Access
Login attempts to Access-protected apps. Used in the Access labs (self-hosted apps, App Launcher, private-network ZTNA).
Decision (Allowed / Blocked)
DNS query logs
Gateway
DNS 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 logs
Gateway
L3/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 logs
Gateway
HTTP requests inspected by Gateway, plus HTTP-policy, isolation, and DLP matches. Used in the DNS/HTTP-policy, order-of-enforcement, and SWG/DLP labs.
SSH commands a user ran on a target through Access for Infrastructure.
n/a (command record)
SCIM provisioning logs
Access
Identity sync between Access and your identity provider (90-day retention).
n/a (sync record)
Posture logs
Device client
Device 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 Policyequals, then pick the exact policy name from the Value list.
By user: Field Emailequals 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:
Field
Value
Type
A
Name
web
IPv4 address
The Ubuntu Server public IP you just copied
Proxy status
DNS only (gray cloud; click the orange cloud to toggle it to gray)
TTL
Auto
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
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
Symptom
Likely cause
Fix
SAML test in Step 1.4 fails with "invalid issuer"
Metadata file was downloaded for a different team name
Repeat 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
This labNow 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.
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:
Setting
Value
Why
Install CA to system certificate store
On
Installs 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 device
On
Overrides 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:
Setting
Value
Why
Allow all Cloudflare One traffic to reach enrolled devices
On
When 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:
Field
Value
Policy Name
Lab Enrollment
Action
Allow
Policy session duration
Same as application session duration (default)
In the Policy rules panel on the left side, under Include:
Field
Value
Selector
Login Methods
Value
Select the SAML provider you created in Step 1.3
Click Save policy.
Scroll down to the Authentication section (marked NEW). Configure:
Setting
Value
Effect
Accept all available identity providers
Off
In 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 authentication
On
Allows 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
Symptom
Likely cause
Fix
Lab-Profile not appearing at the top of the profile list
Profile was saved but never dragged into priority position
Drag Lab-Profile above Default on the Device profiles page
Enrollment policy in Step 2.6 still allows OTP
Accept all available identity providers left On
Toggle 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
This labThe 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.
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:
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.
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.
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
Symptom
Likely cause
Fix
MSI install in Step 3.2 hangs
Earlier uninstall did not fully complete
Reboot 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 build
In 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 profile
If 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-in
Default browser had a stale session or the MSI did not pick up ORGANIZATION
Right-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 correctly
Recheck Step 3.3 (SAML auth) and confirm the client is Connected
Cloudflare One Client doesn't pick up the new mdm.xml
File saved as mdm.xml.txt instead of mdm.xml
Save 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
This labA 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.
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 type
Alert name
1
DEX tests availability (beta)
DEX - Application availability
2
DEX tests latency (beta)
DEX - Application latency
3
Device 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
Symptom
Likely cause
Fix
DEX tests in Step 4.2 stay in "No data" forever
Test frequency lower bound varies by entitlement
Wait 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 results
Posture check scoped to the wrong OS
Open 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 terminal
Use 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
This labInternal 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:
#
Name
IPv4 address
TTL
1
web
10.91.0.91 (your Ubuntu Server IP)
Auto
2
ssh
10.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:
Setting
Set to
Effect
Lock device client switch
On
Prevents users from disconnecting the client
Mode switch
Confirm Off
Prevents 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
Symptom
Likely cause
Fix
nslookup web.cf1pslabs.int returns NXDOMAIN at the end of Lab 5
Resolver policy not active or view not linked to the zone
Disconnect button is still clickable after locking the switch
Profile didn't sync to the client yet
Wait 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
This labThe 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 name
cloudflared runs on
Routes private CIDR
Lab-Tunnel
Ubuntu 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.
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:
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:
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.
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:
Field
Value
CIDR
10.91.0.0/24
Tunnel
Select Lab-Tunnel from the dropdown
Description
Lab private network
Virtual network
Expand 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:
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.
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.
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:
Field
Value
Alert name
Lab-Tunnel Health Alert
Description (optional)
Leave blank, or add a one-liner like Alerts when Lab-Tunnel goes down
Notification email
Click + 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:
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
Symptom
Likely cause
Fix
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 Healthy
Route is wrong, split tunnel excludes the destination, Gateway is not forwarding ICMP, or the cloudflared host lacks raw-socket capability or an allowed ping group
Check 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 NXDOMAIN
Internal DNS zone, view, or resolver policy from Lab 5 misconfigured
Run 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
This labNot 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.
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:
Field
Value
Location name
Ubuntu 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).
(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
Symptom
Likely cause
Fix
dnsproxy command not found after install
tar extraction landed in a different directory
Confirm /usr/local/bin/dnsproxy exists and is executable (chmod +x)
Port 5053 already in use
Lab leftover process from a prior run
Find and kill the stale dnsproxy (sudo lsof -i :5053) then restart
DNS query log doesn't show the Ubuntu Server location
Wrong filter; queries hit the system resolver instead of dnsproxy
Verify 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
This labThe 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:
Field
Value
Policy name
Allow Lab Users
Action
Allow
Session duration
24 hours (default)
Include rule: Selector
Emails
Include rule: Value
add 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:
Field
Value
Application name
Ubuntu Web
Session duration
24 hours
Application domain
Subdomain: web, Domain: your domain. Full FQDN: web.<your-domain>
Identity providers
SAML 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:
Field
Value
Subdomain
web
Domain
Select <your-domain> from the dropdown
Path
(leave blank)
Service type
HTTP
URL
10.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.
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.
Field
Value
Application name
Ubuntu SSH
Session duration
24 hours
Application domain
Subdomain: ssh. Full FQDN: ssh.<your-domain>
Browser rendering
SSH (switches the app from a normal HTTPS proxy to a browser-rendered terminal session)
Identity providers
SAML only
Policy
Allow 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:
Field
Value
Subdomain
ssh
Domain
Your domain
Path
(leave blank)
Service type
SSH
URL
10.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.
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:
Field
Value
Username
cloudflare
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.
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
Symptom
Likely cause
Fix
Access HTTPS app returns 502 Bad Gateway
Public hostname on the tunnel points to wrong port or origin is down
Check 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 tunnel
Confirm 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.
This labReal 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
Field
Value
Name your application
RDP Workstation
Local IP address of your RDP target
10.90.0.90
RDP port
3389
Click Continue.
3.8d. Select a public domain
Field
Value
Select domain
<your-domain>
Enter a subdomain
rdp
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:
If you downloaded the .msi: Run it on the Windows desktop and accept UAC. Once it finishes, open a new PowerShell as Administrator window so it picks up the installed command, then check that it works:
cloudflared.exe --version
If you downloaded the .exe: Open PowerShell as Administrator on the Windows desktop. Run this command to copy the executable from Downloads to a permanent location, make it available in this PowerShell window, and check that it works. If you saved it elsewhere, change the Downloads path in the command:
Keep this PowerShell window open for the next command.
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:
Where
What you should see
Access controls > Applications
RDP Workstation, type RDP, URL rdp.<your-domain>
Networks > Connectors
rdp-tunnel, Status HEALTHY
Networks > Routes > CIDR
10.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:
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.
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.
Field
Value
Service token name
Cloudflare One Client Pre-Login
Service Token Duration
Non-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.
Field
Value
Name
Pre-Login Service Auth
Action
Service Auth
Rule type
Include
Selector
Service Token
Value
Cloudflare 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
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
Symptom
Likely cause
Fix
RDP wizard Continue stays disabled on Step 9.1
cloudflared has not connected back to Cloudflare
On 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 VM
Open 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 credentials
Wrong Client ID / Client Secret pasted, or extra whitespace
Regenerate 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
This labCompanies 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:
Field
Value
Subdomain
intranet
Domain
Select <your-domain> from the dropdown
Path
(leave blank)
Service type
HTTP
URL
10.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.
Field
Value
Application name
Intranet
Session duration
24 hours
Application domain
Subdomain: intranet. Full FQDN: intranet.<your-domain>
Identity providers
SAML only
Policy
Allow 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.
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>
Confirm 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 80
Re-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
This labAcmeCorp'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:
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.
Verify: Direct curl http://<ubuntu_public_ip> and curl http://<ubuntu_public_ip>:8000 fail. https://web.<your-domain> and https://intranet.<your-domain> continue to work through the tunnel. The Cloudflare Tunnel is now the only inbound path to the AcmeCorp content.
Lab 11 Wrap-up
By the end of Lab 11 you should have:
Milestone
☐
AWS Security Group inbound rules for ports 80 and 8000 deleted on the Ubuntu Server
☐
Direct curl http://<ubuntu_public_ip> and curl http://<ubuntu_public_ip>:8000 both time out
curl http://<ubuntu_public_ip> still succeeds after deleting the rules
The AWS Security Group used by the Ubuntu Server has more than one ingress rule for those ports, or the rule names differ
Re-open EC2 > Security Groups > ubuntu-sg > Inbound rules and confirm no rules remain for TCP 80 or TCP 8000
Tunnel hostnames also fail after lockdown
Tunnel route or device profile was changed during the exercise
Re-verify Lab 6 Steps 6.4-6.5 and that the Ubuntu Server's outbound to Cloudflare on 443/UDP is unaffected
Lab 11 to Lab 12
Lab 12 turns to inspection: the App Launcher gives users a single portal, the CF_Authorization JWT shows what Access actually trusts, and the Policy Tester predicts allow / block decisions without a real user attempt.
This labWith a handful of apps published, employees need one place that shows only the apps they're entitled to, and operators need to see what the access decision actually trusted. You turn on the App Launcher (the one URL a lot of customers point their helpdesk at) with a tile for each published app, decode a CF_Authorization JWT on jwt.io to read exactly which claims Cloudflare trusted about a session, and run Policy Tester against Ubuntu Web to predict Allow or Block for usera, itadmin, and userb without making anyone re-authenticate. Estimated time: 30-45 minutes.
Prerequisites:
Labs 8, 10, and (optionally) 9 complete so the App Launcher has at least three tiles to show
12.1 Enable the App Launcher
Why: The App Launcher gives every authenticated user a single dashboard, accessible at https://<team-name>.cloudflareaccess.com, listing every Access app they have permission to reach. Enabling it means users have one URL to remember instead of three separate FQDNs.
In the Zero Trust dashboard, click Settings.
Click Authentication.
Find App Launcher and click Manage or the toggle.
Enable the App Launcher.
Optionally customize the title and branding for the lab.
If you completed Step 9.1 (browser-rendered RDP), also add usera@acmecorp.com and userb@acmecorp.com to the App Launcher policy so the RDP Workstation tile appears.
Click Save.
Verify: Open an InPrivate browser to https://<team-name>.cloudflareaccess.com. Authenticate as usera@acmecorp.com. The App Launcher page loads showing tiles for Ubuntu Web, Ubuntu SSH, Intranet, and (if Step 9.1 was completed) RDP Workstation.
12.2 Inspect the CF_Authorization JWT
Why: Every Access-authenticated session sets a cookie named CF_Authorization that contains a signed JWT. The JWT carries the user's identity, the apps they're authorized for, and metadata. Inspecting this token is a useful debugging skill and reveals what Cloudflare actually trusts about the session.
With the Ubuntu Web app open in a browser (Step 8.4), press F12 to open browser developer tools.
Click the Application tab (Chrome/Edge) or Storage tab (Firefox).
aud: an audience ID specific to the Ubuntu Web app
iat and exp: issued-at and expiration timestamps
identity_nonce: a session identifier
Possibly groups: empty for this lab since the SAML IdP didn't provide group claims
Document the aud value. Cloudflare uses this to identify which app the token grants access to. Different Access apps have different audience IDs.
12.3 Use the Policy Tester
Why: Policy Tester evaluates the Ubuntu Web app's policies against users who have already authenticated to Access. You can review who would be allowed or blocked without asking each user to try the app.
In the Zero Trust dashboard, click Access controls > Applications.
Click Ubuntu Web to open it.
Click Test Policy.
Click Test policies. This runs the test for all active users with a prior Access login; you do not need to enter an email address.
Expand the allowed and blocked results to see which users were tested. Look for usera@acmecorp.com, itadmin@acmecorp.com, and userb@acmecorp.com.
Verify: For users included in the results, usera and itadmin should be Allowed, while userb should be Blocked. A user with no prior Access login may not appear in the bulk test.
Lab 12 Wrap-up
By the end of Lab 12 you should have:
Milestone
☐
App Launcher enabled, showing tiles for Ubuntu Web, Ubuntu SSH, Intranet, and (if Lab 9 done) RDP Workstation
☐
CF_Authorization JWT decoded on jwt.io and the email / aud / iss claims inspected
☐
Bulk Policy Tester run against Ubuntu Web; allowed and blocked users reviewed (usera, itadmin, and userb if present)
Lab 12 Troubleshooting
Symptom
Likely cause
Fix
App Launcher loads empty for usera
Apps don't have Allow Lab Users attached, or usera isn't in the policy
Wrong cookie name (CF_AppSession vs. CF_Authorization)
Use the CF_Authorization cookie scoped to the Access app's hostname, not the parent domain
Policy Tester returns "Application not found"
Access app not finished provisioning yet
Wait a minute after creating an app, then open it from Access controls > Applications > <app> > Test Policy
Lab 12 to Lab 13
Lab 13 layers Gateway Network policies on top of the Access apps to enable private-network ZTNA for WARP-enrolled users, then drives the headline posture-based blocking exercise.
This labThis is the headline ZTNA exercise. So far Access has guarded public hostnames someone types into a browser; real ZTNA also gates private-IP destinations for users already on the tunnel, and it has to revoke access the instant a device turns up unfit. You add a Gateway Network policy (Allow Access App Eval) that lets Access evaluate private traffic, turn on WARP Authentication Identity so users aren't re-prompted, build and then tear down two private-network apps to prove the pattern, stack the three posture policies, then turn Windows Firewall off and watch access drop, turn it back on and watch it return. The posture check from Lab 4 just became an access decision. Estimated time: 60-75 minutes.
Prerequisites:
Labs 1 through 12 complete
Cloudflare One Client connected as itadmin@acmecorp.com
Firewall posture check from Lab 4 currently passing
13.1 Configure a Gateway Network policy that lets Access evaluate private traffic
Why: Up to this point, the three Access apps work for non-WARP users (someone in an InPrivate browser tab without Cloudflare One Client). For WARP users (someone with Cloudflare One Client connected), private-IP destinations are normally just routed through the tunnel without Access evaluation. To make Access policies apply to WARP-user traffic against private IPs, you need a Gateway Network policy that explicitly hands traffic to Access when the destination matches a self-hosted Access app with a private address.
In the Zero Trust dashboard, expand Traffic controls.
Click Firewall policies.
Click the Network tab.
Click Add a policy. Configure:
Field
Value
Policy name
Allow Access App Eval
Action
Allow
Traffic
Self-hosted Access App with Private Address is Present
Save.
Drag this policy to the top of the Network policy list (it must evaluate before any deny-all rule).
Verify: The policy appears at the top of the Network firewall policies list. It is set to Allow when the destination matches a self-hosted Access app with a private address.
13.2 Enable WARP Authentication Identity for SSO
Why: Without this setting, a WARP-enrolled user who navigates to an Access app gets prompted to authenticate again. With it on, WARP's existing identity is passed through as the Access identity automatically - true SSO from device to app.
Why: This exercise demonstrates the Access pattern for private resources accessed by WARP users. Each app maps to a specific internal IP and port. Access policies on each app gate which users can reach that exact resource.
Create both apps via Access controls > Applications > + Add an application > Self-hosted and private. Use the values below:
App Name
Destination Type
IP
Ports
Policy
Web-internal
Private network
10.91.0.91
80, 443
Allow Lab Users
SSH-internal
Private network
10.91.0.91
22
Allow Lab Users
For each app: set Identity providers to SAML only. Click Next, select the Allow Lab Users policy, click Confirm > Next > Add application.
Verify: Two new entries appear in the Applications list: Web-internal and SSH-internal. Each shows a private-network destination.
13.4 Test private-network ZTNA access from the WARP device
Why: Confirm that WARP-user traffic to the private IPs/hostnames is intercepted by Access and gated by the policies.
On the Windows desktop (signed in as cloudflare, Cloudflare One Client enrolled as itadmin@acmecorp.com), confirm the client is Connected.
Open a browser and navigate to http://10.91.0.91 (the Ubuntu Server web).
What is the result? Document what happens.
Open PowerShell. SSH to the Ubuntu Server:
ssh cloudflare@10.91.0.91
What is the result? Document.
Expected result: Both access attempts should succeed because itadmin@acmecorp.com is in the Allow Lab Users policy attached to both private-network Access apps. If prompted for re-authentication, authenticate as itadmin@acmecorp.com / #Savetheinternet.
Verify: Web loads, SSH connects.
13.5 Tear down the private ZTNA Access apps before the next exercise
Why: The next exercise demonstrates the alternative approach to ZTNA: use Gateway Network policies instead of Access apps. Before that, remove the Access apps so the Network policies are the only thing controlling access.
Access controls > Applications.
For each of Web-internal and SSH-internal, click the three-dot menu and select Delete.
Confirm each deletion.
Verify: Only Ubuntu Web and Ubuntu SSH remain in the Applications list. The two private-network apps are gone.
13.6 Deploy the three-policy posture stack as Gateway Network policies
Why: This is the production pattern for ZTNA via Gateway. Three policies evaluate top-to-bottom; the first match wins.
Navigate to Traffic controls > Firewall policies. Click Add a policy. The policy editor opens with a Scope section containing IF Conditions (left) and THEN Actions and settings (right), and a Details section (bottom).
Key detail: The posture condition uses Passed Device Posture Checks with operator is not and value Firewall Check. This means "the Firewall Check has NOT passed"; i.e., the firewall is off. If the Firewall Check dropdown shows "No valid options," confirm your device has reported at least one posture result (check Insights & Logs > Logs > Posture logs). The posture check from Step 4.1 must have run at least once.
Click Create policy.
Policy 2: Cleanup-Policy-AllInternal
Section
Field
Value
If traffic type is...
Dropdown
Network
Then... (Required)
Action dropdown
Allow
And traffic matches...
Click + Add condition
Selector: Destination IP, Operator: in (not "is"), Value: 10.0.0.0/8
Details > Policy name
Cleanup-Policy-AllInternal
No posture condition on this one. Click Create policy.
Policy 3: All-NET-InternalNetwork-ImplicitDeny
Section
Field
Value
If traffic type is...
Dropdown
Network
Then... (Required)
Action dropdown
Block
And traffic matches...
Click + Add condition
Selector: Destination IP, Operator: in (not "is"), Value: 10.0.0.0/8
Details > Policy name
All-NET-InternalNetwork-ImplicitDeny
No posture condition. Click Create policy.
Order the policies
After creating all three, drag them into this order (first match wins):
Posture-Fail-NET-Restricted-Access (Block when firewall is off)
Cleanup-Policy-AllInternal (Allow everyone who passed posture)
All-NET-InternalNetwork-ImplicitDeny (Block everything else as a safety net)
The Allow Access App Eval policy from Step 13.1 should remain above all three.
Verify: The Network policy list shows all four policies in order: Allow Access App Eval at the top, then the three posture-stack policies. Each policy is enabled.
13.7 Test access with posture check passing
Why: Baseline test. With Windows Firewall on and the machine joined to the domain (from Lab 2), both posture checks should report Pass and traffic should flow.
On the Windows desktop (signed in as cloudflare, client enrolled as itadmin@acmecorp.com). Confirm:
Cloudflare One Client: Connected
Windows Firewall: On (Control Panel > System and Security > Windows Defender Firewall)
Test each access path:
SSH: ssh cloudflare@10.91.0.91 (or use the browser-rendered SSH app from Step 8.7)
13.8 Posture failure exercise - disable Windows Firewall and observe the block
Why: This is the headline intermediate-level exercise for Lab 3. Disabling the firewall causes the Firewall Check posture to flip from Pass to Fail. The Posture-Fail-NET-Restricted-Access policy then blocks all access to 10.0.0.0/8. Demonstrating this proves the posture-based access control works.
On the Windows desktop, open Control Panel > System and Security > Windows Defender Firewall > Customize Settings.
Turn off Windows Defender Firewall for both Domain and Private profiles. Save.
Wait 10 minutes. Posture results refresh on a periodic cycle (default is around 10 minutes; some builds use shorter intervals).
Verify posture has flipped to Fail:
In the Zero Trust dashboard, Team & Resources > Devices > Device posture.
Find your device in the Firewall Check results. It now shows Fail.
Both should fail because Posture-Fail-NET-Restricted-Access now matches and blocks.
Verify: Both access attempts time out or are explicitly denied. In the Zero Trust dashboard, Insights & Logs > Logs > Network logs, click + Add filter and set Field Destination IP, Operator equals, Value 10.91.0.91. The most recent entries show Action = Block and the Policy name column shows Posture-Fail-NET-Restricted-Access.
Restore the firewall:
Re-enable Windows Defender Firewall for Domain and Private profiles.
Wait 10 minutes for posture to refresh.
Re-test access. All three should succeed again.
13.9 Explore the block events in the Logs page
Why: The Gateway Network logs from Step 13.8 are useful for auditing posture failures.
In the Zero Trust sidebar, click Insights & Logs > Logs.
Click the Network logs card.
Click + Add filter, set Field Policy, Operator equals, and pick Posture-Fail-NET-Restricted-Access from the Value list. (The Operator only offers equals / does not equal, so match the exact policy name rather than a substring.)
Inspect the results.
Verify: Each row corresponds to an attempted access during Step 13.8. The Action column shows Block, the Policy name column shows Posture-Fail-NET-Restricted-Access, the Email matches UserA, and the Destination IP is 10.91.0.91.
13.10 (Optional) Configure Infrastructure Access for SSH with command logging
Why: Infrastructure Access protects native SSH from a Cloudflare One Client device with an Access policy and short-lived SSH certificates. Command logging requires a separate SSH log encryption key; it is not a connection option in the application editor. You download and decrypt the recorded commands with your private key.
Do this step only if you have lab time remaining. There are four sub-tasks:
a. Create an SSH target
Navigate to Access controls > Targets. If a target already maps 10.91.0.91 in the default virtual network, use that target in sub-step b. The same IP and virtual network cannot be assigned to a second target. Otherwise, click Add a target and fill in:
Field
Value
Hostname
UbuntuServerSSH (no spaces)
IPv4
Type the full address 10.91.0.91, then select it with the default virtual network
If you filled in the new-target form, click Add target.
b. Create an Infrastructure Access application
Navigate to Access controls > Applications > Create new application > Infrastructure > Continue with Infrastructure. The editor is one form; it does not have separate Next pages.
Field
Value
Application name
Infrastructure SSH - Ubuntu Server
Target criteria > Include > Target hostname
Select the existing or newly created target for 10.91.0.91 from sub-step a
Port
22
Protocol
SSH
Access enforcement > Policy name
Allow Infrastructure SSH Users
Configure rules > Include > Selector
Emails: add usera@acmecorp.com and itadmin@acmecorp.com (the same users allowed by the policy from Step 8.1)
Connection context > SSH user
cloudflare (the existing local Ubuntu account); leave the email-alias checkbox off
Click Save. This creates a new Infrastructure policy with the same allowed emails as Allow Lab Users; the existing policy is not selected in this form.
For the SSH CA, go to Access controls > Service credentials > SSH. Click Add a certificate, select SSH with Access for Infrastructure, then click Generate SSH CA. Open the new certificate and copy its CA public key. Do not choose the separate Application certificate type.
c. Configure the Ubuntu Server to trust the CA
SSH into the Ubuntu Server console (cloudflare / #Savetheinternet) and run:
sudo nano /etc/ssh/ca.pub
Paste the public key from sub-step b. Save the file.
Save, validate the configuration, and reload the SSH service:
sudo sshd -t
sudo systemctl reload ssh
Why: Adding Cloudflare's SSH CA public key to TrustedUserCAKeys tells the server's SSH daemon to trust short-lived certificates that Cloudflare signs for authenticated users. That replaces static SSH keys and passwords for this Infrastructure Access connection: instead of managing per-user credentials on the box, the server trusts one CA, and Cloudflare issues a fresh, minutes-long certificate each time an authorized user connects. Reloading sshd applies the validated configuration without dropping an existing SSH session.
d. Test native SSH and encrypted command logging
On the Windows desktop, confirm the Cloudflare One Client is connected as itadmin@acmecorp.com. For command logging, download the Windows ZIP from the ssh-log-cli releases, extract it, and open PowerShell in the folder containing ssh-log-cli.exe. Generate an encryption key pair:
.\ssh-log-cli.exe generate-key-pair -o sshkey
Display the public key so you can copy it:
Get-Content -Raw -LiteralPath '.\sshkey.pub'
In the Zero Trust dashboard, go to Traffic controls > Traffic settings > Traffic logging > SSH log encryption public key, click Edit, paste the public key, and click Save. Keep the matching private-key file sshkey on your Windows desktop. The dashboard cannot display decrypted command text without it.
In PowerShell on the Windows desktop, connect to the Ubuntu Server through the Cloudflare One Client with your native SSH client:
ssh cloudflare@10.91.0.91
Run whoami, ls, pwd, and date. Then go to Insights & Logs > Logs > SSH command logs, select the session, and download its encrypted log. In PowerShell from the same folder, run .\ssh-log-cli.exe decrypt -i <downloaded-log-file> -k .\sshkey, replacing the file placeholder with your download path. Extract the resulting .decrypted.zip to inspect the text files. See the SSH log download and decryption instructions for the file format.
Verify: The SSH session opens as the local cloudflare user without a reusable SSH key. The downloaded encrypted log can be decrypted with your private key to inspect the commands you ran; their plaintext is not displayed in the dashboard.
Lab 13 Wrap-up
By the end of Lab 13 you should have:
Milestone
☐
Allow Access App Eval Gateway Network policy at the top of the Network list
☐
WARP Authentication Identity on so WARP's identity flows into Access (silent SSO)
☐
Two private-network ZTNA Access apps (Web-internal, SSH-internal) created, tested, and then torn down
New Network policies were added later and pushed it down
Drag Allow Access App Eval back to the top of the Network policies list
Lab 13 to Lab 14
Lab 14 turns from identity-based access controls to content-based controls. The first SWG lab deploys the Cloudflare-recommended DNS and HTTP policy stacks plus a Browser Isolation policy for sensitive sites.
Lab 14 - DNS & HTTP Policies with Browser Isolation
This labDeciding who gets in is only half the job. The other half is screening what everyone sends and receives, in both directions. You stand up Gateway's content controls: the recommended DNS policy stack (blocks at resolution time, the cheapest place to stop something), the recommended HTTP stack (inspects the decrypted request for paths and categories), and Browser Isolation, where a risky page runs in a Cloudflare-hosted browser so any zero-day stays off the user's device. Estimated time: 45-60 minutes.
Prerequisites:
Labs 1 through 13 complete
Cloudflare One Client connected on the Windows desktop
TLS decryption is On (from Lab 1 Step 1.5)
Protocol detection is On (from Lab 1 Step 1.5)
Scan files for malware is On (from Lab 1 Step 1.5)
14.1 Set up DNS recommended policies
Why: Cloudflare publishes a recommended baseline DNS policy stack that covers the most common threats: malware C2, phishing, content categories (Adult, Gambling, etc.), and country-based blocks. Deploying the baseline once gives you something to test against and a reference point for customizations.
The Cloudflare-recommended DNS policy stack typically includes:
Security categories block (Malware, Command and Control & Botnet, Phishing, etc.)
In the Zero Trust dashboard, click Traffic controls > Firewall policies > DNS tab.
Click Add a policy to create each policy listed above. In the policy editor, set its conditions and action, enter its name under Details, and click Create policy.
Confirm all created policies are Enabled.
Verify: The DNS firewall policy list shows the recommended policies. Each is enabled.
14.2 Test the DNS policies
Why: Confirm that the DNS policies are actually intercepting and blocking queries before adding more complexity.
On the Windows desktop with Cloudflare One Client connected, open a browser and test each site:
Inspect the logs: Insights & Logs > Logs > DNS query logs. Click + Add filter and set Field Email, Operator equals, Value your device email. Confirm blocked entries show Action = Block and the Policy name column shows the matching policy.
Verify: bbc.com succeeds. malware.testcategory.com shows a block page. Russian site is blocked. DNS logs show block entries with the correct matching policy in the Policy name column.
14.3 Disable DNS policies before testing HTTP policies
Why: Both DNS and HTTP policies can block the same destinations. To isolate the effect of HTTP policies in the next step, temporarily disable the DNS policies. (You'll re-enable everything in Step 16.1 for the order-of-enforcement exercise.)
In the Zero Trust dashboard, navigate to Traffic controls > Firewall policies > DNS tab.
For each policy in the list, click the status toggle to switch it to Off (disabled). Repeat for every DNS policy.
Confirm all DNS policies are showing as disabled (grey/off toggle).
Verify: All DNS firewall policies are toggled off. Re-test malware.testcategory.com in a browser - should now succeed (or fail differently than before, depending on what the browser does with the connection).
14.4 Set up HTTP recommended policies
Why: HTTP policies operate after DNS resolution succeeds and can inspect the actual HTTP/HTTPS request. This is where TLS decryption matters - HTTP policies can match on URL paths, request headers, hostname, and content, none of which are visible without TLS decryption.
In the Zero Trust dashboard, navigate to Traffic controls > Firewall policies > HTTP tab.
Click Add a policy to create each policy listed below.
#
Policy Name
What it blocks
1
Security categories block
Malware, C&C, Phishing, etc.
2
Content categories block
Adult, Gambling, Weapons, etc.
3
Country block
High-risk geographies
4
File type controls
Executable downloads, etc.
5
Untrusted certificate block
Sites with self-signed, expired, or invalid TLS certificates
In the policy editor, set each policy's conditions and action, enter its name under Details, and click Create policy.
Confirm all created policies are Enabled.
Verify: The HTTP firewall policy list shows all five recommended policies, each enabled.
14.5 Test the HTTP policies
Why: Confirm HTTP policies match where DNS policies did. Differences will show because HTTP can match URL paths and other fine-grained signals.
On the Windows desktop, open a browser. Repeat the same test pattern from Step 14.2, now with only HTTP policies active:
Inspect logs: Insights & Logs > Logs > HTTP request logs. Click + Add filter and set Field Email, Operator equals, Value your device email. The Policy name column shows the HTTP policy that matched (not the DNS policy from Step 14.2, which is now disabled).
Verify: Same observable behavior as Step 14.2 (sites blocked), but the matching policies in the logs are now HTTP-tier policies.
14.6 Create a Browser Isolation list and policy
Why: Remote Browser Isolation (RBI) renders pages in a Cloudflare-hosted browser instead of the user's local browser. The user sees a video stream of the page. This isolates the user from any malware or zero-days the page might exploit. Common use cases: marketing teams visiting risky competitor sites, security teams analyzing suspicious URLs, or as a general policy for "lightly risky" content categories.
The lab builds an isolation policy for dlptest.com (a public DLP test site) configured as a "view-only" experience.
Step a. Create the isolation list
Navigate to Reusable components > Lists.
Click Create manual list.
Field
Value
List name
Domain Isolation
List type
Hostnames
In the Add entry field, type dlptest.com and click Add.
Click Save.
Step b. Create the HTTP isolation policy
Navigate to Traffic controls > Firewall policies > HTTP tab.
Click + Add a policy.
Field
Value
Name
All-HTTP-Domain-Isolate
Action
Isolate
Traffic
Domain in list Domain Isolation
Configure Isolation settings:
Setting
Value
Copy from remote to client
Do not allow
Paste from client to remote
Do not allow
Printing
Do not allow
File uploads
Do not allow
File downloads
Allow (needed for next test)
Click Save.
Verify: The new policy appears at or near the top of the HTTP policy list with action Isolate and the four restrictions configured.
14.7 Test the isolation policy
Why: Confirm isolation works and the restrictions block the expected actions.
On the Windows desktop, open a browser and navigate to https://dlptest.com/sample-data/. The page loads inside Browser Isolation (look for a thin yellow border or Cloudflare toolbar).
Action
Expected
Download a sample file from the /sample-data/ page
DNS policies created but malware.testcategory.com still resolves
Browser cached the previous DNS result
Open an InPrivate browser window or run ipconfig /flushdns in PowerShell
Isolation policy not isolating dlptest.com
Either the list doesn't contain the domain, or a higher-priority HTTP policy is matching first
Check the Lists entry; check policy order; check Insights & Logs > Logs > HTTP request logs for which policy actually matched
Lab 14 to Lab 15
Lab 15 keeps building the SWG stack: untrusted certificate handling, a TLS inspection bypass list, the recommended Network policy stack, and a targeted SMTP block on port 25.
This labInspection can't open every connection the same way. Some destinations pin certificates and break under decryption (banks are the classic case), some present invalid certificates you have to make a ruling on, and some traffic never reaches the HTTP tier at all. You tune the checkpoint: a do-not-inspect bypass list for the domains that break under decryption, an untrusted-certificate ruling (block versus pass through), the recommended Network policy stack, and a targeted rule that blocks outbound SMTP on port 25, because port 25 isn't HTTP and has to be stopped a layer down. Estimated time: 45-60 minutes.
Prerequisites:
Lab 14 complete (DNS, HTTP, and Isolation policies in place)
15.1 Test the untrusted certificate policy
Why: The recommended policies you built in Step 14.4 include an Untrusted certificate block policy. This policy uses the Untrusted certificate action to control what happens when Gateway encounters a self-signed, expired, or otherwise untrusted TLS certificate. The three options are: Error (default: show Gateway error page), Block (show your block page), or Pass through (bypass the warning and connect to the upstream). This step tests the existing policy against a known bad-cert site.
Navigate to Traffic controls > Firewall policies > HTTP tab. Find the Untrusted certificate block policy from Step 14.4 and confirm it is enabled. Click it to open the editor.
In the policy editor, look at the right side under THEN > Actions and settings. The Untrusted certificate action dropdown appears below the Action dropdown (which should be set to Allow). The dropdown reads "Set how Gateway should handle insecure requests." Confirm it is set to Block.
Test: On the Windows desktop, open a browser and navigate to https://expired.badssl.com/. The site presents an expired TLS certificate. Gateway should block it with a block page.
(Optional) To see the difference between Block and Pass through:
In the policy editor, change the Untrusted certificate action dropdown from Block to Pass through. Save.
Revisit https://expired.badssl.com/; the page now loads without a certificate warning because Gateway passes through to the untrusted origin.
Change it back to Block and save.
Verify: With Block, expired.badssl.com shows a Gateway block page or error. With Pass through, the page loads. Check Insights & Logs > Logs > HTTP request logs, filter by expired.badssl.com to confirm the policy matched.
15.2 Configure a TLS inspection bypass list
Why: Some sites use certificate pinning, custom TLS configurations, or other quirks that break under Cloudflare's TLS decryption. Banking and certain enterprise SaaS apps are common examples. The bypass list lets you exempt specific domains from decryption while keeping decryption on for everything else.
Step 1. Create the bypass list
Navigate to Reusable components > Lists. Click Create manual list.
Field
Value
List name
DomainInspectionBypass
List type
Hostnames
In the Add entry field, type cnn.com and click Add.
Click Save.
Step 2. Create the bypass policy
Navigate to Traffic controls > Firewall policies > HTTP tab. Click Add a policy.
Field
Value
Name
HTTP-DoNotInspect-Bypass
Action
Do Not Inspect
Traffic
Domain in list DomainInspectionBypass
Click Save. Move this policy to the top of the HTTP policy list (Do Not Inspect must evaluate before any policy that would decrypt).
Verify: The policy is at the top of the HTTP list with action Do Not Inspect.
15.3 Confirm TLS bypass actually skips decryption
Why: The cleanest signal that decryption is being bypassed is the TLS certificate the browser sees. With decryption on, the certificate is issued by Cloudflare Managed G2 (your Gateway root CA). With decryption bypassed, the certificate is whatever the origin actually presented.
Click the padlock icon in the browser address bar.
Click Connection is secure > Certificate is valid (or similar - exact wording varies by browser).
Look at the Issued by field.
Expected: the issuer is something like GlobalSign, DigiCert, or similar public CA - NOT Cloudflare Managed G2.
Compare: navigate to another site that is NOT on the bypass list, like https://www.bbc.com. Inspect its certificate. Issued by should be Cloudflare Managed G2 because that site is being decrypted.
Verify: cnn.com shows a public CA in Issued by. bbc.com shows Cloudflare Managed G2. The difference confirms the bypass works.
15.4 Disable HTTP policies before testing Network policies
Why: Same isolation reasoning as Step 14.3: keep Network policies as the only filter so you can see their effect cleanly. You will re-enable everything in Step 16.1.
Navigate to Traffic controls > Firewall policies > HTTP tab.
For each HTTP policy in the list, click the status toggle to switch it to Off (disabled). Repeat for every HTTP policy.
Verify: All HTTP policies show disabled.
15.5 Set up Network recommended policies
Why: Network policies operate at the TCP/UDP layer - they match on destination IP, port, protocol, and certain selectors that work before any HTTP-level decryption. They're useful for blocking non-HTTP protocols and for catching traffic that bypasses HTTP filtering (anything not on ports 80/443).
Navigate to Traffic controls > Firewall policies > Network tab. Click Add a policy to create each policy manually:
Policy Name
Action
Traffic Selector
All-NET-Internet-Blocklist
Block
Security Categories matches all (Malware, C&C, Phishing, etc.) + Destination IP in Domain/Host Blocklist
All-NET-NO-HTTP-HTTPS-Internet-Deny
Block
Destination port NOT in {80, 443} AND Destination IP in 0.0.0.0/0. Blocks all non-web outbound traffic. Calibrate before production: breaks SSH, RDP, FTP, mail, etc.
Click Create policy for each. Both should be Enabled.
Verify: Both Network policies in the firewall policy list, enabled.
15.6 Test the Network policies
Why: Confirm Network policies catch what they should catch and that the non-web blocker behaves as expected.
Insights & Logs > Logs > Network logs. Filter by Action: Block. Confirm an entry matches the Network blocklist policy.
Test non-HTTP block:
On the Windows desktop, open PowerShell.
Try an outbound SSH connection to a public SSH test server:
ssh -p 22 demo@test.rebex.net
The connection should be blocked by the All-NET-NO-HTTP-HTTPS-Internet-Deny policy because SSH is on port 22.
Check Insights & Logs > Logs > Network logs, filter by destination test.rebex.net or by Block. Confirm the block entry exists.
Verify: Malware test domain is blocked at Network tier; SSH to test.rebex.net fails; both blocks appear in Gateway Network logs.
15.7 Create a targeted SMTP block and test it
Why: The previous step used a broad "block all non-HTTP/HTTPS" policy. In production, you often want surgical port blocks for specific protocols. SMTP (port 25) is the classic example: blocking outbound SMTP from endpoints prevents compromised machines from sending spam directly while still allowing users to send email through their webmail or authenticated SMTP relay.
This should succeed (TcpTestSucceeded: True) since HTTPS is allowed. 9. Inspect logs: Insights & Logs > Logs > Network logs. Filter by destination port 25. The block entry shows the Block-Outbound-SMTP policy matched.
Verify: Port 25 is blocked, port 443 is allowed. The Network log shows the SMTP block with the correct policy name.
Lab 15 Wrap-up
By the end of Lab 15 you should have:
Milestone
☐
Untrusted certificate handling tested against expired.badssl.com (Block vs. Pass through)
☐
TLS inspection bypass list DomainInspectionBypass with cnn.com
☐
HTTP-DoNotInspect-Bypass policy at the top of the HTTP list, verified via certificate issuer
☐
Network recommended policies built and tested
☐
Targeted SMTP (port 25) block tested with Test-NetConnection
Lab 15 Troubleshooting
Symptom
Likely cause
Fix
TLS bypass on cnn.com not working (still shows Cloudflare Managed G2 cert)
Do Not Inspect policy is below an Allow policy that decrypts
Move HTTP-DoNotInspect-Bypass to the top of the HTTP list (Step 15.2)
Test-NetConnection ... -Port 25 succeeds
Port 25 block is below a broader Allow policy
Re-check Step 15.7 ordering; the SMTP block must evaluate before any broad allow
Lab 15 to Lab 16
Lab 16 brings the SWG arc to a head with two prediction exercises: which policy tier fires when DNS, Network, and HTTP all evaluate simultaneously, and how the Cloudflare One Client's mode setting changes which policies apply. DLP basics fit naturally between the two.
This labWith DNS, Network, and HTTP all switched on, only one tier fires for any given request, and knowing which one wins is the daily skill that separates a competent operator from a confused one. You run the full order-of-enforcement exercise across five test cases, add a DLP policy that blocks financial data from leaving, then predict how the same three requests behave as you flip the client between DNS-only, Traffic-only, and Traffic-and-DNS modes. Estimated time: 45-60 minutes.
Prerequisites:
Lab 15 complete (TLS bypass and Network policies in place)
All DNS and HTTP policies from Labs 14 and 15 ready to re-enable
16.1 Order-of-enforcement exercise: enable all policies and predict
Why: This is one of the most useful exercises in the lab. When DNS, HTTP, and Network policies are all enabled simultaneously, only one fires per request. The order is fixed by Cloudflare's policy engine: DNS evaluates first (before resolution succeeds), then Network (before TLS handshake), then HTTP (after TLS handshake completes). For a single request like "browser navigates to https://malware.testcategory.com", which tier wins?
Re-enable all DNS policies (Step 14.3 disabled them): Traffic controls > Firewall policies > DNS tab. Toggle each policy back On.
Re-enable all HTTP policies (Step 15.4 disabled them): Traffic controls > Firewall policies > HTTP tab. Toggle each back On (including isolation from 14.6, untrusted cert from 15.1, and TLS bypass from 15.2).
Confirm Network policies are still on from Step 15.5.
Predict before testing. For each scenario, write down which tier (DNS, Network, or HTTP) you think will fire:
Test each scenario. Visit each URL. Note: was it blocked, allowed, or isolated? After each test, check Insights & Logs > Logs and open the relevant dataset card (DNS, Network, or HTTP) filtered by destination/hostname. The dataset where the entry appears tells you which tier fired.
Expected results (typical Cloudflare order: DNS → Network → HTTP):
Request
Which tier fires
Why
http://malware.testcategory.com
DNS
DNS policy resolves first; security category match blocks before any TCP connection
https://malware.testcategory.com
DNS
Same - DNS evaluation precedes TLS
https://dlptest.com/sample-data/
HTTP
DNS allows (not in security categories), Network allows, HTTP isolation policy fires
https://www.cnn.com
HTTP Do-Not-Inspect
DNS allows, Network allows, HTTP first match is the Do Not Inspect rule which lets the connection through unmodified
SSH to test.rebex.net:22
Network
DNS allows (it's a legit domain), Network policy blocks port 22 outbound
Verify: Your predictions match the actual matching tier shown in the Logs page. Document any surprises - those are the learning moments.
16.2 Build a DLP policy for financial data
Why: Data Loss Prevention scans outbound HTTP/HTTPS traffic for sensitive patterns (credit cards, SSNs, financial data, custom regex). When a match is found, the policy can block, allow with logging, or allow with notification. The lab uses the built-in Financial Information profile against the public dlptest.com test site.
Verify the DLP profile: Navigate to Data loss prevention > Profiles. Confirm the Financial Information built-in profile exists.
Disable isolation for dlptest.com: Navigate to Traffic controls > Firewall policies > HTTP tab. Toggle All-HTTP-Domain-Isolate to Off (otherwise it isolates dlptest.com and you cannot test DLP on it).
Create the DLP policy: On the same HTTP tab, click + Add a policy.
Field
Value
Name
Block-DLP-Financial
Action
Block
Traffic
DLP Profile Financial Information matches Present
Click Save.
Verify: The DLP policy is in the HTTP policy list, enabled.
16.3 Test the DLP policy
Why: Confirm DLP detects financial data being uploaded and blocks it.
Navigate to https://dlptest.com/sample-data/. Download sample-SSN.txt and sample-CCN.txt. Open one in Notepad to confirm it contains test data.
Inspect logs: Insights & Logs > Logs > HTTP request logs. Filter by hostname dlptest.com and action Block. Confirm the DLP Profile column shows Financial Information.
Verify: Paste fails; upload fails; logs show the DLP block with the matching profile.
16.4 Mode-switch prediction exercise
Why: This is the second key intermediate exercise (alongside Step 16.1). The Cloudflare One Client supports several modes that change which tiers of Gateway policy can match. The lab cycles through modes and asks the student to predict which policies fire in each.
Mode
What routes through Cloudflare
Which policies apply
Traffic and DNS mode (default)
All device traffic + DNS resolution forwarded to client DNS resolver
DNS, Network, and HTTP
DNS only mode
DNS resolution only; device traffic goes direct
DNS only
Traffic only mode
All device traffic; DNS resolution managed by device OS
Network and HTTP only
Posture only mode
Nothing; client only reports device health and posture data
None (posture data available for policies but no traffic routing)
Local proxy mode
Only explicitly-directed local HTTP traffic
HTTP only (for traffic sent to the local proxy)
How to switch modes
Mode is set in the Device Profile, not in the client UI. Navigate to Team & Resources > Devices > Device profiles > Lab-Profile. Scroll to Service mode and select the mode you want. Click Save Profile. The Cloudflare One Client on the device will pick up the change within a minute.
Test 1: DNS only mode
In the Lab-Profile, change Service mode to DNS only mode and click Save Profile. Wait for the client to update.
DNS still routes to Gateway, security category match fires
https://dlptest.com/sample-data/
Loads normally (NOT isolated)
DNS allows; HTTP/Network policies do not apply because traffic does not proxy through Cloudflare; isolation policy never evaluates
SSH to test.rebex.net:22
Succeeds
Network policy does not apply (no proxy); DNS allows
Test 2: Traffic only mode
In the Lab-Profile, change Service mode to Traffic only mode and click Save Profile. Wait for the client to update. Predict, then test, the same three requests.
Expected results in Traffic only mode:
Request
Result
Why
https://malware.testcategory.com
Blocked by Network or HTTP
DNS does NOT route to Gateway (so DNS policy can't fire), but the traffic still proxies through Cloudflare; Network or HTTP security category match fires
https://dlptest.com/sample-data/
Isolated
Same as default mode - traffic proxies through Cloudflare
SSH to test.rebex.net:22
Blocked by Network
Network policy still applies (traffic proxies through Cloudflare)
Restore default mode
In the Lab-Profile, change Service mode back to Traffic and DNS mode and click Save Profile.
Verify: All three modes behave as predicted. The mode-switch exercise demonstrates a core architectural truth: which tier of policy can fire depends on which Cloudflare One Client mode is active.
Lab 16 Wrap-up
By the end of Lab 16 you should have:
Milestone
☐
Order-of-enforcement exercise: DNS, Network, and HTTP policies all enabled and predictions matched (DNS → Network → HTTP)
☐
Financial Information DLP profile attached to the Block-DLP-Financial HTTP policy
☐
DLP block verified with sample SSN / CCN files (paste and upload both blocked)
☐
Mode-switch exercise: DNS only mode, Traffic only mode, then back to Traffic and DNS mode
Lab 16 Troubleshooting
Symptom
Likely cause
Fix
Mode-switch shows greyed-out options in the client UI
Mode switch lock from Lab 5 Step 5.4 still active
Temporarily toggle Mode switch to On in the device profile, run the exercise, then turn it back Off
DLP doesn't block paste / upload in Step 16.3
TLS decryption isn't On, or the isolation policy is intercepting dlptest.com before DLP can run
Confirm TLS decryption On (Lab 1 Step 1.5); toggle All-HTTP-Domain-Isolate Off during the test (Step 16.2 sub-step 2)
Order-of-enforcement predictions don't match for one request
Browser cached an earlier verdict, or a higher-priority policy is hiding
Open InPrivate; verify the matching policy in Insights & Logs > Logs
Lab 16 to Lab 17
Lab 17 finishes the SWG arc with the advanced toggles: custom Gateway block pages, clientless RBI, a custom DLP detection entry with payload encryption (and offline decryption), the Gateway Authorization Proxy with PAC file, and tenant control header injection.
This labFive features that show up in real deployments once the basics are in. You brand the block page so users know where to get help, stand up clientless RBI for partners and personal devices that can't run the client, write a custom DLP detection for AcmeCorp's own employee-ID pattern (ACME- followed by six digits) with an encryption keypair so analysts can decrypt matches offline, route agentless devices through Gateway with a PAC file, and inject tenant-control headers so nobody signs into a personal SaaS account on a managed device. This is screening extended to everyone, even the users who never installed the client. Estimated time: 60-75 minutes.
Prerequisites:
Labs 14, 15, and 16 complete
17.1 Create a custom Gateway block page
Why: The default Cloudflare block page is functional but generic. A custom block page lets you brand the experience, provide a help desk URL, and explain to users why their request was blocked.
"This site has been blocked by your organization's security policy. Contact helpdesk@acmecorp.com if you believe this is an error."
Click Save.
Attach the block page to a policy: Traffic controls > Firewall policies > HTTP tab > open an existing Block policy > Edit.
Under Actions and settings > Modify Gateway block behavior, select Custom block page and choose Lab Block Page. Save.
Trigger the block by navigating to a site that matches the policy.
Verify: The block page renders with your custom text, not the default Cloudflare block page.
17.2 Enable clientless Remote Browser Isolation
Why: Clientless RBI lets users access isolated browser sessions without the Cloudflare One Client installed. Traffic is rendered through a Cloudflare-hosted browser as a video stream.
Step 1. Enable clientless RBI settings
Navigate to Browser isolation > Browser isolation settings.
Setting
Value
Allow users to open a remote browser without the device client
On
Allow isolated HTTP traffic when user identity is unknown
On (if available)
Save.
Step 2. Create a clientless isolation policy
Navigate to Traffic controls > Firewall policies > HTTP tab. Click Add a policy.
Verify: The page renders inside the isolated browser. Copy and print are blocked. Check Insights & Logs > Logs > HTTP request logs to confirm the isolation policy matched.
17.3 Create a custom DLP detection entry with regex and payload encryption
Why: Custom DLP detection entries let you detect company-specific data like employee IDs or project codes. Payload encryption lets you decrypt matched content in logs.
Note: If you hit a permissions wall on "Create profile," use Data loss prevention > Detection entries > Create a detection entry instead.
a. Generate a DLP payload encryption keypair
Navigate to Data loss prevention > DLP settings. Find Set a DLP payload and prompt encryption public key.
Paste the contents of dlp_public.pem into the dashboard field. Save.
Why: This is an asymmetric keypair. You hand Cloudflare only the public key, which it uses to encrypt any matched sensitive content before writing it to the logs. The private key never leaves the server, so only someone holding that file (a security analyst) can decrypt a match. That way the caught data (an SSN, an employee ID) is never stored in plaintext where a log reader could see it.
b. Create a custom detection entry
Navigate to Data loss prevention > Detection entries. Click Create a detection entry.
Field
Value
Name
AcmeCorp Employee ID
Type
Custom regex
Pattern
ACME-\d{6}
Save.
c. Create a DLP profile
Navigate to Data loss prevention > Profiles. Click Create profile.
Field
Value
Profile name
AcmeCorp Employee ID
Detection entries
Attach AcmeCorp Employee ID
Save.
d. Create a Gateway HTTP policy
Navigate to Traffic controls > Firewall policies > HTTP tab. Click Add a policy.
Verify: The submission is blocked. Insights & Logs > Logs > HTTP request logs shows the DLP match with profile AcmeCorp Employee ID.
17.4 Decrypt and inspect a DLP payload match in the logs
Why: Step 17.3 set up payload encryption so that matched DLP content is encrypted in the logs with your public key. This step closes the loop by finding a match in the logs and decrypting it with the private key. In production, this is how a security analyst reviews what data was actually caught without the matched content being stored in plaintext.
In the Zero Trust dashboard, click Insights & Logs > Logs > HTTP request logs.
Click + Add filter, set Field Policy, Operator equals, and pick Block-DLP-EmployeeID from the Value list. (Alternatively, filter Field Host equals dlptest.com and add a second condition Field Action equals Block. The Operator only offers equals / does not equal, so there is no substring match.)
Find the blocked request from Step 17.3 (the one where you typed ACME-789012).
Click into the log entry to expand its details.
Find the DLP match section. The matched content is shown as an encrypted payload (base64-encoded ciphertext).
Copy the encrypted payload string and paste it below:
Fill in here: encrypted DLP payload
On the Ubuntu Server (SSH session or console), decrypt the payload using the private key you generated in Step 17.3:
(Exact decryption command may vary depending on the encryption format Cloudflare uses. If pkeyutl fails, try openssl rsautl -decrypt -inkey dlp_private.pem.) 8. The decrypted output should contain the matched text (ACME-789012).
Verify: The decrypted payload reveals the original matched content. If the log entry does not show an encrypted payload (shows plaintext or shows nothing), verify that the public key was saved correctly in Step 17.3 sub-step 1 and that at least one new DLP match occurred after the key was configured.
17.5 Create a Gateway Authorization Proxy and PAC file
Why: Not every device can run the Cloudflare One Client. A proxy endpoint with a PAC file routes web traffic through Cloudflare Gateway for agentless devices.
Step 1. Create the proxy endpoint
Navigate to Traffic controls > Resolvers & Proxies > Proxy endpoints tab. Click Add a proxy endpoint. In the Create a new proxy endpoint dialog, leave Authorization selected and click Continue.
Field
Value
Name
Lab Proxy Endpoint
Save.
Step 2. Create and deploy the PAC file
Click Add a PAC file. Configure to route HTTP/HTTPS through the proxy. Copy the PAC file URL.
On the Windows desktop, open Settings > Network & internet > Proxy.
Under Automatic proxy setup, enable Use setup script. Paste the PAC URL. Save.
Step 3. Create and test a proxy-routed policy
Navigate to Traffic controls > Firewall policies > HTTP. Click Add a policy.
Verify: example.com is blocked. Insights & Logs > Logs > HTTP request logs shows the traffic came through the proxy endpoint.
Clean up: Disable the proxy in Windows Settings. Delete the Proxy-Block-Test policy.
17.6 Configure tenant control policies
Why: Tenant control injects custom HTTP headers so SaaS applications can identify your organization. This prevents personal account access on managed devices.
Navigate to Traffic controls > Firewall policies > HTTP > Add a policy. Configure:
Field
Value
Name
Tenant-Control-Header
Action
Allow
Traffic
Hostname matches lab.cfiq.io
Under Actions and settings > Add custom headers:
Header name
Header value
x-get-token
2KdBpMuDeBAn8GtDv, <team-name> (replace with your slug)
Block policy not actually a Block action, or page not attached
Open the policy editor; confirm Action is Block and the Modify Gateway block behavior is set to Custom block page
PAC file routing fails to direct traffic through Gateway
Windows didn't pick up the PAC URL change
Disable then re-enable Use setup script in Windows proxy settings; try in a fresh browser session
Tenant control endpoint lab.cfiq.io/zt/v1/seed returns no token
Custom header not actually injected; policy may not be matching
Confirm hostname matches lab.cfiq.io exactly in the policy; check HTTP request logs for the policy hit
DLP payload decryption fails (Step 17.4)
Wrong key file or encryption format mismatch
Confirm dlp_private.pem matches the public key uploaded in Step 17.3; try openssl rsautl -decrypt if pkeyutl fails
Lab 17 to Lab 18
Lab 18 installs Cloudflare Mesh on the Ubuntu Server so the server's own outbound traffic egresses through Cloudflare and the Gateway policies you spent Labs 14-17 building apply to it.
Lab 18 - Cloudflare Mesh on the Ubuntu Server
This labOne last on-ramp. A server can publish itself through a tunnel (you did that in Lab 6), but it can also enroll as a Cloudflare One device using Cloudflare Mesh, which pushes the server's own outbound traffic through Cloudflare and under the same Gateway policies you spent Labs 14 through 17 building. You install Mesh alongside the existing Lab-Tunnel, watch the Ubuntu Server show up in the dashboard as a Mesh device, and confirm your DNS and HTTP policies now apply to curl requests that originate from the server itself. That's how you protect infrastructure that makes its own outbound calls, like an app server talking to a SaaS API. Estimated time: 30-45 minutes.
Prerequisites:
Labs 1 through 17 complete
Lab-Tunnel still running on the Ubuntu Server
Cloudflare One Client connected on the Windows desktop
Gateway DNS and HTTP policies from Labs 14 and 15 enabled
18.1 Create a dedicated Device Profile for the Mesh connector
Why: Cloudflare Mesh enrolls as a special Cloudflare One Client device using a service-token-like identity: warp_connector@<team-name>.cloudflareaccess.com. A dedicated profile ensures it gets different settings from human users.
In the Zero Trust sidebar, click Team & Resources > Devices > Device profiles.
Click + Add profile. Configure the following:
Field
Value
Profile name
Mesh Connector Profile
Matching rule: Selector
Email
Matching rule: Operator
is
Matching rule: Value
warp_connector@<team-name>.cloudflareaccess.com
Auto connect
On (0 minutes)
Allow device to leave organization
Off
Save. Drag the profile to position #1 in the profile list.
Verify: The Mesh Connector Profile is at the top of the Device profiles list.
18.2 Install Cloudflare Mesh on the Ubuntu Server
Why: Mesh turns the Ubuntu Server into a Cloudflare One Client device in addition to being a tunnel endpoint. The server's own internet traffic will egress through Cloudflare, and Gateway policies will apply to it.
In the Zero Trust sidebar, click Networks > Tunnels & Mesh.
The wizard generates three install commands. Copy them.
SSH into the Ubuntu Server (cloudflare / #Savetheinternet).
Paste each install command one at a time. Wait for each to complete before pasting the next.
Verify the Mesh service is running:
warp-cli --accept-tos status
Output should show Connected.
Verify: In the Zero Trust dashboard, Team & Resources > Devices > Your devices shows the Ubuntu Server as a Mesh-type device with email warp_connector@<team-name>.cloudflareaccess.com.
18.3 Test that Gateway policies apply to the server's traffic
Why: With Mesh connected, the Ubuntu Server's internet traffic now egresses through Cloudflare. Gateway policies should apply.
From the Ubuntu Server SSH session, test internet access:
curl ifconfig.io
The returned IP should be a Cloudflare IP (the server's traffic egresses through Cloudflare). 2. Test that security policies block malicious destinations:
curl http://malware.testcategory.com
This should be blocked by your DNS or HTTP security-category policies from Lab 14.
Verify: The ifconfig.io response shows a Cloudflare IP. The malware.testcategory.com request is blocked. In Insights & Logs > Logs > HTTP request logs, the server's requests appear with the Mesh device as the source.
Lab 18 Wrap-up
By the end of Lab 18 you should have:
Milestone
☐
Cloudflare Mesh installed on the Ubuntu Server alongside the existing Lab-Tunnel
☐
Ubuntu Server visible in the dashboard as a Mesh-type device (warp_connector@<team-name>.cloudflareaccess.com)
☐
Gateway DNS and HTTP policies applying to the server's own internet traffic
Lab 18 Troubleshooting
Symptom
Likely cause
Fix
Mesh install fails
Permissions or service conflict
Check systemctl status warp-connector for errors. Both cloudflared and Mesh can coexist
curl ifconfig.io from the Ubuntu Server returns a non-Cloudflare IP
Mesh not connected
Run warp-cli --accept-tos status and verify Connected. Check the Mesh profile matches the connector email
curl http://malware.testcategory.com is NOT blocked from the Ubuntu Server
Gateway policies not evaluating Mesh traffic
Verify the Mesh device appears in Team & Resources > Devices > Your devices. Check DNS/HTTP policies from Labs 14-15 are enabled
Global Cloudflare One Client settings; production-shaped device profile (no leave-org, no auto-updates, auto-connect, split tunnel); enrollment policy with SAML and instant authentication.
Lab 3
Silent MSI deployment of the Cloudflare One Client; SAML-authenticated enrollment; mdm.xml seeded for multi_user and pre-login.
Lab 4
Firewall posture check; four DEX synthetic tests; three DEX notifications; warp-diag collected remotely and locally and parsed in the log viewer.
Lab 5
Internal DNS zone cf1pslabs.int with web and ssh records; view linked to Cloudflare One Client users; resolver policy active; profile lockdowns verified.
Lab 6
Cloudflare Tunnel (Lab-Tunnel) on the Ubuntu Server with private route 10.91.0.0/24; FQDN resolution via internal DNS; tunnel health alert; debug-level cloudflared logging.
Lab 7
DNS location for the Ubuntu Server pointed at the Gateway DoH endpoint via dnsproxy, so server-side DNS queries are filtered by Gateway without the Cloudflare One Client.
Lab 8
Reusable Allow Lab Users Access policy; self-hosted Web and SSH apps published through Lab-Tunnel with SAML-only authentication.
Lab 9
Browser-rendered RDP through rdp-tunnel; multi-user enrollment concept verified with UserA / UserB; pre-login service token wired into mdm.xml and the enrollment policy.
Lab 10
Intranet Access app published through Lab-Tunnel demonstrating multiple services behind one tunnel.
Lab 11
Ubuntu Server public IP locked down at the AWS Security Group; verified that the cloudflared tunnel remains the only inbound path.
Lab 12
App Launcher enabled; CF_Authorization JWT decoded and inspected on jwt.io; Policy Tester used to predict allow/block outcomes.
Lab 13
Gateway Network policy that lets Access evaluate private traffic; WARP Authentication Identity for silent SSO; three-policy posture stack with a Windows Firewall failure exercise; optional Infrastructure Access SSH with command logging.
Lab 14
Cloudflare-recommended DNS and HTTP policy stacks; Browser Isolation policy for dlptest.com with copy / paste / print / upload restrictions.
Lab 15
Untrusted-certificate handling tested against expired.badssl.com; TLS inspection bypass for cnn.com verified via certificate issuer; Network recommended policies; targeted SMTP (port 25) block.
Lab 16
Order-of-enforcement prediction exercise across DNS, Network, and HTTP; Financial Information DLP policy blocking sample SSN / CCN; mode-switch prediction exercise across DNS only, Traffic only, and default modes.
Lab 17
Custom Gateway block page; clientless Remote Browser Isolation; custom DLP detection (ACME-######) with payload encryption and offline decryption; Gateway Authorization Proxy with PAC file; tenant control header injection.
Lab 18
Cloudflare Mesh installed on the Ubuntu Server alongside the existing Lab-Tunnel so Gateway policies apply to the server's own internet traffic.
Day 5: Zero Trust Capstone Lab
Day 5 is the individual Zero Trust Capstone Lab: "AcmeCorp from the inside." Working on your own in your enrolled lab environment, you rebuild AcmeCorp's Zero Trust posture end to end, and an automated grader scores your work.
What you build (16 graded items): device posture and split tunnel; a billing app published through a Cloudflare Tunnel and gated by Access; service-token monitoring access; Access for Infrastructure SSH with short-lived certificates; a clientless contractor app; a browser-rendered infrastructure app; Gateway DNS, Network, and HTTP policies with a deny-all floor; Browser Isolation; TLS decryption with a Do-Not-Inspect bypass; a DLP policy; and a student-built Logpush accountability sink provisioned with Terraform.
The capstone is a self-contained guide on its own site. Open it and sign in with your email and the access code your instructor gives you:
Work through that guide start to finish. It carries the full checklist of graded items and the one-line command you run on your enrolled Windows box to submit your build for grading.