TL;DR
Cloudflare Tunnel (cloudflared) đưa ứng dụng nội bộ lên Cloudflare edge mà không mở inbound port. Daemon chạy trong hạ tầng của bạn, tạo kết nối QUIC chỉ-chiều-ra tới Cloudflare, chuyển tiếp traffic từ edge về origin.
Bài này đi qua:
- Vì sao chỉ-chiều-ra quan trọng hơn bạn nghĩ (bề mặt DDoS, gánh nặng firewall, NAT).
- Kiến trúc bên trong của
cloudflared: 4 kết nối QUIC, health check, lưới kết nối. - Ingress rules YAML: khớp hostname + path, catch-all.
- Replica HA: nhiều instance cùng tunnel ID, CF edge cân bằng tải.
- Protocol non-HTTP: SSH/RDP/SMB/Kafka qua Tunnel.
- Playbook 3 giai đoạn chuyển đổi từ VPN sang Tunnel.
- Truy nguyên 6 trường hợp phổ biến nhất.
Luận điểm chính:
Tunnel không phải “reverse proxy có token”. Nó là nền tảng kết nối của Zero Trust, loại bỏ public IP cho ứng dụng nội bộ, gỡ gánh nặng DDoS/WAF/cert khỏi nhóm vận hành, và cho phép HA đa khu vực không cần LB phía trước.
Bài này là Part 8 của Cloudflare One Handbook, chuyển sang tầng kết nối sau khi hoàn tất identity & access.
Dành cho ai
- Kỹ sư Platform/SRE đang chạy ứng dụng nội bộ sau firewall hoặc NAT.
- Kỹ sư bảo mật muốn giảm bề mặt tấn công: bỏ public IP cho dịch vụ nội bộ.
- Kỹ sư network đánh giá thay bastion SSH, reverse proxy, hoặc VPN cho trường hợp “đưa ứng dụng ra ngoài”.
Bạn nên đã đọc:
- Part 4, Access (Tunnel thường pair với Access cho ZTNA).
- Part 3, Mental model 4 tầng (Tunnel ở tầng 4 Resource).
Sau bài này bạn sẽ:
- Hiểu daemon
cloudflaredhoạt động thế nào, khi nào dùng. - Viết được ingress rules cho ứng dụng HTTP và non-HTTP.
- Thiết lập HA với ≥ 2 replica đúng cách.
- Có playbook chuyển đổi từ VPN.
- Truy nguyên được 6 trường hợp lỗi phổ biến.
Bài này không nói về gì
- Magic WAN / Magic Transit: tunnel ở tầng network cho kết nối chi nhánh, khác phạm vi (sẽ đề cập ở Part 10).
- WARP Connector (tunnel cho mạng phía client): Part 9 sẽ đi sâu WARP.
- Cloudflare Tunnel for RDP gateway (unified RDP): nhắc tới nhưng không đi sâu.
- Định tuyến riêng Warp-to-Warp: chủ đề nâng cao, cần Cloudflare One Enterprise.
Khái niệm
- Tunnel: đối tượng trừu tượng ở Cloudflare account, có ID + credentials. Một tunnel có thể có nhiều connector (replica).
- Connector: instance
cloudflareddaemon chạy trong hạ tầng. Một connector nhận một tunnel credential, tạo kết nối chiều ra tới CF. - Ingress rule: cấu hình định hướng request từ Cloudflare đến dịch vụ cụ thể bên trong.
- QUIC: transport dựa trên UDP mà
cloudflaredưa thích (giảm head-of-line blocking, tái kết nối nhanh hơn). Phương án dự phòng là HTTP/2 trên TCP nếu QUIC bị chặn. - Public hostname: DNS record proxied qua CF, trỏ tới tunnel (CNAME
app.example.com → <tunnel>.cfargotunnel.com). - Private network: định tuyến CIDR qua tunnel cho WARP client truy cập. Khác public hostname ở chỗ client phải dùng WARP, không phải browser.
Chỉ-chiều-ra: điểm nhấn chính
Vì sao đáng quan tâm
Khi tổ chức có 50 ứng dụng nội bộ, mỗi ứng dụng có public IP + firewall rule + WAF + cert + cân nhắc DDoS, gánh nặng vận hành ngầm rất lớn:
- Bề mặt tấn công: mỗi public IP là mục tiêu quét.
nmap1 đêm quét được toàn dải. Một ứng dụng có CVE chưa vá, kẻ tấn công tìm ra trong giờ. - DDoS: bạn tự chịu, hoặc thuê ngoài cho CDN (nhưng phải kéo traffic qua CDN rồi về origin, cấu hình phức tạp).
- WAF: bạn quản lý rule, truy nguyên false positive, cập nhật OWASP rule.
- Chứng chỉ: rotate hàng năm, tự động hóa mong manh.
- Firewall ACL: rule bùng nổ: whitelist IP văn phòng, dải VPN, IP đối tác, IP giám sát, v.v.
- NAT / chuyển tiếp cổng: cấu hình router/AWS SG mỗi khi thêm ứng dụng.
- Chuyển đổi dự phòng đa khu vực: cần LB toàn cầu (Route 53 / Cloudflare LB / AWS ALB cross-region).
Tunnel chỉ-chiều-ra giải quyết tất cả bằng cách đảo chiều: bạn không mở port, Cloudflare edge là nơi traffic tới, bạn chỉ cần daemon chiều ra.
Đánh đổi
Không phải bữa trưa miễn phí:
- Phụ thuộc Cloudflare edge: nếu CF edge down (hiếm, nhưng đã xảy ra), Tunnel không chạy. Public IP truyền thống vẫn chạy (nếu internet toàn cầu còn).
- Gánh nặng vận hành daemon: chạy
cloudflared, giám sát nó, cập nhật phiên bản. Tăng 1 phụ thuộc. - Truy nguyên phức tạp hơn: “tại sao ứng dụng không reach được” có thể ở ứng dụng, network, hoặc CF edge. Log ở 3 chỗ khác nhau.
Trong thực tế, đánh đổi có lợi ròng cho phần lớn enterprise.
Daemon cloudflared: bên trong
Phía edge
Khi tunnel được tạo, Cloudflare gán một tunnel ID (UUID) và sinh credentials. Edge có 3 thành phần logic:
- Tunnel terminator: nhận kết nối chiều ra từ daemon, duy trì pool QUIC/HTTP2.
- Tầng chính sách: kiểm tra Access + Gateway nếu áp dụng.
- Request dispatcher: định tuyến request tới connector khỏe mạnh (trong trường hợp HA).
Phía hạ tầng của bạn
cloudflared daemon:
- Đọc credentials (file JSON hoặc chuỗi token).
- Mở 4 kết nối chiều ra tới 4 edge PoP khác nhau (dự phòng có sẵn).
- Ưu tiên: QUIC (UDP 7844). Phương án dự phòng: HTTP/2 (TCP 443).
- Keepalive 25s: giữ kết nối ấm.
- Tự động tái kết nối nếu kết nối rớt (không cần thao tác từ vận hành).
- Đọc ingress rules từ cấu hình để định tuyến request chiều vào.
Yêu cầu port chiều ra
Rule firewall chiều ra cần cho phép:
443/TCPtới các dải IP Cloudflare (HTTP/2 dự phòng)7844/UDPtới các dải IP Cloudflare (QUIC ưu tiên)
Nếu firewall tổ chức chỉ cho 443/TCP, daemon tự chuyển sang phương án dự phòng, chậm hơn QUIC nhưng vẫn hoạt động.
Số kết nối
Mặc định 4 kết nối mỗi connector. Lý do:
- 4 PoP phân tán địa lý, nếu 1 PoP có vấn đề, 3 cái kia vẫn phục vụ.
- Song song hóa cho request đồng thời.
Có thể cấu hình tăng với flag --ha-connections nhưng hiếm khi cần.
Chế độ chạy
# Mode 1: systemd service (production)
sudo cloudflared service install <tunnel-token>
sudo systemctl status cloudflared
# Mode 2: foreground (dev/debug)
cloudflared tunnel run <tunnel-name>
# Mode 3: kubernetes deployment
# Cloudflare có Helm chart chính thức, dùng nhiều replicas
# Mode 4: Docker
docker run -d cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <token>
Production → systemd hoặc k8s. Test/dev → chạy foreground.
Ingress rules: định tuyến trong tunnel
Một tunnel có thể phục vụ nhiều hostname khác nhau về nhiều dịch vụ nội bộ khác nhau. Ingress rules cấu hình path → service.
Cấu hình cơ bản
# ~/.cloudflared/config.yaml
tunnel: a1b2-c3d4-abcd-1234
credentials-file: /etc/cloudflared/cred.json
ingress:
- hostname: gitlab.example.com
service: http://gitlab.internal:80
- hostname: api.example.com
path: /health
service: http://api-internal:8080
- hostname: ssh.example.com
service: ssh://bastion.internal:22
# catch-all BẮT BUỘC, luôn ở cuối
- service: http_status:404
Đánh giá rule
- Từ trên xuống, khớp đầu tiên thắng.
- Khớp theo hostname + path kết hợp.
- Catch-all bắt buộc: nếu không có, tunnel không khởi động được. Thường dùng
http_status:404để trả về 404 cho request không khớp.
Modifier khớp
ingress:
# Regex hostname
- hostname: "*.example.com"
service: http://multi-tenant:80
# Path + method match (không support method, chỉ path)
- hostname: api.example.com
path: "^/admin/.*"
service: http://admin-api:8080
# Different protocol
- hostname: db-admin.example.com
service: tcp://mysql.internal:3306
Tuỳ chọn theo rule
Mỗi rule có thể tuỳ chỉnh hành vi origin connection:
- hostname: slow-app.example.com
service: http://slow:80
originRequest:
connectTimeout: 30s
tcpKeepAlive: 30s
noHappyEyeballs: true
keepAliveConnections: 100
keepAliveTimeout: 90s
httpHostHeader: app.internal
originServerName: app.internal
tlsTimeout: 10s
noTLSVerify: false
Tùy chọn phổ biến:
httpHostHeader: ghi đè headerHostgửi tới origin (nếu ứng dụng mong hostname khác).noTLSVerify: true: anti-pattern nhưng đôi khi cần cho cert nội bộ tự ký. Nên sửa cert thay vì bật cái này.originServerName: tên SNI cho kết nối TLS.
Thiết lập DNS
Sau khi tunnel up + ingress có hostname: gitlab.example.com, cần DNS record:
CNAME gitlab.example.com → <tunnel-id>.cfargotunnel.com (proxied)
Cloudflare dashboard hoặc CLI:
cloudflared tunnel route dns <tunnel-name> gitlab.example.com
Lệnh này tự động tạo DNS record proxied.
Replica HA
Một tunnel với chỉ 1 connector = điểm lỗi đơn. Thực hành tốt: ≥ 2 replica ở ≥ 2 AZ khác nhau.
Cách hoạt động
- Cùng tunnel ID: tất cả replica chia sẻ tunnel credentials (cùng file JSON hoặc token).
- Xử lý độc lập: mỗi replica là instance cloudflared riêng, trên host/AZ riêng.
- CF edge tự cân bằng tải: request đến bất kỳ replica nào khỏe mạnh.
- Health check: CF edge probe replica mỗi vài giây. Replica không phản hồi sẽ bị đánh dấu không khỏe, traffic bỏ qua.
- Tự phục hồi: khi replica trở lại, CF thêm lại vào pool.
Thiết lập 2 replica
Trên host 1:
sudo cloudflared service install eyJhIjoi...<TOKEN>
Trên host 2 (AZ khác):
sudo cloudflared service install eyJhIjoi...<TOKEN> # cùng token
Cùng token = cùng tunnel = replica. CF dashboard hiện 2 “connector” bên dưới tunnel, cả hai đang chạy.
Xác nhận HA hoạt động
# Trên tunnel, check:
cloudflared tunnel info <tunnel-name>
# Output: list CONNECTORS với ID, created_at, client IP
# Nếu > 1 connector với trạng thái "Healthy" → HA đang chạy
Hoặc dashboard: Networks → Tunnels → [tunnel] → tab Connectors.
Replica Kubernetes
# Helm values
replicaCount: 3
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: topology.kubernetes.io/zone
labelSelector:
matchLabels:
app: cloudflared
Anti-affinity trải replica ra các AZ. Quan trọng cho HA thật, không phải “3 pod cùng một node”.
Hành vi chuyển đổi dự phòng
- Khởi động lại có kiểm soát (cập nhật phiên bản daemon): replica A restart, traffic chuyển sang B trong < 10s, quay lại khi A khỏe mạnh.
- Sập: vài giây để CF edge phát hiện không khỏe, chuyển traffic. Nếu request của người dùng đang bay gặp sự cố sập thì request thất bại, người dùng thử lại, định tuyến tới replica khác.
- Phân mảnh network: replica vẫn “up” ở góc nhìn cục bộ nhưng không reach CF, CF edge đánh dấu không khỏe, bỏ qua.
Đánh đổi chi phí
Mỗi replica = 1 chi phí host. Với 4-5 tunnel riêng mỗi tunnel 3 replica = 15 host. Chi phí đáng kể.
Tối ưu: chia sẻ tunnel giữa nhiều ứng dụng, một tunnel phục vụ nhiều ứng dụng qua ingress rules (xem phần trên). Một tunnel 3 replica có thể phục vụ 10 ứng dụng.
Protocol non-HTTP
Tunnel không chỉ cho HTTP. Nhiều protocol được hỗ trợ:
SSH
Ingress:
- hostname: ssh.example.com
service: ssh://bastion.internal:22
Client kết nối qua WARP (chế độ client) hoặc SSH render qua browser của Cloudflare.
Cloudflare dashboard → Access app type → Infrastructure → SSH.
RDP
- hostname: rdp.example.com
service: rdp://winhost.internal:3389
Tương tự SSH, Cloudflare có RDP render qua browser (RDP trên HTTPS qua browser).
TCP tổng quát
- hostname: db.example.com
service: tcp://postgres.internal:5432
Client kết nối qua WARP → chuyển tiếp cổng cục bộ → origin.
SMB (Windows file share)
- hostname: fileserver.example.com
service: smb://fileserver.internal:445
Kafka
- hostname: kafka.example.com
service: tcp://kafka.internal:9092
Client Kafka kết nối qua cầu WARP.
Giới hạn
- Protocol chỉ dùng UDP (DNS, IoT): hỗ trợ hạn chế.
- Protocol multiplex (gRPC streaming hai chiều): hoạt động nhưng cần kiểm thử kỹ timeout.
- Kerberos: hỗ trợ chưa đầy đủ.
Tunnel vs các giải pháp khác
Khi nào chọn gì
- Cloudflare Tunnel: ứng dụng nội bộ, ZTNA enterprise, đa khu vực, không muốn quản public IP.
- VPN: kết nối site-to-site, truy cập ở tầng network, tích hợp legacy yêu cầu ACL theo IP.
- Bastion / Jump host: hạ tầng chỉ SSH, nhóm nặng kỹ thuật vẫn muốn quy trình dựa trên shell.
- ngrok / localtunnel: phơi laptop dev, thử webhook, chia sẻ tạm. Không dùng cho production.
- Reverse proxy (nginx): website công khai, CDN phía trước, không phải ZTNA.
Tunnel không thay VPN cho mọi thứ
VPN vẫn phù hợp:
- Site-to-site giữa datacenter và cloud.
- Truy cập tầng network cho protocol legacy.
- Nhóm đã đầu tư vào hạ tầng VPN, đang hoạt động tốt.
Tunnel mạnh nhất cho người dùng-tới-ứng dụng (ZTNA), không phải site-to-site.
Chuyển đổi từ VPN: playbook
Giai đoạn 1: Đánh giá (1-2 tuần)
- Kiểm kê toàn bộ ứng dụng người dùng truy cập qua VPN.
- Phân loại: HTTP web app / SSH / RDP / DB / file share / legacy TCP.
- Xác định người phụ trách mỗi ứng dụng.
- Ưu tiên: ứng dụng nào bị tắc VPN nhiều nhất (độ trễ VPN đau nhất), chuyển đổi trước.
Giai đoạn 2: Thí điểm 5 ứng dụng (2-4 tuần)
- Chọn 5 ứng dụng HTTP đơn giản (ví dụ wiki nội bộ, Jira, dashboard).
- Thiết lập 1 tunnel với 2 replica.
- Cấu hình ingress rules cho 5 ứng dụng.
- Chính sách Access cho mỗi ứng dụng.
- Enroll WARP cho nhóm thí điểm (Part 9).
- Đường lùi: VPN vẫn chạy song song, người dùng dự phòng qua VPN nếu có vấn đề.
Giai đoạn 3: Mở rộng (2-6 tháng)
- Mỗi sprint: 5-10 ứng dụng chuyển đổi.
- Protocol non-HTTP (SSH, RDP) cần kiểm thử kỹ: render qua browser có trải nghiệm khác client native.
- Ứng dụng legacy có yêu cầu lạ (sticky session, header cụ thể) cần tinh chỉnh originRequest.
- Truy cập VPN giảm mỗi tháng, dần dần không còn dùng.
Giai đoạn 4: Gỡ VPN (1-2 tháng)
- Công bố ngày ngưng hỗ trợ với nhóm.
- Giám sát số session VPN, gần 0.
- Tắt VPN concentrator.
- Tiết kiệm chi phí: giấy phép, host, bảo trì.
Kinh nghiệm: giai đoạn 3 thường lâu hơn dự kiến vì ứng dụng legacy. Đừng cố ép chuyển những cái khó, tạm giữ VPN cho 5-10% ứng dụng legacy, tập trung 90% còn lại.
Truy nguyên: 6 trường hợp phổ biến
1. “cloudflared không connect được”
Check:
sudo journalctl -u cloudflared -n 50
Nguyên nhân phổ biến:
- Token sai: dán lại từ dashboard.
- Firewall chiều ra chặn 443/7844: đề nghị nhóm network cho phép.
- Phân giải DNS thất bại: kiểm thử
dig argotunnel.comtừ host. - Proxy công ty: đặt biến môi trường
HTTPS_PROXY.
2. “Tunnel up nhưng ứng dụng 502 Bad Gateway”
Chính sách pass, nhưng origin không phản hồi:
# From host running cloudflared:
curl -v http://gitlab.internal:80
# If fail → app down or network issue
# If ok → ingress config wrong (hostname/path/service mismatch)
3. “Replica A chạy, B không hiện trong dashboard”
Kiểm tra:
- B đang chạy:
sudo systemctl status cloudflaredtrên host B. - B cùng token/credentials: so sánh
/etc/cloudflared/cred.json. - B network:
curl https://cloudflare.comtừ B.
Thường gặp: B copy token sai, hoặc dùng token khác, tạo tunnel mới thay vì replica.
4. “HA đang chạy nhưng khi restart A, traffic đóng băng 30s”
Chu kỳ health check của CF edge mặc định ~10s. Khi A restart:
- Có kiểm soát (SIGTERM), A drain kết nối trước khi tắt, < 2s.
- Sập đột ngột, CF mất tới 10s để phát hiện.
Giảm nhẹ: restart luân phiên với thời điểm so le. Đừng restart tất cả replica cùng lúc.
5. “SSH qua Tunnel chậm hơn kết nối trực tiếp”
Tunnel có thêm vài chặng (WARP → CF edge → tunnel → origin). Độ trễ điển hình +20-50ms.
Nếu > 200ms, không phải lỗi Tunnel, kiểm tra:
- Tải CPU/network origin.
- Thương lượng cipher SSH (crypto chậm trên CPU cũ).
originRequest.connectTimeoutquá ngắn.
6. “noTLSVerify: true dùng như đường vòng cho cert tự ký”
Anti-pattern. Sửa:
- Thay cert tự ký bằng Let’s Encrypt hoặc CA công ty.
- Origin có CA cert, mount CA bundle vào cloudflared:
- hostname: app.example.com
service: https://app.internal:443
originRequest:
caPool: /etc/ssl/corporate-ca.pem
originServerName: app.internal
Đánh đổi
| Quyết định | Option A | Option B | Khuyến nghị |
|---|---|---|---|
| Mỗi app một tunnel vs chia sẻ | 1 tunnel mỗi app | 1 tunnel nhiều ingress | Chia sẻ, quản 5 tunnel dễ hơn 50. Chỉ tách khi compliance yêu cầu |
| Số replica | 1 | ≥ 2 | ≥ 2 cho production luôn. 3+ nếu đa khu vực |
| Cách triển khai | systemd trên VM | deployment k8s | k8s nếu đã có cluster. VM ổn cho legacy |
| Ưu tiên protocol | QUIC (7844 UDP) | HTTP/2 dự phòng | QUIC, chỉ dự phòng khi firewall chặn UDP |
| noTLSVerify | Bật cho nhanh | Sửa cert | Sửa cert, bật là nợ kỹ thuật |
Zero-config (cloudflared tunnel run quick) | Chế độ dev | Named tunnel | Named tunnel cho bất kỳ thứ gì tồn tại > 1 ngày |
Danh sách kiểm tra: trước khi production Tunnel
Thiết lập:
- Named tunnel (không dùng quick tunnel cho production).
- Credentials lưu an toàn (k8s secret, AWS SM, Vault).
- Cấu hình ingress có catch-all rule.
- DNS record proxied (orange cloud).
HA:
- ≥ 2 replica ở ≥ 2 host/AZ.
- Pod anti-affinity (nếu k8s).
- Kiểm thử khởi động lại có kiểm soát của 1 replica, traffic không gián đoạn.
- Giám sát trạng thái connector.
Bảo mật:
- Chính sách Access trên Application (Part 4).
- Ứng dụng origin không còn phơi public IP: kiểm tra firewall rule.
-
noTLSVerifytắt. -
originRequest.caPoolcấu hình nếu origin dùng CA công ty.
Vận hành:
- Cảnh báo khi connector không khỏe > 5 phút.
- Log
cloudflaredđẩy tới SIEM. - Runbook cho helpdesk khi người dùng báo “ứng dụng không vào được”.
- Quy trình cập nhật phiên bản cloudflared (hàng quý).
Bài học thực tế
- Chia sẻ tunnel rộng hơn bạn nghĩ. 1 tunnel có thể phục vụ 50+ ứng dụng qua ingress. Đừng tạo 50 tunnel: gánh nặng vận hành lớn, HA phức tạp.
- Pod anti-affinity là nghiêm trọng. Không có: 3 replica cùng 1 node, node chết là tunnel chết. Có: trải trên node/AZ, bền bỉ.
originRequest.caPoollà người hùng thầm lặng. Enterprise thường có CA công ty cho dịch vụ nội bộ. DùngcaPoolthay vìnoTLSVerify.- Chuyển đổi từ VPN không tuyến tính. Kỳ vọng 80% suôn sẻ, 20% ứng dụng legacy đau đầu (SAP, Citrix, Oracle). Dành thêm thời gian cho 20% đó.
- Chi phí replica vs khả năng chịu gián đoạn. Startup/không quan trọng: 2 replica. Production quan trọng: 3+ replica ở 3 AZ. Nặng compliance: thêm khu vực riêng.
Kết luận
Cloudflare Tunnel là xương sống của kiến trúc Zero Trust. Access kiểm soát ai vào (Part 4), Tunnel kiểm soát làm sao reach được ứng dụng mà không phơi public IP.
Mô hình chỉ-chiều-ra không phải “tính năng màu mè”, nó là thay đổi kiến trúc giảm bề mặt tấn công, gánh nặng vận hành, và cho phép các pattern (đa khu vực, HA, chính sách theo từng app) trước đây khó có.
Nếu phải nhớ một câu:
Tunnel không phải reverse proxy có token. Nó là nền tảng kết nối của Zero Trust, loại bỏ public IP cho ứng dụng nội bộ, gỡ gánh nặng vận hành cho nhóm hạ tầng, và cho phép HA đa khu vực mà không cần LB phía trước.
Trong Part 9 mình chuyển sang phía client: WARP client + luồng enroll thiết bị. Làm sao thiết bị người dùng gia nhập Cloudflare network, tín hiệu device posture, split tunnel, truy nguyên khi không kết nối được.
Tài liệu tham khảo
- Cloudflare Tunnel overview
- cloudflared configuration reference
- Ingress rules
- cloudflared GitHub
- Helm chart cloudflared
Trong series này:
- ← Part 7: SCIM và group sync
- Tiếp theo → Part 9: WARP client và device enrollment
- Xem toàn bộ: Series Cloudflare One Handbook