رفع مشکل DNS در WireGuard: 3 حالت خرابی
تونل WireGuard برقرار است اما نامها resolve نمیشوند یا queryها به router محلی نشت میکنند؟ 3 حالت خرابی DNS، خطای دقیق و راهحل هرکدام را بررسی کنید.
چرا بهمحض برقراری تونل WireGuard، DNS از کار میافتد
DNS روی WireGuard به 3 شکل از کار میافتد و هرکدام راهحل جداگانهای دارد. ممکن است هیچ نامی resolve نشود، یا نامها resolve شوند اما queryها خارج از تونل از سیستم شما خارج شوند، یا resolver manager خود کلاینت چند ثانیه پس از راهاندازی interface، تنظیمات را بازنویسی کند. تقریباً هیچوقت خود تونل مشکل ندارد. مشکل معمولاً همان خطی است که به کلاینت میگوید از کدام resolver درخواست کند، و همچنین مسیریابیای که تعیین میکند packetها چگونه به آن resolver برسند.
WireGuard، IP packetها را منتقل میکند و درباره DNS (domain name system، سرویسی که نامهایی مانند example.com را به IP address تبدیل میکند) اطلاعی ندارد. خط DNS = در یک block مربوط به کلاینت [Interface]، تنظیم WireGuard نیست. این خط توسط wg-quick، یعنی shell wrapperای که interface را راهاندازی میکند، خوانده میشود و wg-quick سپس پیکربندی resolver کلاینت را در زمان فعال بودن تونل ویرایش میکند و در wg-quick down آن را بازمیگرداند. بنابراین هر یک از مشکلات زیر، مشکل مسیریابی یا مشکل wg-quick است، نه مشکل cryptography. اگر خود تونل هنوز ایجاد نشده است، کار را با یک VPN مبتنی بر WireGuard که روی VPS شخصی خود میزبانی میکنید شروع کنید و سپس به این صفحه بازگردید.
پیش از هرگونه تغییر در DNS، از سالم بودن تونل مطمئن شوید.
sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1wg show باید peer را همراه با یک latest handshake جدید فهرست کند و هر دو ping باید پاسخ دریافت کنند. اگر ping 1.1.1.1 timeout شود، با مشکل forwarding یا NAT (network address translation) مواجه هستید، نه مشکل DNS؛ هیچ مقدار تنظیمات resolver این مشکل را برطرف نمیکند. در همه مثالهای اینجا، 10.8.0.0/24 بهعنوان subnet تونل و 10.8.0.1 بهعنوان address تونل سرور استفاده شده است. مقادیر خود را جایگزین کنید.
خطای اول: هیچ نامی resolve نمیشود، چون resolver هرگز پاسخ نمیدهد
نشانه دقیق است. ping 1.1.1.1 کار میکند، اما curl https://example.com این خروجی را برمیگرداند:
curl: (6) Could not resolve host: example.comاز سمت client، مستقیماً از tunnel resolver پرسوجو کنید. dig در بسته dnsutils در Ubuntu و Debian ارائه میشود.
dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.comدستور اول یک address برمیگرداند و ثابت میکند که packetها از طریق tunnel به internet میرسند. دستور دوم چیزی برنمیگرداند و ;; communication timed out; no servers could be reached را چاپ میکند. تشخیص کامل همین است: client شما به 10.8.0.1 اشاره میکند و 10.8.0.1 روی UDP port 53 پاسخ نمیدهد.
این وضعیت دو علت دارد. یا هیچ resolverی روی server در حال اجرا نیست، یا firewall سرور query را پیش از رسیدن drop میکند. هر دو مورد را روی server بررسی کنید.
sudo ss -ulnp | grep ':53'
sudo nft list rulesetresolverی که در حال اجراست و بهدرستی bind شده، خطی شامل 10.8.0.1:53 یا 0.0.0.0:53 نشان میدهد. در Ubuntu، مورد غیرمنتظره معمولاً 127.0.0.53:53 است: این همان systemd-resolved stub listener است که یک loopback address را bind میکند و عمداً از ماشینهای دیگر قابل دسترسی نیست. اگر VPN client را به سروری هدایت کنید که تنها resolver آن همین stub باشد، دقیقاً همین timeout رخ میدهد.
راهحل، resolverی است که روی tunnel address به درخواستها گوش دهد، بههمراه یک firewall rule که دسترسی peerها به آن را مجاز کند.
sudo apt update && sudo apt install -y unbound
printf 'server:\n interface: 10.8.0.1\n access-control: 10.8.0.0/24 allow\n' \
| sudo tee /etc/unbound/unbound.conf.d/wireguard.conf
sudo systemctl restart unbound
sudo ss -ulnp | grep '10.8.0.1:53'سپس port را فقط برای tunnel traffic باز کنید. در nftables، این دو خط را به chain با نام input در /etc/nftables.conf اضافه کنید و با sudo systemctl reload nftables پیکربندی را reload کنید.
udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" acceptدر ufw، sudo ufw allow in on wg0 to any port 53 همین کار را انجام میدهد. هرگز port 53 را برای public internet باز نکنید. یک open recursive resolver ظرف چند روز توسط scannerها شناسایی میشود و برای تقویت حملات denial of service به کار میرود؛ provider شما نیز پیش از آنکه خودتان متوجه شوید، این traffic را تشخیص میدهد.
dig +short @10.8.0.1 example.com را دوباره از client اجرا کنید. وجود یک address در خروجی نشان میدهد که مسیر resolver کار میکند؛ بنابراین اکنون فقط باید client از آن استفاده کند. این خط را به block با نام [Interface] در client اضافه کنید و interface را با sudo wg-quick down wg0 && sudo wg-quick up wg0 restart کنید.
[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1خطای دوم: نشت DNS، چون split tunnel، resolver را مسیریابی نمیکند
این مورد بدتر است، چون همهچیز ظاهراً درست کار میکند. نامها resolve میشوند، صفحات بارگذاری میشوند و queryها بهصورت cleartext از شبکه محلی عبور میکنند؛ همان شبکهای که نمیخواستید به آن اعتماد کنید.
دو پیکربندی باعث این مشکل میشوند. حالت اول، کلاینتی است که AllowedIPs = 0.0.0.0/0, ::/0 دارد اما خط DNS = را ندارد. wg-quick، route پیشفرض را در جدول مسیریابی خودش نصب میکند و با suppress_prefixlength 0 یک rule اضافه میکند. این کار عمداً routeهای محلیِ خاصتر را فعال نگه میدارد تا ماشین همچنان بتواند به printer خود دسترسی داشته باشد. resolverی که کلاینت از طریق DHCP دریافت کرده است، و معمولاً router در 192.168.1.1 است، با یکی از همین routeهای محلی مطابقت دارد. ترافیک شما از tunnel عبور میکند، اما شبکه محلی همچنان فهرست کامل نامهایی را که جستوجو میکنید دریافت میکند.
حالت دوم، split tunnel با AllowedIPs = 10.8.0.0/24 و DNS = 9.9.9.9 است. چون 9.9.9.9 داخل AllowedIPs نیست، کلاینت routeای برای رسیدن به آن از طریق tunnel ندارد. در نتیجه query، درست مانند حالت اول، از لینک محلی خارج میشود.
مشخص کنید کدام resolver واقعاً پاسخ میدهد. whoami.akamai.net یک نام آزمایشی عمومی است که با IP address مربوط به recursive resolverی که query را ارسال کرده است پاسخ میدهد. بنابراین میتوانید پاسخ آن را با public address سرور خود مقایسه کنید.
resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53resolvectl status برای هر link یک block چاپ میکند. اگر block مربوط به link اترنت یا wireless شما همچنان Current DNS Server: 192.168.1.1 را نشان دهد، درحالیکه block مربوط به wg0 چیزی نشان نمیدهد، نشت رخ داده است. بازگرداندن dig +short whoami.akamai.net بهجای address سرور شما، آدرس broadband خانگیتان را از سمت مقصد نیز تأیید میکند. خط tcpdump مدرکی است که اختلاف را پایان میدهد: در خروجی سالم، همه packetهای port 53 روی wg0 قرار میگیرند و در حالت نشت، روی wlan0 یا enp3s0 قرار دارند.
رفع مشکل دو بخش دارد و هر دو ضروری هستند. DNS را روی addressی تنظیم کنید که داخل tunnel قرار دارد و مطمئن شوید این address داخل AllowedIPs نیز هست.
[Interface]
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 10.20.0.0/1610.8.0.1 داخل 10.8.0.0/24 قرار دارد؛ بنابراین query رمزگذاری میشود و به سرور ارسال میگردد. اگر در split tunnel اصرار دارید از یک resolver عمومی استفاده کنید، آن را بهصورت host route اضافه کنید: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32. در این حالت packetها از tunnel عبور میکنند، هرچند شبکه محلی همچنان میتواند از sessionهای قبلی متوجه شود که کدام provider را انتخاب کردهاید. استفاده از resolverی که خودتان اجرا میکنید، این مسئله را برطرف میکند.
تخصیص resolver یکی از تفاوتهای قابل مشاهده میان WireGuard ساختهشده بهصورت دستی و یک mesh هماهنگشده است و بخشی از موازنه مزایا و معایب در مقایسه WireGuard با Tailscale بهشمار میرود. اجرای یک control server خودمیزبان Headscale این هماهنگی را بدون واگذاری key material شما به شخص ثالث فراهم میکند.
خرابی سوم: resolvconf و systemd-resolved در کلاینتهای Linux با یکدیگر تداخل دارند
کلاینتهای macOS، Windows، iOS و Android، DNS = را از طریق برنامه رسمی اعمال میکنند و معمولاً مشکل زیادی ایجاد نمیکنند. مشکل در Linux رخ میدهد؛ جایی که یک اسکریپت shell باید حدس بزند شما از کدامیک از چند مدیر resolver استفاده میکنید.
نخستین خرابی آشکار است. sudo wg-quick up wg0 متوقف میشود و این پیام را نشان میدهد:
resolvconf: command not foundwg-quick، resolvconf را فراخوانی میکند، اما آن binary نصب نشده است. پیادهسازیای را نصب کنید که با systemd-resolved ارتباط برقرار میکند، سپس interface را دوباره فعال کنید.
sudo apt install -y openresolv
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0خرابی دوم بیصداست و همان خرابیای است که یک عصر کامل را تلف میکند. interface فعال میشود، resolvectl status wg0 مقدار DNS Servers: 10.8.0.1 را بهدرستی نشان میدهد، اما lookupها همچنان به resolver قبلی ارسال میشوند. systemd-resolved برای هر link فهرست resolver جداگانهای نگه میدارد و برای هر query یک link انتخاب میکند. اگر هیچ linkای بهعنوان مسیر پیشفرض برای نامها علامتگذاری نشده باشد، همچنان از resolver مربوط به link بیسیم استفاده میکند؛ زیرا آن link یک search domain دارد و link شما ندارد.
Resolver را تنظیم کنید و مسیر پیشفرض را در همان مرحله اختصاص دهید. %i به نام interface گسترش مییابد؛ بنابراین این block بدون تغییر روی هر interface کار میکند.
[Interface]
Address = 10.8.0.2/32
PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.
PostDown = resolvectl revert %iاگر از PostUp به این روش استفاده میکنید، خط DNS = را حذف کنید؛ در غیر این صورت دو سازوکار وضعیت resolver را مینویسند و فقط یکی از آنها در پایان پاکسازی را انجام میدهد. آرگومان ~. بخش مهم کار است: این آرگومان wg0 را بهعنوان routing domain برای همه نامها علامتگذاری میکند؛ بنابراین systemd-resolved همه queryها را به آنجا میفرستد و برای هر query یک link انتخاب نمیکند. آن را بررسی کنید.
resolvectl status wg0خروجی سالم شامل DNS Servers: 10.8.0.1 و Default Route: yes است. اگر Default Route مقدار no را نشان دهد، بخش resolvectl domain اجرا نشده است و دوباره به انتخاب link بازگشتهاید.
یک حالت دیگر نیز شایان توجه است. اگر /etc/resolv.conf یک فایل واقعی باشد، نه symlinkی به /run/systemd/resolve/stub-resolv.conf، مالک آن چیز دیگری است؛ معمولاً NetworkManager یا یک container runtime. پیش از اشکالزدایی هر بخش دیگر، ls -l /etc/resolv.conf را اجرا کنید؛ زیرا ابزاری که آن فایل را در هر تغییر شبکه بازنویسی میکند، در نامناسبترین زمان تغییرات شما را برمیگرداند.
ارتقا: resolver فیلترکننده خودتان روی tunnel
پس از آنکه queryها بهطور پایدار از tunnel عبور کردند، resolver در سمت دور به یک نقطه کنترل تبدیل میشود. اجرای AdGuard Home در آنجا برای همه دستگاههای متصل، فیلترکردن بر اساس blocklist و query log فراهم میکند؛ بدون نرمافزار سمت client و بدون پیکربندی جداگانه برای هر دستگاه. اسکریپت نصب رسمی، که در July 2026 بررسی شده است، یک خط است.
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -vsetup wizard در اولین اجرا روی port 3000 به درخواستها گوش میدهد. بهجای بازکردن عمومی این port، از طریق tunnel و در نشانی http://10.8.0.1:3000 به آن دسترسی پیدا کنید. سپس در wizard، هم نشانی گوشدادن DNS و هم نشانی گوشدادن admin را روی 10.8.0.1 تنظیم کنید. اگر unbound مربوط به failure one هنوز همان نشانی را در اختیار دارد، ابتدا آن را با sudo systemctl disable --now unbound متوقف کنید؛ زیرا دو process نمیتوانند روی یک نشانی، به UDP port 53 متصل شوند و process دوم با listen udp 10.8.0.1:53: bind: address already in use خارج میشود.
اگر پیکربندیهای client از قبل مقدار DNS = 10.8.0.1 را داشته باشند، نیازی به تغییر ندارند. اکنون query log هر lookup را از هر peer نشان میدهد. این یک تصمیم واقعی درباره حریم خصوصی است، نه یک مزیت رایگان: اعتماد خود را از provider اینترنت به خودتان منتقل میکنید و مسئول بهروز نگهداشتن آن system نیز خودتان هستید. سروری که در معرض اینترنت قرار دارد، ابتدا به اقدامات پایه امنیتی نیاز دارد و ده دقیقه اول روی یک VPS جدید این موارد را پوشش میدهد.
FAQ
چرا تونل WireGuard متصل میشود، اما نامها resolve نمیشوند؟
تونل بستهها را منتقل میکند و اصلاً نامها را مدیریت نمیکند. بنابراین، اگر تونل فعال باشد اما lookupها خراب باشند، resolverی که به آن اشاره کردهاید پاسخ نمیدهد. در کلاینت، با dig +short @10.8.0.1 example.com آزمایش کنید. پاسخ communication timed out یعنی یا هیچ resolverی روی آن نشانی تونل در حال listening نیست، که اغلب به این دلیل است که stub مربوط به systemd-resolved فقط روی 127.0.0.53 bind میشود، یا firewall سرور در حال drop کردن پورت UDP شماره 53 ورودی از wg0 است. ابتدا listener را اصلاح کنید، سپس پورت را فقط برای wg0 باز کنید.
چگونه بررسی کنم که DNS من از طریق WireGuard نشت میکند؟
در کلاینت sudo tcpdump -ni any -c 10 port 53 را اجرا کنید و هنگام مرور وب، ستون interface را monitor کنید. همه بستهها باید روی wg0 باشند. اگر بستهها روی interface بیسیم یا ethernet شما ظاهر شوند، queryها بهصورت cleartext خارج میشوند. dig +short whoami.akamai.net نظر دوم را ارائه میدهد، زیرا با نشانی عمومی recursive resolverی پاسخ میدهد که query را ارسال کرده است. بنابراین، اگر پاسخ نشانی سرور شما نباشد، نشت را تأیید میکند.
اگر از split tunnel استفاده کنم، آیا به خط DNS = نیاز دارم؟
بله. نشانی resolver نیز باید داخل AllowedIPs قرار داشته باشد؛ در غیر این صورت، کلاینت مسیری به آن ندارد. با AllowedIPs = 10.8.0.0/24، resolverی در 10.8.0.1 تحت پوشش قرار میگیرد و query رمزنگاری میشود. resolver عمومی مانند 9.9.9.9 تحت پوشش نیست. بنابراین، query از طریق لینک محلی خارج میشود، حتی اگر خط DNS درست به نظر برسد.
چرا resolvectl سرور درست را نشان میدهد، اما lookupها همچنان به جای دیگری میروند؟
systemd-resolved برای هر link یک فهرست resolver جداگانه نگه میدارد و برای هر query یک link را انتخاب میکند. بنابراین، وقتی link دیگری مسیر پیشفرض نامها را در اختیار دارد، ورودی درست در wg0 نادیده گرفته میشود. PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. را به بلوک [Interface] کلاینت اضافه کنید و خط DNS = را حذف کنید. سپس resolvectl status wg0 باید Default Route: yes را گزارش کند.
وقتی چند کلاینت دچار مشکل هستند، ابتدا کدام کلاینت را اصلاح کنم؟
ابتدا یک کلاینت Linux را اصلاح کنید، زیرا تنها همین پلتفرم سازوکار را به شما نشان میدهد. resolvectl status و tcpdump مشخص میکنند کدام resolver پاسخ داده و کدام interface بسته را منتقل کرده است. برنامههای تلفن و desktop همان مقادیر DNS و AllowedIPs را اعمال میکنند، اما جزئیات plumbing را نمایش نمیدهند. بنابراین، پس از درست شدن کلاینت Linux، پیکربندیای را کپی میکنید که قبلاً صحت آن را اثبات کردهاید.