วิธีตั้งค่า Cloudflare Tunnel บน VPS โดยไม่ต้องเปิดพอร์ต
เรียนรู้วิธีติดตั้ง cloudflared บน VPS เพื่อปิดพอร์ต 80 และ 443 อย่างถาวร พร้อมตั้งค่า systemd service และ ingress rules เพื่อให้แอปพลิเคชันรับทราฟฟิกผ่าน localhost เท่านั้น
หน้าที่ของ Cloudflare Tunnel และความหมายที่แท้จริงของการไม่เปิดพอร์ต
Cloudflare Tunnel จะติดตั้ง daemon ขนาดเล็กที่เรียกว่า cloudflared ไว้บน VPS ของคุณ daemon ตัวนี้จะเปิดการเชื่อมต่อขาออกไปยัง Cloudflare และคงสถานะการเชื่อมต่อไว้ เมื่อมีคำขอเข้ามายัง hostname ของคุณ คำขอเหล่านั้นจะมาถึงที่ edge ของ Cloudflare และถูกส่งย้อนกลับลงมาผ่านการเชื่อมต่อที่เปิดไว้นี้ ดังนั้นจึงไม่มีการเชื่อมต่อใดที่ต้องวิ่ง เข้ามา ที่เซิร์ฟเวอร์ของคุณโดยตรง
ขั้นตอนที่คู่มือส่วนใหญ่ไม่ได้กล่าวถึงคือ การติดตั้ง tunnel ไม่ได้ช่วยปิดช่องทางใดๆ หากพอร์ต 80 และ 443 ยังคงเปิดอยู่ใน firewall ของคุณ และแอปพลิเคชันของคุณยังคงฟังคำสั่งอยู่ที่ 0.0.0.0 เท่ากับว่าคุณได้เพิ่มช่องทางเข้าที่สองขึ้นมาแทนที่จะเป็นการแทนที่ช่องทางเดิม IP ต้นทางของคุณยังคงเข้าถึงได้ และใครก็ตามที่ค้นพบ IP นั้นจะสามารถเข้าถึงเซิร์ฟเวอร์ของคุณได้โดยตรงโดยไม่ต้องผ่าน Cloudflare การปิดพอร์ตเหล่านั้นเป็นขั้นตอนที่คุณต้องทำด้วยตนเอง และเป็นขั้นตอนที่ทำให้สิ่งที่ทำมาก่อนหน้านี้ทั้งหมดมีความหมาย
cloudflared จำเป็นต้องมีการเข้าถึงขาออกไปยัง region1.v2.argotunnel.com และ region2.v2.argotunnel.com ที่พอร์ต 7844 โดยใช้โปรโตคอล UDP สำหรับ QUIC และจะเปลี่ยนไปใช้ TCP สำหรับ HTTP/2 ในกรณีที่จำเป็น หากเครือข่ายของคุณมีการกรองขาออก (egress filtering) ให้เปิดอนุญาตทั้งสองโปรโตคอล หรือบังคับให้ใช้เส้นทาง TCP ด้วย --protocol http2
ก่อนเริ่มต้น
- โดเมนที่อยู่ในบัญชี Cloudflare แล้ว โดยใช้ nameservers ของ Cloudflare ในการจัดการโซนนั้น
cloudflared tunnel route dnsจะเขียนระเบียนลงในโซนดังกล่าว ดังนั้นโซนจะต้องมีอยู่ก่อนแล้ว - การเข้าถึงขาออก (outbound access) บนพอร์ต 7844 จาก VPS ทั้งโปรโตคอล UDP และ TCP
- แอปพลิเคชันที่ทำงานและรอรับการเชื่อมต่อในเครื่อง (listening locally) อยู่แล้ว แม้จะเป็นเพียง
python3 -m http.server 8080สำหรับการทดสอบครั้งแรกก็ตาม sudoบนเครื่อง และเปิดเซสชัน SSH สำรองไว้อีกหนึ่งเซสชันก่อนที่คุณจะปรับแต่งไฟร์วอลล์
การติดตั้ง cloudflared บน Ubuntu หรือ Debian
Cloudflare แนบแพ็กเกจ .deb มากับทุก cloudflared release ดังนั้นการติดตั้งจึงทำได้ด้วยการดาวน์โหลดหนึ่งครั้งและเรียกใช้ dpkg หนึ่งครั้ง
curl -fsSL -o /tmp/cloudflared.deb \
https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i /tmp/cloudflared.deb
cloudflared --versionให้รัน dpkg --print-architecture ก่อนหากคุณไม่แน่ใจว่าเครื่องที่ใช้อยู่เป็นสถาปัตยกรรมใด สำหรับ 64-bit ARM ชื่อไฟล์จะลงท้ายด้วย arm64 แทนที่จะเป็น amd64 และไม่มีส่วนอื่นเปลี่ยนแปลง การที่ cloudflared --version แสดงสตริงเวอร์ชันออกมาถือเป็นการยืนยันเพียงพอที่คุณต้องทำก่อนดำเนินการต่อ
แพ็กเกจที่ติดตั้งด้วยวิธีนี้จะอยู่นอกเส้นทางการอัปเดตของ apt ดังนั้น apt-get upgrade จะไม่มีวันอัปเดตตัวมันให้ และการอัปเดตจะกลายเป็นหน้าที่ของคุณ sudo cloudflared update จะดึง release ล่าสุดมาและแทนที่ไฟล์ binary เดิมในตำแหน่งเดิม เมื่อ service ถูกสร้างขึ้นแล้ว ให้ตามด้วย sudo systemctl restart cloudflared เพื่อให้ process ที่กำลังทำงานอยู่เป็น binary เวอร์ชันใหม่ ควรจัดตารางการอัปเดตนี้ไว้พร้อมกับงาน patching ส่วนอื่นๆ ของคุณ เนื่องจาก tunnel daemon เป็นซอฟต์แวร์ที่เชื่อมต่อกับอินเทอร์เน็ต แม้ว่าจะไม่ได้เปิดพอร์ตใดๆ ก็ตาม
เข้าสู่ระบบและสร้าง tunnel แบบระบุชื่อ
cloudflared tunnel loginบน VPS ที่ไม่มีหน้าจอ (headless) จะไม่มีเบราว์เซอร์เปิดขึ้นมา ให้คัดลอก URL ที่แสดงบนหน้าจอไปเปิดในเบราว์เซอร์บนแล็ปท็อปของคุณแล้วเลือก zone เมื่อดำเนินการเสร็จสิ้น ไฟล์ ~/.cloudflared/cert.pem จะถูกสร้างขึ้น
cert.pem คือข้อมูลรับรองสำหรับ บัญชี ของคุณ ซึ่งใช้ในการอนุญาตให้สร้าง tunnel, เขียน DNS record ลงใน zone นั้น และลบ tunnel ได้ แต่ตัว tunnel ที่กำลังทำงานอยู่จะไม่ใช้ไฟล์นี้ ให้เก็บรักษาไว้เหมือนรหัสผ่าน เพราะหากผู้อื่นได้ไฟล์นี้ไป พวกเขาสามารถประกาศชื่อโฮสต์ใหม่บนโดเมนของคุณได้
cloudflared tunnel create homelabหากการทำงานสำเร็จ ระบบจะแสดงทั้งสองบรรทัดด้านล่างนี้ โดย UUID ที่ปรากฏคือค่าที่คุณต้องนำไปวางในไฟล์ config
Tunnel credentials written to /home/you/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
Created tunnel homelab with id 6ff42ae2-765d-4adf-8112-31c55c1551efไฟล์ JSON ดังกล่าวคือตัวตน (identity) ของ tunnel และเป็นข้อมูลรับรองเพียงอย่างเดียวที่ service ที่กำลังทำงานอยู่ต้องการ ใครก็ตามที่ถือไฟล์นี้สามารถลงทะเบียนเป็น tunnel ของคุณและรับ traffic ของคุณได้ ไม่มีวิธีหมุนเวียน (rotate) ข้อมูลนี้โดยเฉพาะ การเพิกถอนสิทธิ์ทำได้โดยการใช้ cloudflared tunnel delete homelab และสร้าง tunnel ใหม่เท่านั้น
เก็บไฟล์ข้อมูลรับรองไว้ในตำแหน่งที่เหมาะสม
เนื่องจาก service ทำงานด้วยสิทธิ์ root คุณควรจัดเก็บไฟล์ไว้ในไดเรกทอรีที่เป็นของ root แทนการวางไว้ในโฮมไดเรกทอรี ซึ่งอาจถูกเข้าถึงได้โดยงานสำรองข้อมูล (backup job) หรือบัญชีผู้ใช้ที่มีการใช้งานร่วมกัน
sudo install -d -m 700 -o root -g root /etc/cloudflared
sudo install -m 600 -o root -g root \
~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json \
/etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
rm ~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
sudo ls -l /etc/cloudflared
cloudflared tunnel listls -l ควรแสดงผล -rw------- root root บนไฟล์ JSON โดย cloudflared tunnel list จะอ่านค่าจาก cert.pem ทำให้การทำงานดำเนินต่อไปได้ตามปกติ และควรแสดงชื่อ tunnel, UUID รวมถึงจำนวนการเชื่อมต่อที่ใช้งานอยู่ในขณะนั้น
เขียน config.yml ด้วยกฎ ingress จริง
ให้เขียนไฟล์ config ไว้ที่ /etc/cloudflared/config.yml ไม่ใช่ใน home directory ของคุณ เหตุผลคือ cloudflared service install จะคัดลอกไฟล์ config ใดก็ตามที่พบไปยัง /etc/cloudflared/config.yml แล้วทำการ hard-code --config /etc/cloudflared/config.yml ลงใน systemd unit หากคุณสร้างไฟล์ไว้ใน ~/.cloudflared/config.yml ไฟล์นั้นจะเป็นเพียง snapshot ครั้งเดียว การแก้ไขไฟล์ใน home directory ในภายหลังจะไม่มีผลใดๆ บริการจะยังคงใช้กฎเดิมโดยไม่มีการแจ้งเตือน การเขียนไฟล์ไว้ในตำแหน่งที่ใช้งานจริงจะช่วยขจัดปัญหานี้ได้ทั้งหมด
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
loglevel: info
ingress:
- hostname: app.example.com
service: http://127.0.0.1:8080
- hostname: files.example.com
service: http://127.0.0.1:8081
- hostname: grafana.example.com
path: ^/api/
service: http://127.0.0.1:3000
- service: http_status:404กฎจะถูกอ่านจากบนลงล่างและกฎแรกที่ตรงกันจะมีผล กฎที่ไม่มี hostname จะตรงกับทุก hostname ดังนั้นกฎ catch-all จึงต้องอยู่บรรทัดสุดท้าย หากละทิ้งไป config จะถูกปฏิเสธด้วย The last ingress rule must match all URLs (i.e. it should not have a hostname or path filter) ส่วน http_status:404 เป็นบริการในตัวที่ตอบกลับด้วย 404 และไม่ทำอย่างอื่น ซึ่งมีความสำคัญเพราะหากไม่มีกฎนี้ คำขอสำหรับ hostname ที่คุณไม่ได้ตั้งใจจะเผยแพร่จะหลุดไปที่กฎจริงกฎสุดท้ายที่เหลืออยู่
ให้ใช้ 127.0.0.1 ใน URL ของ service: แทน localhost บน Ubuntu นั้น localhost จะ resolve ไปที่ ::1 ก่อน และแอปที่ผูกไว้กับ IPv4 loopback เท่านั้นจะปฏิเสธการเชื่อมต่อนั้น บรรทัด log จะแสดง dial tcp [::1]:8080: connect: connection refused และผู้เข้าชมจะเห็นข้อผิดพลาด 502
การใช้ http:// แบบปกติถือว่าถูกต้องในกรณีนี้เนื่องจากการเชื่อมต่อไม่เคยออกไปนอกเครื่อง ให้ใช้ https:// เฉพาะเมื่อแอปในเครื่องบังคับให้ใช้ TLS (transport layer security) เท่านั้น และให้คาดการณ์ว่าจะพบ x509: certificate is valid for example.com, not localhost เมื่อ certificate ของแอปไม่ตรงกับชื่อที่คุณเรียกใช้งาน ให้แก้ไขด้วย originServerName ภายใต้ originRequest หรือยอมรับความเสี่ยงด้วย noTLSVerify: true
ตรวจสอบกฎก่อนเริ่มการทำงานใดๆ:
sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress validate
sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress rule https://app.example.com/loginingress validate จะรายงานว่า config ถูกต้องหรือไม่ หรือระบุกฎที่ทำให้เกิดข้อผิดพลาด ingress rule จะรับ URL หนึ่งรายการและพิมพ์กฎแรกที่ตรงกับ URL นั้น ซึ่งเป็นวิธีที่เร็วที่สุดในการตรวจสอบว่า regex ของ path ไม่ตรงกับสิ่งที่คุณคาดการณ์ไว้
ชี้ DNS ไปที่ tunnel
cloudflared tunnel route dns homelab app.example.com
cloudflared tunnel route dns homelab files.example.com
cloudflared tunnel route dns homelab grafana.example.comทุกการเรียกใช้คำสั่งจะเขียน CNAME record แบบ proxied โดยชี้ไปยัง 6ff42ae2-765d-4adf-8112-31c55c1551ef.cfargotunnel.com เป้าหมายดังกล่าวจะ resolve ได้เฉพาะภายในเครือข่ายของ Cloudflare เท่านั้น ดังนั้นคำตอบของ DNS สาธารณะสำหรับ hostname ของคุณจะเป็นที่อยู่ของ Cloudflare และ IP ของ VPS จะไม่ปรากฏอยู่ในนั้น ทั้งนี้ wildcard hostname ใน config.yml ยังคงต้องการ DNS record ที่ตรงกันสำหรับทุกชื่อที่คุณใช้งานจริง
เมื่อมี record นั้นอยู่แล้ว คำสั่งจะล้มเหลวในลักษณะนี้:
Failed to add route: code: 1003, reason: Failed to create record app.example.com with err An A, AAAA, or CNAME record with that host already exists.record ที่มีอยู่เดิมนั้นมักจะเป็น A record แบบเก่าที่ชี้ไปยัง IP สาธารณะของ VPS ของคุณ ซึ่งเป็น record ที่คุณต้องการลบออก ให้ลบ record ดังกล่าวในหน้าแดชบอร์ดของ Cloudflare แล้วจึงรันคำสั่งอีกครั้ง การปล่อย record เดิมไว้จะทำให้ DNS ยังคงเผยแพร่ IP ต้นทางของคุณต่อไป ซึ่งจะทำให้ tunnel ไม่สามารถปกปิดข้อมูลได้ตามวัตถุประสงค์
ติดตั้งเป็น service เพื่อให้ทำงานต่อได้หลังรีบูต
ให้รันแบบ foreground หนึ่งครั้ง เพราะการอ่านข้อผิดพลาดบน terminal ของคุณเองนั้นทำได้ง่ายกว่าการอ่านใน journal มาก
sudo cloudflared --config /etc/cloudflared/config.yml tunnel run homelabการเริ่มต้นที่สมบูรณ์จะแสดง log หลายบรรทัดของ Registered tunnel connection โดยแบ่งตาม edge location และแต่ละรายการจะมี connIndex ของตัวเอง ให้ลองโหลด hostname ของคุณในเบราว์เซอร์เพื่อยืนยันว่า ingress rules ส่งคุณไปยังปลายทางที่ถูกต้อง จากนั้นให้หยุดการทำงานด้วย Ctrl-C
sudo cloudflared --config /etc/cloudflared/config.yml service install
systemctl status cloudflaredคำสั่งนี้จะเขียน /etc/systemd/system/cloudflared.service พร้อมกับ cloudflared-update.service และ cloudflared-update.timer จากนั้นจะรัน systemctl enable cloudflared.service และ systemctl start cloudflared.service ให้คุณ enable คือส่วนสำคัญในขั้นตอนนี้ เพราะเป็นสิ่งที่ทำให้ tunnel กลับมาทำงานได้อีกครั้งหลังรีบูต ExecStart ของ unit คือ cloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run ซึ่งเป็นเหตุผลว่าทำไม path ของ config นี้จึงเปลี่ยนแปลงไม่ได้
ความล้มเหลว 3 ประการในขั้นตอนนี้จะมีข้อความแจ้งเตือนที่ชัดเจน possible conflicting configuration in /home/you/.cloudflared/config.yml and /etc/cloudflared/config.yml หมายความว่ามีไฟล์ทั้งสองอยู่และ cloudflared ปฏิเสธที่จะเดา: ให้ลบไฟล์ที่คุณไม่ต้องการออก configuration file ... must contain entries for the tunnel to run and its associated credentials (tunnel: TUNNEL-UUID, credentials-file: CREDENTIALS-FILE) หมายความว่า config ของคุณใช้ shorthand แบบด่วน url: แทนที่จะใช้ key ของ named-tunnel ซึ่ง shorthand ดังกล่าวไม่สามารถรันเป็น service ได้ cloudflared service is already installed หมายความว่ามี unit รุ่นเก่าค้างอยู่ ให้รัน sudo cloudflared service uninstall ก่อน
ไม่มีคำสั่ง reload หลังจากที่คุณแก้ไข /etc/cloudflared/config.yml ให้รัน sudo systemctl restart cloudflared จากนั้นให้พิสูจน์ว่ามันทำงานหลังรีบูตได้จริงแทนการคาดเดา:
sudo reboot
# once it is back
systemctl is-enabled cloudflared
systemctl is-active cloudflared
journalctl -u cloudflared -n 50 --no-pager
curl -sI https://app.example.comการที่ is-enabled แสดง enabled และ is-active แสดง active คือจุดประสงค์ทั้งหมดของส่วนนี้ เมื่อ route ถูกสร้างขึ้นและ service กำลังทำงาน cert.pem ก็ไม่มีหน้าที่อื่นบนเซิร์ฟเวอร์อีกต่อไป: rm ~/.cloudflared/cert.pem การเพิ่ม hostname ในภายหลังหมายถึงการรัน cloudflared tunnel login อีกครั้งเท่านั้น
ปิดพอร์ต 80 และ 443 มิฉะนั้น tunnel จะเป็นเพียงเส้นทางเสริม
คุณต้องดำเนินการสองขั้นตอนควบคู่กัน หากทำเพียงอย่างเดียว ต้นทาง (origin) จะยังคงเข้าถึงได้จากภายนอก
ขั้นแรก ให้ผูก (bind) แอปพลิเคชันเข้ากับ loopback address สำหรับ Nginx หมายถึงการใช้ listen 127.0.0.1:8080; แทนที่ listen 80; ซึ่งเป็นขั้นตอนเดียวกับที่อธิบายไว้ใน คำอธิบายการตั้งค่า Nginx reverse proxy นี้ สำหรับ Docker Compose ให้ใช้ ports: - "127.0.0.1:8080:80" การใช้รูปแบบ "8080:80" แบบปกติจะเป็นการเผยแพร่พอร์ตบนทุกอินเทอร์เฟซ และ Docker จะเขียนกฎ NAT (network address translation) ของตนเองซึ่งแพ็กเก็ตจะวิ่งผ่านก่อนถึง ufw ดังนั้นกฎ deny ของ ufw จึงไม่สามารถหยุดการเชื่อมต่อได้ ปัญหานี้มีคำอธิบายแยกต่างหากใน เหตุใดพอร์ตที่เผยแพร่โดย Docker จึงไม่สนใจ ufw
sudo ss -lntpบริการทุกตัวที่คุณย้ายควรแสดงผลเป็น 127.0.0.1:8080 ในคอลัมน์ Local Address หากบรรทัดใดแสดงเป็น 0.0.0.0:8080 หรือ *:8080 แสดงว่าบริการนั้นยังคงเปิดรับการเชื่อมต่อจากภายนอก
ขั้นที่สอง ให้ปิดพอร์ตดังกล่าว
sudo ufw status numbered
sudo ufw delete allow 'Nginx Full'
sudo ufw statusให้ลบกฎ allow สำหรับพอร์ต 80 และ 443 แทนการเพิ่มกฎ deny ทับลงไป เนื่องจาก ufw จะหยุดประมวลผลที่กฎแรกที่ตรงกัน หากมีกฎ allow เดิมที่ค้างอยู่ลำดับบนกว่า กฎนั้นจะมีผลเหนือกว่า ให้คงกฎ SSH ไว้ตามเดิม คู่มือพื้นฐานไฟร์วอลล์ ufw ครอบคลุมรายละเอียดส่วนที่เหลือของชุดกฎดังกล่าว ผู้ให้บริการ VPS ส่วนใหญ่จะมี network firewall แยกต่างหากในแผงควบคุม ซึ่งไม่ใช่ ufw ดังนั้นคุณต้องปิดพอร์ต 80 และ 443 ในส่วนนั้นด้วย
จากนั้นให้ตรวจสอบจากภายนอก เพราะการใช้ curl http://127.0.0.1:8080 บนตัวเซิร์ฟเวอร์เองไม่สามารถพิสูจน์สถานะการเข้าถึงจากโลกภายนอกได้
# run these from a different machine
nc -vz 203.0.113.10 443
curl -sI https://app.example.comผลลัพธ์ที่คุณต้องการคือการถูกปฏิเสธ (refused) หรือหมดเวลา (timed-out) เมื่อใช้ nc กับ IP โดยตรง และสามารถใช้ 200 ผ่านชื่อโฮสต์ได้สำเร็จ การตรวจสอบว่าพอร์ตเปิดอยู่จริงหรือไม่ ครอบคลุมวิธีการทดสอบเพิ่มเติม
Tunnel เป็นเพียงช่องทางขนส่งข้อมูล ไม่ใช่การยืนยันตัวตน สิ่งใดก็ตามที่คุณเผยแพร่ผ่าน tunnel จะสามารถเข้าถึงได้จากสาธารณะ เว้นแต่คุณจะวางระบบล็อกอินไว้ด้านหน้า ไม่ว่าจะเป็น Cloudflare Access ที่ระดับ edge หรือ การใช้ OAuth2 proxy วางไว้หน้าแอป บนเซิร์ฟเวอร์ สำหรับ SSH จำเป็นต้องมีมาตรการเฉพาะเนื่องจาก tunnel ไม่ครอบคลุมถึง ให้เปิดพอร์ต 22 ไว้แต่จำกัดสิทธิ์เฉพาะ source address ของคุณเท่านั้น
ประโยชน์และต้นทุนของการใช้ Cloudflare Tunnel
สิ่งที่คุณได้รับนั้นเป็นรูปธรรม IP ต้นทางของคุณจะไม่ถูกเผยแพร่อีกต่อไป ไม่มีการเปิดพอร์ตขาเข้า การตั้งค่านี้สามารถใช้งานได้จากเครื่องที่ไม่มี Public IP เลย ใบรับรองสาธารณะเป็นหน้าที่ของ Cloudflare ทำให้ไม่ต้องรันไคลเอนต์ ACME (automatic certificate management environment) บนเครื่องของคุณ และการโจมตีแบบ volumetric จะถูกสกัดกั้นที่ edge แทนที่จะมากินแบนด์วิดท์ของคุณ
ต้นทุนที่ต้องแลกก็เป็นรูปธรรมเช่นกัน Cloudflare จะทำ TLS termination ที่ edge ของพวกเขา: คำขอของผู้เข้าชมจะถูกถอดรหัสที่นั่นและเข้ารหัสใหม่เข้าไปใน tunnel ดังนั้น Cloudflare จึงสามารถอ่านทราฟฟิกของคุณได้ นี่คือสิ่งที่ทำให้ฟีเจอร์ firewall, caching และ Access rules ของพวกเขาทำงานได้ และไม่มีการตั้งค่าใดที่จะปิดการทำงานนี้ได้หากคุณยังคงใช้ proxy ของพวกเขา หากการที่บุคคลที่สามสามารถเข้าถึงข้อมูลแบบ plaintext ของคุณเป็นสิ่งที่ยอมรับไม่ได้ ให้หยุดเพียงเท่านี้และเลือกโซลูชันอื่น
นอกจากนี้ Cloudflare ยังกลายเป็น dependency หลักสำหรับการเข้าถึงบริการ เมื่อ cloudflared ไม่ได้เชื่อมต่อ ผู้เข้าชมจะพบหน้า Error 1033 ของ Cloudflare แทนที่จะเป็นแอปของคุณ และคุณได้ตัดเส้นทางตรงที่ผู้ใช้สามารถใช้สำรองได้ออกไปโดยเจตนา
มีเพียง HTTP, HTTPS และ WebSocket เท่านั้นที่สามารถเข้าถึง hostname สาธารณะได้จากเบราว์เซอร์ทั่วไป โปรโตคอล TCP อื่นๆ ไม่ว่าจะเป็น SSH, RDP (remote desktop protocol) หรือเซิร์ฟเวอร์เกม จำเป็นต้องมีซอฟต์แวร์ที่ฝั่งไคลเอนต์ด้วย เช่น cloudflared access tcp ที่ทำหน้าที่ส่งต่อพอร์ตในเครื่อง หรือการใช้ไคลเอนต์ WARP ไม่มีเส้นทางใดที่เข้าถึงได้โดยไม่ต้องติดตั้งไคลเอนต์สำหรับโปรโตคอลเหล่านี้
ขนาดของ request body จะถูกจำกัดไว้ที่ edge และการอัปโหลดที่เกินขีดจำกัดจะถูกปฏิเสธด้วย HTTP 413 ก่อนที่จะถึงแอปของคุณ ข้อมูล ณ เดือนสิงหาคม 2026 เพดานดังกล่าวอยู่ที่ 100 MB สำหรับแผน Free และ Pro และสูงกว่านั้นในแผนแบบชำระเงิน ดังนั้นควรตรวจสอบหน้าขีดจำกัดปัจจุบันของ Cloudflare ก่อนที่คุณจะออกแบบระบบโดยอิงตามตัวเลขดังกล่าว นอกจากนี้ ข้อกำหนดการใช้งานแบบ self-serve ของ Cloudflare ยังจำกัดการใช้ proxy เพื่อให้บริการวิดีโอและไฟล์ขนาดใหญ่อื่นๆ ที่ไม่ใช่ HTML เป็นหลัก ซึ่งควรศึกษาให้ดีก่อนที่จะเชื่อมต่อคลังสื่อเข้ากับ tunnel แบบฟรี
Cloudflare Tunnel, reverse SSH tunnel หรือ Tailscale Funnel
ทั้งสามวิธีนี้เป็นการเชื่อมต่อแบบ outbound-only ทั้งหมด จึงสามารถใช้งานได้จากเครื่องที่ไม่มีพอร์ตขาเข้าและไม่มี public IP โดยมีความแตกต่างกันที่ผู้ถือครองข้อมูล plaintext ของคุณและชื่อโฮสต์ที่สาธารณะมองเห็น
Reverse SSH tunnel จำเป็นต้องมีเครื่องที่สองที่มี public IP ซึ่งเครื่องนั้นจะทำหน้าที่เป็นประตูด่านหน้า โดยคุณจะต้องเป็นผู้ดูแล certificate, reverse proxy และ firewall ด้วยตนเองทั้งหมด ไม่มีบุคคลที่สามเข้ามาถอดรหัสข้อมูลใดๆ วิธีนี้มีส่วนประกอบที่ต้องจัดการมากกว่า และจำเป็นต้องใช้ autossh หรือ systemd unit ที่มี Restart=always เพื่อให้การเชื่อมต่อคงอยู่ได้เมื่อเครือข่ายขัดข้อง ขั้นตอนการทำ reverse SSH tunnel สำหรับ CGNAT ได้อธิบายรายละเอียดการสร้างไว้แล้ว
Tailscale Funnel เป็นวิธีที่ใกล้เคียงที่สุด โดยเป็นแบบ outbound-only เช่นเดียวกัน และการทำ TLS termination จะเกิดขึ้นบนเครื่องของคุณเอง ทำให้ relay ของ Tailscale ไม่สามารถเข้าถึงข้อมูล plaintext ได้ ข้อจำกัดคือเรื่องชื่อและพอร์ต โดย Funnel จะให้บริการได้เฉพาะชื่อภายใต้โดเมน ts.net ของ tailnet คุณเท่านั้น และรองรับเฉพาะพอร์ต 443, 8443 และ 10000 ความแตกต่างระหว่าง Tailscale Serve และ Funnel ได้อธิบายรายละเอียดทั้งสองส่วนนี้ไว้
ดังนั้น ให้เลือกโดยพิจารณาจากข้อจำกัดที่คุณมี เลือก Cloudflare Tunnel เมื่อสาธารณะจำเป็นต้องเข้าถึงผ่านโดเมนของคุณเองและคุณยอมรับได้ที่ Cloudflare จะอ่านข้อมูลการรับส่ง เลือก Tailscale Funnel เมื่อชื่อโฮสต์แบบ ts.net เป็นที่ยอมรับได้และคุณไม่ต้องการส่งข้อมูล plaintext ให้กับ proxy ของผู้อื่น และเลือก reverse SSH tunnel เมื่อคุณมีเซิร์ฟเวอร์สาธารณะเป็นของตนเองอยู่แล้วและไม่ต้องการให้มีบุคคลที่สามเข้ามาเกี่ยวข้องในเส้นทางการเชื่อมต่อเลย
FAQ
ฉันยังจำเป็นต้องเปิดพอร์ต 443 เมื่อใช้ Cloudflare Tunnel หรือไม่?
ไม่จำเป็น cloudflared จะเชื่อมต่อออกไปยัง Cloudflare ผ่านพอร์ต 7844 และคำขอทั้งหมดจะถูกส่งกลับผ่านการเชื่อมต่อนี้ จึงไม่มีการใช้พอร์ตขาเข้า อย่างไรก็ตาม การติดตั้ง tunnel ไม่ได้ปิดพอร์ตให้คุณโดยอัตโนมัติ คุณต้องลบกฎอนุญาตพอร์ต 80 และ 443 ใน ufw, ปิดพอร์ตดังกล่าวใน network firewall ของผู้ให้บริการ, ตั้งค่าแอปให้ bind กับ 127.0.0.1 และลบ A record ที่ยังคงเผยแพร่ IP ของ VPS คุณทิ้ง ตรวจสอบความถูกต้องด้วย sudo ss -lntp บนเครื่องเซิร์ฟเวอร์ และ nc -vz <your-ip> 443 จากเครื่องอื่น
ทำไม hostname ของฉันถึงแสดง Cloudflare Error 1033?
Error 1033 หมายความว่า Cloudflare ถือ DNS record ของ hostname นั้นอยู่ แต่ไม่พบ cloudflared ที่เชื่อมต่ออยู่เพื่อรับคำขอ อาจเป็นเพราะ process หยุดทำงาน หรือ process ทำงานอยู่แต่ไม่สามารถเข้าถึง Cloudflare ได้ ให้ตรวจสอบ systemctl status cloudflared และ journalctl -u cloudflared -n 50 จากนั้นยืนยันว่าพอร์ตขาออก 7844 ได้รับอนุญาตทั้ง UDP และ TCP เนื่องจาก firewall ที่บล็อก UDP และ QUIC โดยไม่ยอมให้ใช้ TCP fallback จะทำให้เกิดปัญหานี้ cloudflared tunnel info homelab จะแสดงการเชื่อมต่อที่ Cloudflare เห็นอยู่ในปัจจุบัน หากรายการว่างเปล่า แสดงว่าปัญหาอยู่ที่ฝั่งของคุณ
ทำไมฉันถึงได้รับข้อผิดพลาด 502 Bad Gateway ผ่าน tunnel?
502 หมายความว่า cloudflared ได้รับคำขอแล้ว แต่ไม่สามารถติดต่อบริการภายในของคุณได้ ดังนั้นปัญหาจึงอยู่ที่จุดเชื่อมต่อระหว่างสองส่วนนี้ ไม่ใช่ที่ Cloudflare ให้ตรวจสอบ log หากพบ dial tcp [::1]:8080: connect: connection refused หมายความว่าไม่มีบริการใดทำงานอยู่บนพอร์ตที่คุณระบุ และ [::1] ในข้อความดังกล่าวมักหมายความว่าคุณระบุ localhost ใน URL ของ service: ในขณะที่แอป bind เฉพาะ IPv4 เท่านั้น ให้เปลี่ยนไปใช้ http://127.0.0.1:8080 แทน ส่วน HTTP/1.x transport connection broken: malformed HTTP response คือความไม่สอดคล้องในทางตรงกันข้าม คือคุณระบุ https:// ในขณะที่ต้นทางสื่อสารด้วย HTTP ปกติ
ฉันสามารถใช้งาน SSH, RDP หรือเซิร์ฟเวอร์เกมผ่าน Cloudflare Tunnel ได้หรือไม่?
ไม่ได้หากใช้งานผ่าน client ทั่วไป hostname สาธารณะที่ผ่าน tunnel จะรองรับเฉพาะ HTTP, HTTPS และ WebSocket ซึ่งเป็นโปรโตคอลที่เบราว์เซอร์ใช้งาน สำหรับโปรโตคอล TCP อื่นๆ จำเป็นต้องมีซอฟต์แวร์บนเครื่อง client ด้วย เช่น cloudflared access tcp เพื่อทำ port forwarding หรือใช้ WARP client หากคุณต้องการใช้งาน SSH จากเครื่องใดก็ได้โดยไม่ต้องติดตั้งซอฟต์แวร์เพิ่ม tunnel ไม่ใช่เครื่องมือที่เหมาะสม ให้เปิดพอร์ต 22 ไว้และจำกัดสิทธิ์การเข้าถึงด้วย source address แทน
Cloudflare สามารถเห็น traffic ของฉันผ่าน tunnel หรือไม่?
เห็น Cloudflare จะทำ TLS termination ที่ edge ของตนเอง, ถอดรหัสคำขอที่จุดนั้น และเข้ารหัสใหม่เพื่อส่งผ่าน tunnel ไปยังเซิร์ฟเวอร์ของคุณ การถอดรหัสดังกล่าวเป็นสิ่งที่ทำให้ firewall, การทำ caching และนโยบาย Access ของ Cloudflare ทำงานได้ ซึ่งหมายความว่าข้อมูล plaintext ของคุณจะปรากฏบนเครื่องของพวกเขา ไม่มีการตั้งค่าใดที่หลีกเลี่ยงเรื่องนี้ได้หากคุณยังใช้ proxy ของพวกเขา หากคุณไม่ยอมรับเงื่อนไขนี้ ให้ใช้ Tailscale Funnel หรือติดตั้ง reverse proxy ของคุณเองบนเครื่องเซิร์ฟเวอร์สาธารณะ