☁Hướng dẫn Zero Trust
Tiếng Việt
Telegram Tài liệu Cloudflare ↗

🌐 Trang này chưa được dịch sang tiếng Việt — đang hiển thị nội dung tiếng Anh.

Module 4 — ZTNA: Publish a Private App with Access

Goal: Make an internal application reachable securely from anywhere — without a VPN and without opening any inbound firewall ports — and control exactly who can reach it.

👤 Who does this App owner + Security
⏱️ Time ~60 minutes
🎯 You'll finish with A private app published at a real URL, reachable only by the people you allow
✋ Before you begin Modules 1–3 done; a private web app to test (e.g. an internal wiki, Grafana, a dev tool) running somewhere you control; a server/VM that can reach that app

🧭 How this works: You'll run a tiny program called cloudflared next to your app. It makes a safe, outbound-only connection (a "Tunnel") to Cloudflare — so you never expose your app to the internet. Then Access puts a login check in front of it.

We'll go: (A) create a Tunnel → (B) connect your app → (C) add an Access application → (D) write the policy → (E) test → (F) build reusable groups.


Part A — Create a Tunnel

  1. 👉 Zero Trust → Networks → Tunnels.
  2. 👉 Click Create a tunnel.
  3. 👉 Choose Cloudflared → Next.
  4. ⌨️ Name it after the location, e.g. datacenter-1 → Save tunnel.

📺 What you'll see: An "Install and run a connector" page with commands for each operating system and a long install token already filled in.

  1. 👉 Choose the tab for your server's OS (Windows / macOS / Debian / Red Hat / Docker).
  2. 👉 Copy the command shown — it already contains your unique token.

⚠️ Watch out: That command contains a secret token. Treat it like a password; don't paste it into chat or tickets.


Part B — Connect your app

Step B1 — Run the connector on your server

  1. 👉 Log in to the server/VM that can reach your app.
  2. 👉 Paste and run the command you copied. For example, on Debian/Ubuntu it looks like:
    curl -L https://pkg.cloudflare.com/install.sh | sudo bash   # if cloudflared isn't installed
    sudo cloudflared service install eyJhIjoiZXhhbXBsZS...      # your token
    
  3. 📺 Back in the dashboard, the tunnel's Connector status changes to Connected / Healthy (give it ~30 seconds).

✅ Checkpoint: The dashboard shows your connector as Healthy. Click Next.

Step B2 — Tell Cloudflare where your app lives (public hostname)

You'll now map a public URL to your internal app.

  1. 📺 You're on the Route tunnel / Public Hostnames step.
  2. 👉 Click Add a public hostname and fill in:
    Field Example Meaning
    Subdomain wiki The name users will type
    Domain yourcompany.com A domain in your Cloudflare account
    Type HTTP How cloudflared reaches your app locally
    URL localhost:3000 Where your app runs on that server
  3. 👉 Click Save tunnel.

📺 What you'll see: wiki.yourcompany.com is now published and proxied to your internal app.

💡 No domain in Cloudflare yet? You'll need to add one (a zone) to use self-hosted Access apps with your own hostname. Ask your admin to add the domain, or use a domain you already manage in Cloudflare.

✅ Checkpoint: Visit https://wiki.yourcompany.com — you reach your app (right now it's open to anyone; the next part locks it down).


Part C — Add an Access application

This puts the login check in front of wiki.yourcompany.com.

  1. 👉 Zero Trust → Access controls → Applications.
  2. 👉 Click Create new application (Add an application).
  3. 👉 Choose Self-hosted and private.

📺 What you'll see: An application configuration page.

  1. ⌨️ Application name: Internal Wiki.
  2. 👉 Click Add public hostname and enter the same hostname you published: subdomain wiki, domain yourcompany.com.
  3. 👉 Set Session Duration to 24h (use a shorter value like 1h for sensitive apps).
  4. 👉 Under authentication, select the identity provider from Module 2.
    • 💡 If you only have one IdP, turn on Apply instant authentication so users skip the chooser screen.
    • 💡 Turn on Authenticate with Cloudflare One Client to let already-signed-in WARP users in seamlessly.
  5. Don't click Create yet — first add a policy in Part D (the wizard lets you add it inline), or click Create and add the policy right after. Either works.

Part D — Write the access policy (who's allowed)

  1. 👉 In the application, go to the Policies section → Add a policy (or Create new policy).

  2. ⌨️ Policy name: Allow — Employees on healthy devices.

  3. 👉 Action: Allow.

  4. 👉 Build the rules:

    Rule type Selector Operator Value
    Include Emails ending in — @yourcompany.com
    Require Device Posture in Disk encrypted (from Module 3)

    (If your IdP groups tested correctly in Module 2, use Include → IdP Groups → Engineering instead of the email domain for tighter control.)

  5. 👉 Click Save the policy, then Save/Create the application.

💡 Best practice — add a default-deny net: create a second, lower-priority policy named Block — Everyone with Action = Block and Include = Everyone. Because policies are read top-down, the Allow matches your people first and everyone else hits the Block.

⚠️ Watch out: Never use the Bypass action on a sensitive app — it removes the login check entirely.


Part E — Test it (the satisfying part)

Test 1 — an allowed user

  1. 👉 On your pilot device (signed in as an allowed employee), open https://wiki.yourcompany.com.
  2. 📺 You're sent to your company login (or straight in, if instant auth + WARP session). After login, your app loads. ✅

Test 2 — a blocked user

  1. 👉 Open the same URL in a private/incognito window and sign in as someone not allowed (or use a personal email).
  2. 📺 You see the Cloudflare "You don't have access" block page. ✅

Test 3 — check the logs

  1. 👉 Zero Trust → Logs → Access (or Access → Logs).
  2. 📺 You see both attempts: one Allowed, one Blocked, each with the user's email and the policy that decided it.

✅ Checkpoint: The right people get in, the wrong people are blocked, and you can see it in the logs. You've just replaced VPN for this app. 🎉


Part F — Build reusable groups (so you don't repeat yourself)

Right now your rules are typed into one app. When you add a 2nd, 3rd, 10th app, you don't want to re-type them. Build a reusable Access Group once.

  1. 👉 Zero Trust → Access controls → Policies → Groups (or Reusable components → Groups).
  2. 👉 Click Add a group.
  3. ⌨️ Name: Secure employees.
  4. 👉 Add rules once, e.g.:
    • Include → Emails ending in → @yourcompany.com
    • Require → Device Posture → Disk encrypted
  5. 👉 Click Save.

Now in any application's policy, you can simply select Include → Access Groups → Secure employees. Change the group once, and every app updates.

💡 Tip: Also create reusable Lists (Zero Trust → Reusable components → Lists) for things like an Offboarding email list or approved device serial numbers — then Exclude → Emails in list → Offboarding across all apps.


✅ Module 4 complete!

You now have:

  • ✅ A private app published via Tunnel (no inbound ports opened)
  • ✅ An Access policy allowing the right people, blocking others
  • ✅ A working allow/block test, visible in logs
  • ✅ A reusable group to apply to future apps

Want more app types?

  • SaaS apps (Salesforce, etc.): Applications → Create new application → SaaS → integrate via SAML/OIDC.
  • SSH / RDP servers: publish through the same Tunnel and use Access for Infrastructure (optionally browser-rendered, no client needed).
  • Unmanaged-device fallback: keep the Allow for healthy devices, and add a Gateway → Isolate rule (Module 5) so risky devices open the app in a safe remote browser instead of being blocked.

Quick troubleshooting

Problem Fix
App URL shows a Cloudflare error, not your app Tunnel connector not healthy, or the local URL/port is wrong — recheck Part B
Everyone is blocked, including you Your Include rule is too narrow or a Block policy is above the Allow — fix order (Part D)
Group rule never matches Groups didn't come through in Module 2's Test — fix the IdP first
Allowed but app keeps asking to log in Session duration too short, or cookies blocked — raise session duration
"DNS record already exists" A record for that hostname exists; delete it or pick another subdomain

🔌 Want the full picture on connectors? This module used one Cloudflare Tunnel for a single app. To connect whole subnets, do site-to-site / device mesh, or bring an entire office online, see Module 4b — Connectors: Tunnel, Mesh & Appliance.

👉 Next: Module 4b — Connectors · or Module 5 — Gateway

Module 4b goes deep on Tunnel, Mesh, and the Cloudflare One Appliance. Or skip to Gateway to start filtering traffic.