# Cloudflare One client outside-tunnel firewall preflight, 2026-10-28. # Run on the affected Windows computer with WARP disconnected. No admin rights, # credentials or registration required. No results uploaded. Inspect before running. # Cloudflare source: https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/ $ErrorActionPreference = 'Continue' Write-Host 'Cloudflare One preflight (outside WARP tunnel)' -ForegroundColor Cyan Write-Host 'Run from the affected network. No Access/service credentials are used.' Write-Host 'A successful TCP probe DOES NOT prove warp-svc.exe is allowed by a process-scoped firewall.' function Show-Result($Name, $Result, $Detail) { $colour = if ($Result -eq 'REACHED') { 'Green' } elseif ($Result -eq 'FAILED') { 'Red' } else { 'Yellow' } Write-Host ('[{0}] {1}: {2}' -f $Result, $Name, $Detail) -ForegroundColor $colour } function Check-Dns($Name) { try { $addresses = @(Resolve-DnsName -Name $Name -Type A -ErrorAction Stop | Where-Object { $_.Type -eq 'A' } | Select-Object -ExpandProperty IPAddress -Unique) if ($addresses.Count -eq 0) { throw 'no A answers' } Show-Result $Name 'REACHED' ($addresses -join ', ') } catch { Show-Result $Name 'FAILED' $_.Exception.Message } } function Check-Https($Name, $Url) { try { $request = [System.Net.HttpWebRequest]::Create($Url) $request.Method = 'GET' $request.AllowAutoRedirect = $false $request.Timeout = 7000 $request.ReadWriteTimeout = 7000 # Direct path only: this preflight must not send system proxy credentials. $request.Proxy = $null try { $response = $request.GetResponse() } catch [System.Net.WebException] { if ($_.Exception.Response -eq $null) { throw } $response = $_.Exception.Response } $code = [int]$response.StatusCode $response.Close() Show-Result $Name 'REACHED' ('HTTP status {0}; direct path, no redirects followed or credentials sent' -f $code) } catch { Show-Result $Name 'FAILED' $_.Exception.Message } } # Force TCP to Cloudflare's documented IP while keeping the real HTTPS hostname # as both TLS SNI and Host header; do not disable certificate validation. function Check-PinnedHttps($Name, $Hostname, $Address) { $client = $null; $ssl = $null try { $client = New-Object System.Net.Sockets.TcpClient $task = $client.ConnectAsync($Address, 443) if (-not $task.Wait(7000)) { throw 'TCP 443 timeout' } $task.GetAwaiter().GetResult() $ssl = New-Object System.Net.Security.SslStream($client.GetStream(), $false) $task = $ssl.AuthenticateAsClientAsync($Hostname) if (-not $task.Wait(7000)) { throw 'TLS timeout' } $task.GetAwaiter().GetResult() if (-not $ssl.IsAuthenticated) { throw 'TLS handshake did not authenticate' } $raw = "GET / HTTP/1.1`r`nHost: $Hostname`r`nConnection: close`r`n`r`n" $bytes = [System.Text.Encoding]::ASCII.GetBytes($raw) $ssl.Write($bytes, 0, $bytes.Length) $ssl.ReadTimeout = 7000 $buffer = New-Object byte[] 512 $read = $ssl.Read($buffer, 0, $buffer.Length) if ($read -le 0) { throw 'no HTTPS response' } $line = ([System.Text.Encoding]::ASCII.GetString($buffer, 0, $read) -split "`r`n")[0] if ($line -notmatch '^HTTP/1.[01] [0-9]{3}' -and $line -notmatch '^HTTP/2') { throw 'unexpected HTTP response' } Show-Result $Name 'REACHED' ('{0} — {1}' -f $Address, $line) } catch { Show-Result $Name 'FAILED' ('{0} — {1}' -f $Address, $_.Exception.Message) } finally { if ($ssl) { $ssl.Dispose() }; if ($client) { $client.Dispose() } } } Write-Host "`n-- Kaplan Access enrollment and Cloudflare device API --" Check-Dns 'kaplaninternational.cloudflareaccess.com' Check-Https 'Kaplan Access /warp (unauthenticated)' 'https://kaplaninternational.cloudflareaccess.com/warp' # Version 2026.6+ and the older version use different SNI names. # The Client pins these IPs instead of relying on the DNS answers above. foreach ($hostname in @('api.devices.cloudflare.com','zero-trust-client.cloudflareclient.com','notifications.cloudflareclient.com')) { Check-Dns $hostname foreach ($address in @('162.159.137.105','162.159.138.105')) { Check-PinnedHttps $hostname $hostname $address } } Write-Host "`n-- Outside-tunnel reachability --" Check-Dns 'engage.cloudflareclient.com' Check-Https 'engage.cloudflareclient.com (browser-like path, not pinned)' 'https://engage.cloudflareclient.com/' Check-PinnedHttps 'engage.cloudflareclient.com (pinned ingress)' 'engage.cloudflareclient.com' '162.159.197.3' Write-Host "`n-- Captive portal destinations (HTTP 4xx or redirect may be normal) --" foreach ($name in @('cloudflareportal.com','cloudflareok.com','cloudflarecp.com','www.msftconnecttest.com','captive.apple.com','connectivitycheck.gstatic.com')) { Check-Dns $name # Captive portal probes are usually plain HTTP; do not substitute HTTPS. Check-Https $name ('http://' + $name + '/') } Write-Host "`n-- Optional time synchronization --" Check-Dns 'time.cloudflare.com' try { $udp = New-Object System.Net.Sockets.UdpClient $udp.Client.ReceiveTimeout = 4000 $packet = New-Object byte[] 48 $packet[0] = 0x1b $null = $udp.Send($packet, $packet.Length, 'time.cloudflare.com', 123) $peer = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Any, 0) $reply = $udp.Receive([ref]$peer) if ($reply.Length -lt 48) { throw 'NTP response shorter than 48 bytes' } Show-Result 'NTP UDP 123' 'REACHED' ('48+ byte reply from {0}' -f $peer.Address) } catch { Show-Result 'NTP UDP 123' 'FAILED' $_.Exception.Message } finally { if ($udp) { $udp.Dispose() } } Write-Host "`n-- Tunnel protocol --" Show-Result 'MASQUE (162.159.197.0/24 UDP 443)' 'UNVERIFIED' 'A website / plain UDP packet cannot prove MASQUE succeeds. Test using the client.' Show-Result 'WireGuard (162.159.193.0/24 UDP 2408)' 'UNVERIFIED' 'A website / plain UDP packet cannot prove WireGuard succeeds. Test using the client.' Show-Result 'Inside-tunnel connectivity and DNS' 'UNVERIFIED' 'Requires a connected Cloudflare One client.' Show-Result 'Service token and process-scoped firewall' 'UNVERIFIED' 'Requires client registration logs / firewall policy review.' Write-Host "`nNo diagnostics were uploaded. A FAILED result is not proof of a firewall rule: VPN, DNS, proxy, TLS inspection, and local policy can also cause failures."