اپ WebRTC شما روی وایفای خودتان کار میکند و درست برای همان مشتریای که از هتل تماس میگیرد شکست میخورد. رسانهٔ همتا به همتا به مسیری میان دو طرف نیاز دارد؛ وقتی هر دو پشت NAT سختگیر باشند، یا فایروالی UDP را دور بریزد، مسیری نیست و تماس اصلاً وصل نمیشود. هیچکس خطایی نمیبیند — فقط یک «در حال اتصال…» بیپایان.
سرور TURN راه جایگزین همین تماسهاست: هر دو طرف رسانهشان را به آن میفرستند و آن رله میکند. اگر ویدیو یا صدا را در اپی میگنجانید، برای تیمی Matrix یا Jitsi اجرا میکنید یا برای بازیای گفتوگوی صوتی میسازید، به یکی نیاز دارید. coturn پیادهسازی متنباز استاندارد است و سبک است.
کدام پلن
TURN پردازنده و حافظهٔ کمی میخواهد. چیزی که مصرف میکند پهنای باند است: هر جریان رلهشده دو بار، ورود و خروج، از سرور میگذرد. ترافیک در پلنهای ما با سرعت 1 Gbit/s نامحدود است، پس پرسش این است که همزمان چند تماس رلهشده در جریان است، نه اینکه چقدر صورتحساب میآید.
| تماسهای رلهشدهٔ همزمان | پلن |
|---|---|
| چند تماس — یک تیم، یک اپ کوچک | Nano-IP: ۲ vCPU، ۱ گیگابایت، ۱۵ گیگابایت (۸ دلار در ماه) |
| دهها تماس | Micro-IP: ۲ vCPU، ۲ گیگابایت، ۲۵ گیگابایت (۱۰ دلار در ماه) |
| بیشتر، یا TURN کنار سرور اپ | Small-IP: ۴ vCPU، ۴ گیگابایت، ۳۵ گیگابایت (۱۶ دلار در ماه) |
باید پلنی با 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 نگذرید. بدون آنها هر کسی با اعتبارنامهٔ معتبر میتواند از رلهٔ شما برای رسیدن به نشانیهای خصوصی استفاده کند — از جمله سرویسهای سمت داخلی خود سرور. بررسی کردیم: با این خطوط، رله به یک نشانی خصوصی با «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 گرفت؛ با گذرواژهٔ بد، هیچ. اگر میخواهید چیدمان خودتان را همینطور آزمایش کنید، این گزینه را برای لحظهای روشن کنید — هر تماس را مجبور میکند از 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 اتصال بد را خوب نمیکند. یک گام اضافه میکند، پس سرور را نزدیک کسانی بگذارید که به آن نیاز دارند.
- اگر SFU اجرا میکنید — مثلاً videobridge در Jitsi — آن قطعهای جداست. TURN به کلاینتهایی کمک میکند که نمیتوانند مستقیم به آن برسند.
- کل مدل امنیتی همان راز است. آن را از فرانتاند دور نگه دارید و اگر روزی لو رفت عوضش کنید.
مرتبط
- سرور VPS برای Jitsi Meet: تماس تصویری روی سرور خودتان
- Matrix Synapse را خودتان میزبانی کنید: تماسهای Matrix هم به سرور TURN نیاز دارند
- سرور VPS برای وبسایت با IP اختصاصی: اپی که اعتبارنامهها را صادر میکند
- چگونه فایروال ufw را پیکربندی کنیم
نظرات
هنوز نظری نیست. اولین نفر باشید.