Máy Windows ở văn phòng truy cập từ xa qua Cloudflare Tunnel

Tóm tắt

Ghi chú thao tác trực tiếp (2026-08-22): dựng đường truy cập từ xa vào một máy Windows đặt tại văn phòng công ty, từ MacBook ở nhà, qua Cloudflare Tunnel + Access — cả SSH lẫn RDP trên cùng một tunnel. Không mở cổng nào trên firewall công ty, không dùng VPN. Đây là bản mở rộng của mô hình đã dùng cho ssh-vao-pi-qua-cloudflare-tunnel-va-access, khác ở ba điểm: origin là Windows chứ không phải Linux, tunnel được quản lý từ dashboard (remotely-managed) thay vì file config cục bộ, và có thêm ingress RDP bên cạnh SSH.

Điểm khác biệt về mặt vận hành so với homelab: máy này không có đường cứu hộ vật lý — chủ hệ thống không ở văn phòng, mọi thao tác tại chỗ đều phải nhờ người khác. Điều đó chi phối gần như mọi lựa chọn kỹ thuật bên dưới.

Thông số máy

MụcGiá trị
HostnameDESKTOP-NTJREDH
Hệ điều hànhWindows 10 Pro (đã hết hỗ trợ bảo mật từ 10/2025)
Loại máyDesktop
IP LAN văn phòng10.1.22.50 (mạng riêng, không cùng subnet với vtech:tam hay vtech:mik5009)
Tài khoản dùngadmin (local account, thuộc nhóm Administrators)
BitLockerKhông bật — reboot vào thẳng Windows, không có màn hình chờ chặn mạng
Tunneltantan-office-desktop (remotely-managed)
cloudflared2026.8.2, cài bằng MSI, chạy dưới dạng Windows service

Hai hostname công khai, cùng trỏ về tunnel trên:

  • chiissh.nguyenbinhson.comssh://localhost:22
  • chiirdp.nguyenbinhson.comrdp://localhost:3389

Cả hai đều có Cloudflare Access application dạng self-hosted, policy Allow theo email cá nhân (email OTP).

Điểm chính

Thứ tự thiết lập quan trọng hơn nội dung từng bước

Trình tự đã dùng: chuẩn bị máy Windows → tạo tunnel + published application route → tạo Access application → mới cài connector. Lý do: từ giây phút connector chạy, hostname đã sống trên Internet; nếu Access chưa có policy thì cổng RDP hở ra cho bất kỳ ai biết tên miền, chỉ còn mật khẩu Windows chắn lại. Đây đúng là loại cửa sổ hở đã gặp một lần trong topology-mang-homelab (WAN mới lên trước, interface list WAN thêm sau, SSH router hở mấy phút).

Phía Windows

  • RDP bật qua registry: fDenyTSConnections=0 tại HKLM:\System\CurrentControlSet\Control\Terminal Server, và bật NLA bằng UserAuthentication=1 tại ...\WinStations\RDP-Tcp.
  • OpenSSH Server là optional feature, không có sẵn: Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0, rồi đặt service sshd sang Automatic. Đổi shell mặc định từ cmd sang PowerShell bằng khoá DefaultShell trong HKLM:\SOFTWARE\OpenSSH.
  • Public key cho tài khoản Administrator nằm ở chỗ khác thường: sshd không đọc C:\Users\<user>\.ssh\authorized_keys nếu user thuộc nhóm Administrators — nó đọc file chung C:\ProgramData\ssh\administrators_authorized_keys, và file đó phải có ACL chỉ gồm Administrators:F + SYSTEM:F (icacls ... /inheritance:r). Sai ACL thì sshd từ chối im lặng và rơi về hỏi mật khẩu, không báo lỗi gì rõ ràng.
  • Tài khoản phải thật sự có mật khẩu. Account admin ban đầu ở trạng thái Password required: No; Windows có policy mặc định chặn account mật khẩu rỗng đăng nhập qua mạng, nên cả RDP lẫn SSH đều sẽ từ chối với thông báo mơ hồ. Sửa bằng net user admin *net user admin /passwordreq:yes.
  • Điện năng là phần quyết định máy có còn dùng được hay không: powercfg /change standby-timeout-ac 0, hibernate-timeout-ac 0, và powercfg /hibernate off (lệnh cuối tắt luôn Fast Startup). Máy desktop nên không phải xử lý hành vi gập nắp.
  • winget không có trên bản Windows 10 này (build cũ, thiếu App Installer) — cài cloudflared bằng MSI tải trực tiếp từ GitHub release, rồi msiexec /i ... /quiet. Cần đặt [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 trước khi Invoke-WebRequest.
  • MSI cài vào C:\Program Files (x86)\cloudflared\ và thêm vào PATH, nhưng cửa sổ PowerShell đang mở không thấy PATH mới — gọi bằng đường dẫn đầy đủ hoặc mở cửa sổ mới.
  • Đăng ký service bằng cloudflared.exe service install <TOKEN>. Token lấy từ dashboard, không cần đăng nhập tài khoản Cloudflare trên máy Windows — khác hẳn cách cloudflared tunnel login đã dùng trên Pi, và là lý do chọn kiểu remotely-managed: người thao tác hộ tại văn phòng chỉ cần chạy đúng một lệnh.

Phía Cloudflare

  • UI Zero Trust đã đổi tên: chỗ thêm public hostname giờ nằm ở tab Published application routes trong trang tunnel. Tab Hostname routesCIDR routes là cho private network qua WARP, không dùng ở đây.
  • Trường Type phải chọn đúng SSH / RDP từ dropdown; trường URL chỉ ghi localhost:22 / localhost:3389, không thêm tiền tố giao thức.
  • Cloudflare tự tạo CNAME proxied trỏ về <tunnel-id>.cfargotunnel.com.
  • Kiểm chứng Access thật sự chắn: curl vào https://<hostname>/ từ ngoài phải trả 302 về <team>.cloudflareaccess.com/cdn-cgi/access/login/... với auth_status: NONE.

Phía MacBook (opera)

  • SSH: alias cf:tantan:win trong ~/.ssh/config, dùng ProxyCommand /opt/homebrew/bin/cloudflared access ssh --hostname %h — cùng mẫu với cf:home:picf:vtech:tam.
  • RDP cần hai phần rời nhau:
    1. Mở đường hầm và giữ tiến trình chạy: cloudflared access rdp --hostname chiirdp.nguyenbinhson.com --url rdp://localhost:13389
    2. Dùng Windows App của Microsoft (cask Homebrew windows-app) trỏ tới localhost:13389.
  • Dùng port local 13389 chứ không phải 3389 vì cổng mặc định hay bị chiếm trên máy client.
  • Cảnh báo certificate không tin cậy khi kết nối là bình thường (Windows dùng self-signed cert cho RDP, lại đang nối qua localhost).
  • Tắt cửa sổ chạy cloudflared access rdp là mất kết nối ngay — đây là điểm gây bối rối nhất trong vận hành hàng ngày.

Đánh đổi giữa SSH và RDP

Cả hai được dựng song song trên cùng một tunnel vì chúng bù cho nhau, không thay thế nhau:

  • RDP cho toàn bộ desktop, cần cho phần mềm GUI. Nhưng nhạy với độ trễ, tốn băng thông, chỉ một session tương tác (RDP vào là khoá màn hình người đang ngồi trước máy, và ngược lại), và yêu cầu Windows Pro trở lên — Windows Home không làm RDP host được.
  • SSH nhẹ, chịu độ trễ tốt, hợp cho script và truyền file, và quan trọng nhất: là đường cứu hộ. RDP hỏng thì SSH vào chạy Restart-Service TermService cứu được; chiều ngược lại thì không.

Chi phí thêm ingress thứ hai gần như bằng 0 khi tunnel, DNS và Access policy đã dựng sẵn — nên không có lý do gì phải chọn một.

Ràng buộc vận hành

  • Không có đường cứu hộ ngoài người ở văn phòng. Mọi thay đổi có khả năng làm máy mất mạng hoặc không boot (BIOS, driver mạng, firewall, Windows Update lớn) chỉ nên làm khi có người ở đó. Vì lý do này, việc bật “Restore on AC Power Loss” trong BIOS đã được hoãn có chủ ý — nó đòi reboot vào BIOS, mà lúc dựng thì chưa có đường remote nào để cứu nếu hỏng.
  • Windows 10 đã hết hỗ trợ bảo mật. Máy không nhận bản vá nữa. Lớp Access đứng trước là thứ bù lại: kẻ tấn công không chạm được tới RDP nếu chưa qua xác thực email OTP.
  • Đây là máy đặt tại công ty. Dựng tunnel ra ngoài về bản chất là mở một đường bypass firewall của họ; kỹ thuật thì trong suốt với IT (chỉ thấy outbound 443 tới Cloudflare) nhưng nên xin phép.
  • Nếu firewall công ty chặn UDP outbound, QUIC (UDP 7844) không lên được — ép protocol: http2 để chạy trên TCP 443.
  • Sau khi tunnel chạy ổn, nên Disable-NetFirewallRule -DisplayGroup "Remote Desktop" để đóng RDP với LAN công ty. Không ảnh hưởng kết nối qua tunnel vì cloudflared nối tới localhost, mà traffic loopback thì Windows Firewall không lọc. Kết quả: đường vào duy nhất là qua Cloudflare Access.

Khái niệm

Nguồn liên quan

  • ssh-vao-pi-qua-cloudflare-tunnel-va-access — mô hình gốc mà ghi chú này mở rộng; so sánh Linux/systemd + local config với Windows service + remotely-managed tunnel.
  • topology-mang-homelab — bối cảnh homelab và bài học về cửa sổ hở khi dựng đường vào trước khi dựng lớp chặn.

Người liên quan

Câu hỏi mở

  • Tiến trình cloudflared access rdp hiện phải chạy tay trong Terminal mỗi phiên. Có nên gói thành LaunchAgent chạy nền trên opera — như đã làm cho tunnel Companion — hay việc chạy tay lại là một lớp an toàn có ích (không có đường hầm mở thường trực)?
  • Có nên nhờ bật “Restore on AC Power Loss” trong BIOS ở lần có người tại văn phòng tiếp theo không? Đổi lại là một lần reboot có rủi ro.
  • Máy chạy Windows 10 hết hỗ trợ — nâng lên Windows 11, hay chấp nhận và dựa hoàn toàn vào lớp Access?