[Solution guide] Build customer portal zero trust access no vpn sg#30661
[Solution guide] Build customer portal zero trust access no vpn sg#30661marciocloudflare wants to merge 6 commits intoproductionfrom
Conversation
|
This pull request requires reviews from CODEOWNERS as it changes files that match the following patterns:
|
| @@ -0,0 +1,297 @@ | |||
| --- | |||
| pcx_content_type: solution-guide | |||
| title: Build a customer portal with Zero Trust access (Zero Trust Free plan) | |||
There was a problem hiding this comment.
| title: Build a customer portal with Zero Trust access (Zero Trust Free plan) | |
| title: Publish a customer portal with Zero Trust access (Zero Trust Free plan) |
|
|
||
| In this stage you install `cloudflared`, create a tunnel, and map a public hostname to your local service. | ||
|
|
||
| ### Prerequisites |
There was a problem hiding this comment.
suggest moving prereqs up a level and higher up on the page
|
|
||
| The fastest path uses the **Get Started** flow in the Cloudflare dashboard. It creates a tunnel, installs `cloudflared` on your host, and creates an Access application in one workflow. | ||
|
|
||
| 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Get Started**. |
There was a problem hiding this comment.
check /cloudflare-one/setup/secure-private-apps/private-web-app/ to see if this content can be turned into partials
| 9. Select **Continue**. | ||
|
|
||
| <Render | ||
| file="access/secure-private-apps-shared-steps" |
There was a problem hiding this comment.
there's some inconsistency in the example URL, i.e. we show grafana.example.com and dashboard.example.com in earlier steps and portal.example.com at the end.
| If you prefer to create the tunnel and Access application separately: | ||
|
|
||
| - For a hand-driven dashboard flow, refer to [Create a tunnel (dashboard)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) and then [Publish a self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/). | ||
| - For an automated, API-driven setup (useful for Terraform or CI), refer to [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/). |
There was a problem hiding this comment.
| - For an automated, API-driven setup (useful for Terraform or CI), refer to [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/). | |
| - For an automated, API-driven setup, refer to [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/) or [Create a tunnel (Terraform)](/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/terraform/). |
|
|
||
| A single tunnel can publish multiple applications by mapping different subdomains to different local services. For example, `portal.example.com` reaches `localhost:3000` and `admin.example.com` reaches `localhost:8080`. | ||
|
|
||
| To add a second published application to an existing tunnel, refer to [Published applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/). Each route maps one public hostname to one local service. Follow that page using the `customer-portal` tunnel you created in the previous step. |
There was a problem hiding this comment.
| To add a second published application to an existing tunnel, refer to [Published applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/). Each route maps one public hostname to one local service. Follow that page using the `customer-portal` tunnel you created in the previous step. | |
| To add a second published application to an existing tunnel, refer to [Add a published application route](/cloudflare-one/networks/routes/add-routes/#add-a-published-application-route). Each route maps one public hostname to one local service. Follow that page using the `customer-portal` tunnel you created in the previous step. |
|
|
||
| ## Add identity-based access control | ||
|
|
||
| To control which employees and clients can reach the portal, put [Cloudflare Access](/cloudflare-one/access-controls/) in front of it. Access checks every request against a policy before it reaches your origin, and the policy can require an identity provider login, a one-time PIN, a service token, or other context such as country or device posture. |
There was a problem hiding this comment.
Device posture requires having a managed corporate device (running the CF1 Client or with mTLS certs installed). If you're focusing on clientless access then I would leave this out.
| To control which employees and clients can reach the portal, put [Cloudflare Access](/cloudflare-one/access-controls/) in front of it. Access checks every request against a policy before it reaches your origin, and the policy can require an identity provider login, a one-time PIN, a service token, or other context such as country or device posture. | |
| To control which employees and clients can reach the portal, put [Cloudflare Access](/cloudflare-one/access-controls/) in front of it. Access checks every request against a policy before it reaches your origin, and the policy can require an identity provider login, a one-time PIN, a service token, or other context such as country. |
| - [Okta](/cloudflare-one/integrations/identity-providers/okta/) for Okta-based SSO. | ||
| - [Microsoft Entra ID](/cloudflare-one/integrations/identity-providers/entra-id/) for Microsoft 365 organizations. | ||
| - [GitHub](/cloudflare-one/integrations/identity-providers/github/) when most users already have GitHub accounts (useful for development teams). | ||
| - [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) for external clients who do not log in with your IdP. One-time PIN is enabled by default and requires no additional configuration. |
There was a problem hiding this comment.
| - [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) for external clients who do not log in with your IdP. One-time PIN is enabled by default and requires no additional configuration. | |
| - [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) for external clients who do not log in with your IdP. |
There was a problem hiding this comment.
I think this is turned off by default in a new account, unless the dash team has recently changed the behavior. The dash quick start flow may be turning it on for you.
|
|
||
| 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Access controls** > **Applications**. | ||
|
|
||
| 2. Find your customer portal application, select **Configure**, then go to the **Policies** tab. |
| - Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `acme.example.com`). | ||
| - On each application's policy, use an `Allow` action with an `Include` rule that references the client's rule group. | ||
|
|
||
| If your identity provider exposes group membership (for example, Google Workspace groups, Okta groups, or Microsoft Entra ID security groups), you can also map IdP groups to applications without maintaining email lists in Cloudflare. Refer to the IdP-specific page for the group claim configuration. |
There was a problem hiding this comment.
| If your identity provider exposes group membership (for example, Google Workspace groups, Okta groups, or Microsoft Entra ID security groups), you can also map IdP groups to applications without maintaining email lists in Cloudflare. Refer to the IdP-specific page for the group claim configuration. | |
| If your identity provider exposes group membership (for example, Google Workspace groups, Okta groups, or Microsoft Entra ID security groups), you can also map IdP groups to applications without maintaining email lists in Cloudflare. Refer to the [IdP-specific page](/cloudflare-one/integrations/identity-providers/) for the group claim configuration. |
|
|
||
| 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Access controls** > **Applications**. | ||
|
|
||
| 2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to the **Policies** tab. |
There was a problem hiding this comment.
| 2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to the **Policies** tab. | |
| 2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to **Access policies**. |
|
|
||
| 4. Configure the policy: | ||
|
|
||
| - **Action**: _Service Auth_ |
There was a problem hiding this comment.
do we want to use the same policy format shown in ?
| A typical pattern: | ||
|
|
||
| - Create one rule group per client containing the client contacts' email addresses. | ||
| - Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `acme.example.com`). |
There was a problem hiding this comment.
| - Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `acme.example.com`). | |
| - Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `client-acme.example.com`). |
|
Il gio 7 mag 2026, 19:11 ranbel ***@***.***> ha scritto:
… ***@***.**** commented on this pull request.
------------------------------
In
src/content/docs/use-cases/solutions/build-customer-portal-zero-trust-access-no-vpn.mdx
<#30661 (comment)>
:
> +
+After you create the token, copy both the Client ID and Client Secret. Cloudflare displays the Client Secret only once; if you lose it, you must generate a new token.
+
+### Add a Service Auth policy
+
+To let the service token reach a specific Access application:
+
+1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Access controls** > **Applications**.
+
+2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to the **Policies** tab.
+
+3. Select **Add a policy**.
+
+4. Configure the policy:
+
+ - **Action**: _Service Auth_
do we want to use the same policy format shown in ?
—
Reply to this email directly, view it on GitHub
<#30661 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/B4A4WYS2IF224NSGSLRPXWT4ZS7T7AVCNFSM6AAAAACYUFZLZGVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHM2DENBWGEZTMMRXGU>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
| Store both values in environment variables or a secrets manager. Do not commit them to source control. | ||
|
|
||
| :::note[Service token expiration] | ||
| Service tokens expire on the duration you selected when creating them. To extend a token before it expires or to rotate a compromised one, refer to the Service tokens documentation. |
There was a problem hiding this comment.
| Service tokens expire on the duration you selected when creating them. To extend a token before it expires or to rotate a compromised one, refer to the Service tokens documentation. | |
| Service tokens expire on the duration you selected when creating them. To extend a token before it expires or to rotate a compromised one, refer to the [Service tokens documentation](/cloudflare-one/access-controls/service-credentials/service-tokens/#renew-service-tokens). |
| Each log entry contains the user email, IP address, the application requested, the identity provider used, whether the attempt was allowed, and a Ray ID for tracing. | ||
|
|
||
| :::note[Per-request logs require Enterprise] | ||
| Authentication logs record every login. To audit individual HTTP requests made during an authenticated session, refer to the Per-request logs section in the Access authentication logs reference. |
There was a problem hiding this comment.
| Authentication logs record every login. To audit individual HTTP requests made during an authenticated session, refer to the Per-request logs section in the Access authentication logs reference. | |
| Authentication logs record every login. To audit individual HTTP requests made during an authenticated session, refer to the [Per-request logs section](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/#per-request-logs) in the Access authentication logs reference. |
|
|
||
| ### Run more than one tunnel replica | ||
|
|
||
| A single `cloudflared` instance is a single point of failure. To make the tunnel highly available, run `cloudflared` on more than one host. Cloudflare load-balances requests across all replicas of the same tunnel. |
There was a problem hiding this comment.
| A single `cloudflared` instance is a single point of failure. To make the tunnel highly available, run `cloudflared` on more than one host. Cloudflare load-balances requests across all replicas of the same tunnel. | |
| A single `cloudflared` instance is a single point of failure. To ensure that the application remains available if a `cloudflared` instance goes down, run `cloudflared` on more than one host. |
There was a problem hiding this comment.
To load balance traffic, users need to setup Cloudflare Load Balancing. Replicas only handle failover scenarios.
| Anyone with the tunnel token can run the tunnel. Store it in a secrets manager and rotate it if it is exposed. Refer to [Rotate a token without service disruption](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/#rotate-a-token-without-service-disruption) for the rotation procedure. | ||
| ::: | ||
|
|
||
| ### Require managed devices for sensitive applications (paid) |
There was a problem hiding this comment.
Cloudflare One Client and device posture are available on free plans.
| Anyone with the tunnel token can run the tunnel. Store it in a secrets manager and rotate it if it is exposed. Refer to [Rotate a token without service disruption](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/#rotate-a-token-without-service-disruption) for the rotation procedure. | ||
| ::: | ||
|
|
||
| ### Require managed devices for sensitive applications (paid) |
There was a problem hiding this comment.
| ### Require managed devices for sensitive applications (paid) | |
| ### Require managed devices for sensitive applications |
| - [Cloudflare Tunnel overview](/cloudflare-one/networks/connectors/cloudflare-tunnel/) — concept page covering outbound-only connections. | ||
| - [Tunnel with firewall](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) — IPs and hostnames to allowlist when egress is restricted. | ||
| - [Tunnel useful terms](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/) — glossary of tunnel and connector concepts. | ||
| - [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/) — automated tunnel creation for Terraform or CI. |
There was a problem hiding this comment.
|
|
||
| - [Cloudflare Tunnel overview](/cloudflare-one/networks/connectors/cloudflare-tunnel/) — concept page covering outbound-only connections. | ||
| - [Tunnel with firewall](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) — IPs and hostnames to allowlist when egress is restricted. | ||
| - [Tunnel useful terms](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/) — glossary of tunnel and connector concepts. |
There was a problem hiding this comment.
i'd replace the Tunnel useful terms link with Published applications: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/
| - [Access policies](/cloudflare-one/access-controls/policies/) — reference for actions, rule types, and selectors. | ||
| - [Common policies](/cloudflare-one/access-controls/policies/common-policies/) — example policies for typical scenarios. | ||
| - [Rule groups](/cloudflare-one/access-controls/policies/groups/) — reusable rule sets for per-tenant access. | ||
| - [Choose an application type](/cloudflare-one/access-controls/applications/choose-application-type/) — comparison of self-hosted, SaaS, infrastructure, and bookmark applications. |
There was a problem hiding this comment.
| - [Choose an application type](/cloudflare-one/access-controls/applications/choose-application-type/) — comparison of self-hosted, SaaS, infrastructure, and bookmark applications. |
(This guide is only using self-hosted apps.)
| **Identity providers** | ||
|
|
||
| - [Identity providers overview](/cloudflare-one/integrations/identity-providers/) — index of supported providers. | ||
| - [Google Workspace](/cloudflare-one/integrations/identity-providers/google-workspace/) — Google Workspace integration steps. |
There was a problem hiding this comment.
I'd add Microsoft Entra ID to this list, or leave out the IdP-specific links.
| **Logs and monitoring** | ||
|
|
||
| - [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/) — full reference for authentication log fields. | ||
| - [Tunnel notifications](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/notifications/) — tunnel health alert configuration. |
There was a problem hiding this comment.
I'd replace Tunnel notifications with info about getting Tunnel logs
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/
| By the end of this guide, you will have: | ||
|
|
||
| - An internal web application reachable through a public hostname on your domain (for example, `dashboard.example.com`). | ||
| - Access policies that allow employees through their corporate identity provider and named clients through one-time PIN. |
|
|
||
| import { DashButton, Render } from "~/components"; | ||
|
|
||
| When you need to give employees, clients, or contractors access to internal applications, you have to decide how users prove who they are, which applications each user can reach, and how to grant temporary access to automated systems. Common scenarios include exposing internal admin panels to staff, sharing client-specific dashboards with external customers, or letting CI/CD pipelines reach internal APIs. This guide replaces VPN-style network access with per-application access policies tied to user identity, using Cloudflare Tunnel to connect your application to Cloudflare and Cloudflare Access to enforce who can reach it. The core workflow runs on the [Zero Trust Free plan](https://www.cloudflare.com/plans/zero-trust-services/); some hardening features in the final stage require paid Zero Trust plans. |
There was a problem hiding this comment.
The hardening features mentioned below should all be available on Free. Some logging functionality requires Enterprise.
| - tunnel | ||
| - warp-client | ||
| sidebar: | ||
| label: Build a customer portal |
There was a problem hiding this comment.
| label: Build a customer portal | |
| label: Publish a customer portal |
Summary
Closes PCX-21162
Documentation checklist