Cloudflare Tunnel: expose server nội bộ ra internet không cần public IP hay port forwarding
Cloudflare Tunnel cho phép bạn expose bất kỳ service nội bộ nào - web app, API, NAS, SSH - ra internet qua một subdomain của bạn, hoàn toàn miễn phí và không cần public IP, không mở port trên router, không đụng đến firewall. Một daemon nhỏ (`cloudflared`) chạy trên máy nội bộ, tạo outbound connection đến Cloudflare - traffic từ ngoài vào đi theo chiều ngược lại qua tunnel đó. ---
Vấn đề
Kịch bản quen thuộc: bạn chạy một service trên server nội bộ - có thể là homelab Raspberry Pi, VPS ở mạng private, hay môi trường dev cần share URL với client để demo. Để expose ra ngoài, cách thông thường là:
- Mở port trên router (port forwarding)
- Có static public IP
- Setup nginx reverse proxy
- Xử lý SSL certificate
Mỗi bước đều có friction riêng. Và quan trọng hơn: mở port trực tiếp ra internet là rủi ro bảo mật - scanner bot quét port liên tục, nếu service có lỗ hổng là bị exploit ngay.
Cloudflare Tunnel giải quyết toàn bộ stack trên: không cần public IP, không mở port, SSL tự động, và có thêm lớp DDoS protection từ Cloudflare edge.
Giải thích: cơ chế hoạt động
Internet → Cloudflare Edge
→ Cloudflare Tunnel (qua kết nối outbound)
→ cloudflared daemon (chạy trên máy nội bộ)
→ Service nội bộ (localhost:3000, 192.168.x.x:8080, ...)
Điểm quan trọng: cloudflared chỉ tạo kết nối outbound. Máy nội bộ không cần nhận inbound connection nào từ ngoài vào. Đây là lý do tại sao Tunnel an toàn hơn port forwarding - firewall của bạn không cần có ngoại lệ nào.
Cloudflare sẽ gán cho tunnel một subdomain của domain bạn (ví dụ: app.yourdomain.com) và route traffic từ subdomain đó qua tunnel đến service nội bộ.
Yêu cầu
- Domain đã được thêm vào Cloudflare (Active)
- Máy nội bộ chạy Linux, Windows, macOS, hoặc Docker
- Service cần expose đang chạy (web server, API, v.v.)
- Tài khoản Cloudflare (free là đủ)
Cài đặt và cấu hình
Phương án 1: Docker (nhanh nhất, khuyến nghị)
Nếu bạn đã chạy Docker, đây là cách nhanh nhất. Vào Cloudflare Zero Trust dashboard → Networks → Tunnels → Create a tunnel, đặt tên tunnel, chọn Docker, copy token.
docker run -d --restart=always \
--name cloudflared \
cloudflare/cloudflared:latest \
tunnel --no-autoupdate run --token <YOUR_TUNNEL_TOKEN>
--restart=always đảm bảo cloudflared tự khởi động lại sau khi máy reboot.
Phương án 2: Linux (Ubuntu/Debian)
# Download và cài cloudflared
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
# Cài đặt như service với token từ dashboard
sudo cloudflared service install <YOUR_TUNNEL_TOKEN>
# Verify service đang chạy
sudo systemctl status cloudflared
Phương án 3: Windows
Download MSI installer từ GitHub releases, chạy installer, sau đó:
# Chạy với quyền Administrator
cloudflared.exe service install <YOUR_TUNNEL_TOKEN>
Phương án 4: macOS
brew install cloudflared
sudo cloudflared service install <YOUR_TUNNEL_TOKEN>
Cấu hình public hostname (expose service)
Sau khi tunnel đã chạy và connect, vào Zero Trust dashboard → Networks → Tunnels → [tên tunnel] → Configure → Public Hostname.
Thêm hostname:
| Field | Value |
|---|---|
| Subdomain | app |
| Domain | yourdomain.com |
| Service Type | HTTP |
| URL | localhost:3000 |
Cloudflare tự tạo DNS CNAME record cho app.yourdomain.com trỏ đến tunnel. SSL certificate tự động từ Cloudflare. Không cần cấu hình gì thêm.
Expose nhiều service qua một tunnel
Một tunnel có thể serve nhiều subdomain - không cần chạy nhiều tunnel. Đây là điểm mạnh so với giải pháp ngrok hay localtunnel.
Cách cấu hình qua file config.yml (locally-managed tunnel):
tunnel: <TUNNEL_UUID>
credentials-file: /root/.cloudflared/<TUNNEL_UUID>.json
ingress:
# Web app chính
- hostname: app.yourdomain.com
service: http://localhost:3000
# API backend
- hostname: api.yourdomain.com
service: http://localhost:8080
# NAS / Synology
- hostname: nas.yourdomain.com
service: http://192.168.1.100:5000
# Grafana monitoring
- hostname: monitoring.yourdomain.com
service: http://localhost:3001
# SSH qua browser (cần Access)
- hostname: ssh.yourdomain.com
service: ssh://localhost:22
# Catchall - bắt buộc phải có ở cuối
- service: http_status:404
Chạy với config file:
cloudflared tunnel --config /path/to/config.yml run
Protocol hỗ trợ
| Protocol | Support | Ghi chú |
|---|---|---|
| HTTP/HTTPS | ✓ | Đầy đủ, tự động SSL |
| SSH | ✓ | Browser-rendered SSH với Access |
| RDP | ✓ | Remote desktop |
| TCP | ✓ | Custom protocol |
| UDP | ✗ | Không hỗ trợ |
SSH qua browser: Cloudflare cho phép SSH qua browser không cần cài SSH client. Kết hợp với Zero Trust Access để xác thực trước - xem bài tiếp theo về Zero Trust.
Bảo mật: những gì KHÔNG nên expose trực tiếp
Cloudflare Tunnel không phải silver bullet. Một số service cần thêm lớp bảo vệ:
Không expose trực tiếp:
- Database (MySQL, PostgreSQL, Redis, MongoDB) - dùng qua ứng dụng, không expose port DB ra ngoài
- Admin panels (phpMyAdmin, Adminer) - cần Access authentication phía trước
- Internal dashboards - thêm Cloudflare Access để yêu cầu đăng nhập
Cách thêm Access authentication (cho service nhạy cảm):
Vào Zero Trust → Access → Applications → Add Application. Chọn Self-hosted, nhập domain/subdomain cần bảo vệ, tạo policy yêu cầu email hoặc Google OAuth trước khi truy cập. Cloudflare sẽ hiện màn hình login trước khi cho vào service.
Ví dụ thực tế: homelab setup
Đây là cách một setup homelab điển hình có thể trông như thế nào:
yourdomain.com (trên Cloudflare)
├── app.yourdomain.com → localhost:3000 (web app)
├── api.yourdomain.com → localhost:8080 (backend API)
├── nas.yourdomain.com → 192.168.1.50:5000 (Synology NAS) [+ Access]
├── monitoring.yourdomain.com → localhost:3001 (Grafana) [+ Access]
└── ssh.yourdomain.com → localhost:22 (SSH over browser) [+ Access]
Tất cả chạy qua một tunnel duy nhất. Không có port nào mở trên router. Không cần public IP.
Kiểm tra và debug
# Xem log cloudflared
sudo journalctl -u cloudflared -f
# Test tunnel trực tiếp
cloudflared tunnel info <TUNNEL_NAME>
# Kiểm tra ingress rule
cloudflared tunnel ingress validate
# Test routing
cloudflared tunnel ingress rule https://app.yourdomain.com
Nếu service không respond, kiểm tra:
- Service nội bộ đang thực sự chạy chưa (
curl http://localhost:3000) - Cloudflared daemon status (
systemctl status cloudflared) - DNS CNAME record đã được tạo chưa (Cloudflare dashboard → DNS)
- Tunnel status trên Zero Trust dashboard: phải là
HEALTHY
So sánh Cloudflare Tunnel vs các giải pháp khác
| Cloudflare Tunnel | ngrok | Port forwarding | |
|---|---|---|---|
| Public IP cần? | ✗ Không | ✗ Không | ✓ Cần |
| Giá | Free | Free (giới hạn) | Free |
| Custom domain | ✓ Domain riêng | Chỉ plan trả phí | ✓ |
| SSL | ✓ Tự động | ✓ | Manual |
| Nhiều service | ✓ Một tunnel | Mỗi service một URL | ✓ |
| Bảo mật | ✓ DDoS protection | Cơ bản | Phụ thuộc firewall |
| Persistent URL | ✓ | ✗ Free đổi mỗi session | ✓ |
Kết
Cloudflare Tunnel là giải pháp elegant cho bài toán expose service nội bộ: không cần public IP, không mở port, SSL tự động, nhiều service qua một tunnel, và có thể kết hợp với Cloudflare Access để bảo vệ service nhạy cảm.
Bài tiếp theo chúng tôi sẽ đi sâu vào Cloudflare Zero Trust - bức tranh toàn diện hơn: không chỉ expose service mà còn kiểm soát ai được truy cập vào service nào, từ thiết bị nào, dựa trên identity. Đây là hướng đi thay thế VPN truyền thống cho team nhỏ.
Tham khảo
- Cloudflare Tunnel - Official docs
- Getting Started with Cloudflare Tunnel
- Host local projects without static IP using Cloudflare Tunnel
- Expose your local server with Cloudflare Tunnel
- Cloudflare Tunnel: Expose Your Home Network
BKGlobal Tech Team