−25%

Windows รายปี ถึง 31 ต.ค. ดูแพ็กเกจ

EQVPS
เริ่มต้นใช้งาน

VPS สำหรับเซิร์ฟเวอร์ TURN (coturn) ของ WebRTC

สายที่ล้มเหลวหลัง NAT แบบเข้มงวดต้องใช้ TURN relay รัน coturn บนเซิร์ฟเวอร์ที่มี IPv4 ของตัวเอง พอร์ต ข้อมูลล็อกอินแบบมีอายุ การตั้งค่าที่ทดสอบแล้ว และแพ็กเกจ

แอป WebRTC ของคุณใช้ได้บน Wi-Fi ของคุณ แต่ล้มเหลวกับลูกค้าคนเดียวที่โทรมาจากโรงแรม สื่อแบบ peer-to-peer ต้องมีเส้นทางระหว่างสองฝั่ง เมื่อทั้งคู่อยู่หลัง NAT แบบเข้มงวด หรือไฟร์วอลล์ทิ้ง UDP ก็จะไม่มีเส้นทาง และสายก็แค่ไม่ติด ไม่มีใครเห็นข้อผิดพลาด มีแต่ “กำลังเชื่อมต่อ…” ไม่รู้จบ

เซิร์ฟเวอร์ TURN คือทางสำรองสำหรับสายแบบนั้น ทั้งสองฝั่งส่งสื่อไปที่มัน แล้วมันส่งต่อให้ ถ้าคุณกำลังใส่วิดีโอหรือเสียงลงในแอป รัน Matrix หรือ Jitsi ให้ทีม หรือทำแชตเสียงสำหรับเกม คุณต้องมีสักตัว coturn คือตัวมาตรฐานแบบโอเพนซอร์ส และเบามาก

เลือกแพ็กเกจไหน

TURN ใช้ CPU และหน่วยความจำน้อย สิ่งที่มันใช้คือแบนด์วิดท์ ทุกสตรีมที่ส่งต่อจะผ่านเซิร์ฟเวอร์สองครั้ง ทั้งเข้าและออก ทราฟฟิกบนแพ็กเกจของเราไม่จำกัดที่ 1 Gbit/s คำถามจึงอยู่ที่ว่ามีสายที่ส่งต่อพร้อมกันกี่สาย ไม่ใช่ว่าจะโดนเรียกเก็บเท่าไร

จำนวนสายที่ส่งต่อพร้อมกันแพ็กเกจ
ไม่กี่สาย ทีมหนึ่งทีม แอปเล็ก ๆNano-IP: 2 vCPU, 1 GB, 15 GB ($8/เดือน)
หลายสิบสายMicro-IP: 2 vCPU, 2 GB, 25 GB ($10/เดือน)
มากกว่านั้น หรือวาง TURN ข้างเซิร์ฟเวอร์แอปSmall-IP: 4 vCPU, 4 GB, 35 GB ($16/เดือน)

ต้องเป็นแพ็กเกจที่มี IPv4 ของตัวเอง เพราะ TURN คือการเชื่อมต่อขาเข้าบนหลายพอร์ต และแพ็กเกจ NAT รับจากภายนอกได้แค่ SSH บนแพ็กเกจที่มี IP ของตัวเองของเรา ที่อยู่สาธารณะจะอยู่บนอินเทอร์เฟซของเซิร์ฟเวอร์โดยตรง coturn จึงหาเจอเองโดยไม่ต้องมีบรรทัด external-ip วางเซิร์ฟเวอร์ใกล้ผู้ใช้ของคุณ เยอรมนีหรือฟินแลนด์ ดูได้ว่าว่างหรือไม่ตอนสั่งซื้อ ชำระด้วยคริปโตได้ ไม่ต้องทำ KYC ถ้าเรื่องนี้สำคัญกับคุณ

พอร์ตที่ต้องเปิด: 3478 UDP และ TCP, 49152–65535 UDP สำหรับสื่อที่ส่งต่อ และ 5349 TCP ถ้าคุณเพิ่ม TLS

ติดตั้งและตั้งค่า

ในฐานะ root บน Ubuntu 24.04 หรือ Debian 12:

apt install -y coturn
ip -4 addr    # IP สาธารณะของคุณควรอยู่ในรายการของอินเทอร์เฟซ

เขียนไฟล์ตั้งค่าพร้อมความลับที่ใช้ร่วมกันชุดใหม่:

SECRET=$(openssl rand -hex 32)
cat > /etc/turnserver.conf <<EOF
listening-port=3478
fingerprint
use-auth-secret
static-auth-secret=$SECRET
realm=turn.example.com
min-port=49152
max-port=65535
no-cli
no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
EOF
echo "$SECRET"    # แบ็กเอนด์ของคุณต้องใช้ค่านี้
systemctl restart coturn
ufw allow 22/tcp && ufw allow 3478 && ufw allow 49152:65535/udp && ufw enable

อย่าข้ามบรรทัด denied-peer-ip ถ้าไม่มี ใครก็ตามที่มีข้อมูลล็อกอินถูกต้องจะใช้ relay ของคุณเข้าถึงที่อยู่ส่วนตัวได้ รวมถึงบริการที่อยู่ฝั่งภายในของเซิร์ฟเวอร์เอง เราตรวจแล้ว เมื่อมีบรรทัดเหล่านี้ relay ไปยังที่อยู่ส่วนตัวจะถูกปฏิเสธด้วย “403 Forbidden IP”

ข้อมูลล็อกอินที่หมดอายุได้

เมื่อใช้ use-auth-secret coturn จะรับชื่อผู้ใช้ที่มีเวลาหมดอายุอยู่ในตัว และรหัสผ่านที่เป็น HMAC ของชื่อนั้น แบ็กเอนด์ของคุณเป็นผู้สร้าง ความลับไม่เคยออกจากฝั่งเซิร์ฟเวอร์ ใน Node.js:

const crypto = require("crypto");

// ชื่อผู้ใช้/รหัสผ่าน TURN ชั่วคราว (รูปแบบ TURN REST API ที่ coturn เข้าใจผ่าน use-auth-secret)
function turnCredentials(secret, user, ttlSeconds = 3600) {
  const username = `${Math.floor(Date.now() / 1000) + ttlSeconds}:${user}`;
  const credential = crypto.createHmac("sha1", secret).update(username).digest("base64");
  return { username, credential };
}

console.log(JSON.stringify(turnCredentials(process.env.TURN_SECRET, "alice")));

ในเบราว์เซอร์ ขอคู่ใหม่จากแบ็กเอนด์แล้วส่งให้ RTCPeerConnection:

const { username, credential } = await fetch("/api/turn-credentials").then((r) => r.json());

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: "stun:turn.example.com:3478" },
    {
      urls: ["turn:turn.example.com:3478?transport=udp", "turn:turn.example.com:3478?transport=tcp"],
      username,
      credential,
    },
  ],
});

ทดสอบ

จากเซิร์ฟเวอร์ ใช้สูตรเดียวกันด้วย openssl (ได้รหัสผ่านตรงกับโค้ด Node.js ทุกตัวอักษร เราเทียบแล้ว):

U="$(( $(date +%s) + 3600 )):test"
P=$(echo -n "$U" | openssl dgst -binary -sha1 -hmac "$SECRET" | base64)
turnutils_uclient -y -n 10 -u "$U" -w "$P" YOUR_SERVER_IP

ตอนท้ายคุณควรเห็น “Total lost packets 0” ถ้ารหัสผ่านผิด ไคลเอนต์จะหยุดที่ “Cannot complete Allocation” นั่นคือเซิร์ฟเวอร์ทำหน้าที่ของมัน

เรายังทดสอบจาก Chromium จริงด้วย เมื่อใช้ iceTransportPolicy: "relay" และข้อมูลล็อกอินใหม่ เบราว์เซอร์ได้ relay candidate จากเซิร์ฟเวอร์ ส่วนรหัสผ่านผิดไม่ได้เลยสักตัว ถ้าอยากทดสอบระบบของคุณแบบเดียวกัน ให้เปิดตัวเลือกนี้ชั่วคราว มันบังคับให้ทุกสายวิ่งผ่าน TURN

TLS ถ้าจำเป็น

บางเครือข่ายบล็อก UDP และจู้จี้กับ TCP ธรรมดา TURN ผ่าน TLS จะทะลุได้มากกว่า ขอใบรับรองสำหรับ turn.example.com (ใช้ certbot ก็ได้) ไฟล์ของมันอ่านได้เฉพาะ root ขณะที่ coturn รันในนามผู้ใช้ turnserver จึงต้องให้ coturn มีสำเนาของตัวเอง:

mkdir -p /etc/coturn
cp /etc/letsencrypt/live/turn.example.com/fullchain.pem /etc/letsencrypt/live/turn.example.com/privkey.pem /etc/coturn/
chown turnserver: /etc/coturn/*.pem && chmod 640 /etc/coturn/privkey.pem
cat >> /etc/turnserver.conf <<'EOF'
tls-listening-port=5349
cert=/etc/coturn/fullchain.pem
pkey=/etc/coturn/privkey.pem
EOF
systemctl restart coturn && ufw allow 5349/tcp

เพิ่ม turns:turn.example.com:5349?transport=tcp ลงในรายการ urls ใบรับรองมีอายุสั้น จึงควรใส่ขั้นตอนคัดลอกไว้ใน deploy hook ของ certbot ไม่อย่างนั้น TLS จะพังเงียบ ๆ ตอนต่ออายุครั้งแรก

ควรรู้

  • TURN ไม่ได้ทำให้การเชื่อมต่อแย่ ๆ ดีขึ้น มันเพิ่มอีกหนึ่ง hop จึงควรวางเซิร์ฟเวอร์ใกล้คนที่ต้องใช้
  • ถ้าคุณรัน SFU เช่น videobridge ของ Jitsi นั่นเป็นอีกส่วนหนึ่ง TURN ช่วยไคลเอนต์ที่เข้าถึงมันโดยตรงไม่ได้
  • โมเดลความปลอดภัยทั้งหมดอยู่ที่ความลับ เก็บให้ห่างจากฟรอนต์เอนด์ และเปลี่ยนทันทีถ้ามันรั่ว

ที่เกี่ยวข้อง

พร้อม deploy แล้วหรือยัง? จ่ายด้วยคริปโต ไม่ต้อง KYC — ใช้งานได้ในราวหนึ่งนาที

Deploy เลย →

FAQ

ทำไมสาย WebRTC ถึงต้องมีเซิร์ฟเวอร์ TURN?

WebRTC พยายามเชื่อมผู้ร่วมสายเข้าหากันโดยตรง และส่วนใหญ่ก็ทำได้ แต่เมื่อทั้งสองฝั่งอยู่หลัง NAT แบบสมมาตรหรือไฟร์วอลล์ที่ทิ้ง UDP เช่น Wi-Fi โรงแรม เครือข่ายออฟฟิศ หรือผู้ให้บริการมือถือบางราย ก็จะไม่มีเส้นทางตรง และสายจะล้มเหลวเงียบ ๆ เซิร์ฟเวอร์ TURN จะส่งต่อสื่อให้เฉพาะสายแบบนั้น สัดส่วนที่มักพูดถึงคือราวหนึ่งในสิบสาย

TURN รันบนแพ็กเกจ NAT ได้ไหม?

ไม่ได้ ไคลเอนต์เชื่อมต่อเข้ามาที่ UDP และ TCP 3478 และช่วงพอร์ต relay ขณะที่แพ็กเกจ NAT รับจากภายนอกได้เพียงพอร์ต SSH ของคุณ คุณต้องใช้แพ็กเกจที่มี IPv4 ของตัวเอง

ใช้แบนด์วิดท์เท่าไร?

ทุกสตรีมที่ส่งต่อจะวิ่งผ่านเซิร์ฟเวอร์สองครั้ง เข้าจากฝั่งหนึ่งและออกไปยังอีกฝั่ง วิดีโอคอลแบบตัวต่อตัวที่ 1–2 Mbit/s ต่อทิศทางจะใช้ไม่กี่ Mbit/s บนเซิร์ฟเวอร์ CPU และ RAM แทบไม่รู้สึก ขีดจำกัดจริงคือพอร์ต 1 Gbit/s

ทำไมไม่ใส่ชื่อผู้ใช้และรหัสผ่านตายตัวไว้ในแอปเลย?

เพราะทุกอย่างใน JavaScript ของเบราว์เซอร์เป็นสาธารณะ และเซิร์ฟเวอร์ TURN ที่รหัสผ่านรั่วก็คือ relay ฟรีสำหรับทั้งอินเทอร์เน็ต เมื่อใช้ use-auth-secret แบ็กเอนด์ของคุณจะแจกข้อมูลล็อกอินที่หมดอายุได้ ซึ่งลงนามด้วยความลับที่เบราว์เซอร์ไม่มีวันเห็น

ต้องใช้ TLS ที่พอร์ต 5349 ไหม?

ช่วยได้ในเครือข่ายที่บล็อก UDP และเข้มงวดกับ TCP ธรรมดา เพิ่มหลังจาก TURN แบบปกติใช้งานได้แล้ว โดยให้ใบรับรองสำหรับชื่อโฮสต์ TURN แก่ coturn และเปิด 5349/tcp เราได้ทดสอบ relay ผ่าน TLS แล้วเช่นกัน

ความคิดเห็น

ยังไม่มีความคิดเห็น เป็นคนแรกสิ

แสดงความคิดเห็น

ความคิดเห็นจะถูกตรวจสอบก่อนแสดง