WireGuard-এ DNS ঠিক করুন: 3টি সাধারণ ব্যর্থতা
WireGuard tunnel চালু, কিন্তু নাম resolve হচ্ছে না বা DNS query local router-এ leak হচ্ছে? তিনটি failure mode শনাক্ত করে routing ও resolver সেটিং ঠিক করুন।
WireGuard tunnel চালু হলেই DNS কেন নষ্ট হয়
WireGuard-এর মাধ্যমে DNS তিনভাবে ব্যর্থ হতে পারে, এবং প্রতিটির আলাদা সমাধান আছে। কিছুই resolve হয় না। অথবা নাম resolve হলেও query tunnel-এর বাইরে আপনার মেশিন থেকে বেরিয়ে যায়। আবার interface চালু হওয়ার কয়েক সেকেন্ড পর client-এর নিজস্ব resolver manager সেটিংটি overwrite করে। Tunnel প্রায় কখনোই সমস্যার কারণ নয়। সমস্যা হয় সেই এক লাইনে, যা client-কে কোন resolver-কে জিজ্ঞাসা করতে হবে তা জানায়, এবং সেই routing-এ, যা নির্ধারণ করে resolver-এ packet কীভাবে যাবে।
WireGuard IP packet পরিবহন করে এবং DNS সম্পর্কে কিছু জানে না। DNS (domain name system) হলো এমন একটি service, যা example.com-এর মতো নামকে IP address-এ রূপান্তর করে। Client-এর [Interface] block-এর DNS = line কোনো WireGuard setting নয়। Interface চালু করা shell wrapper wg-quick এই line পড়ে। এরপর tunnel সক্রিয় থাকা অবস্থায় wg-quick client-এর resolver configuration সম্পাদনা করে এবং wg-quick down-এ সেটি পুনরুদ্ধার করে। তাই নিচের প্রতিটি সমস্যা routing সমস্যা অথবা wg-quick সমস্যা। এগুলো cryptography সমস্যা নয়। Tunnel এখনো তৈরি না হলে প্রথমে নিজের VPS-এ self-hosted WireGuard VPN সেটআপ করুন। তারপর এই পৃষ্ঠায় ফিরে আসুন।
DNS নিয়ে কোনো পরিবর্তন করার আগে tunnel সুস্থ আছে কি না নিশ্চিত করুন।
sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1wg show-এ সাম্প্রতিক latest handshake-সহ peer দেখা উচিত এবং উভয় ping-এর উত্তর পাওয়া উচিত। ping 1.1.1.1 timeout হলে DNS সমস্যার বদলে forwarding বা NAT (network address translation) সমস্যা থাকার সম্ভাবনাই বেশি। Resolver configuration পরিবর্তন করলেও এতে কোনো উপকার হবে না। ICMP drop করা firewall থেকেও একই timeout হতে পারে। তাই forwarding rule পরিবর্তন করার আগে curl -sI https://1.1.1.1 দিয়ে বিষয়টি নিশ্চিত করুন। Ping-এর উত্তর পাওয়া গেলেও প্রকৃত network traffic শুরু হলে throughput কমে গেলে সেটি আলাদা সমস্যা। এই ধরনের সমস্যা প্রায় সব সময় DNS নয়, MTU-এর সঙ্গে সম্পর্কিত; দেখুন ধীর WireGuard-এর কারণ প্রায় সব সময় MTU। এখানে প্রতিটি উদাহরণে tunnel subnet হিসেবে 10.8.0.0/24 এবং server-এর tunnel address হিসেবে 10.8.0.1 ব্যবহার করা হয়েছে। আপনার নিজস্ব মান বসান।
ব্যর্থতা এক: কোনো কিছুই resolve হয় না, কারণ resolver কখনও উত্তর দেয় না
লক্ষণটি নির্দিষ্ট। ping 1.1.1.1 কাজ করে, আর curl https://example.com এটি ফেরত দেয়:
curl: (6) Could not resolve host: example.comclient থেকে সরাসরি tunnel resolver-কে জিজ্ঞাসা করুন। Ubuntu এবং Debian-এ dig কমান্ডটি dnsutils package থেকে আসে।
dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.comপ্রথম command একটি address ফেরত দেয়। এতে প্রমাণ হয় যে tunnel-এর মাধ্যমে packet Internet-এ পৌঁছাচ্ছে। দ্বিতীয়টি কিছু ফেরত দেয় না এবং ;; communication timed out; no servers could be reached প্রিন্ট করে। পুরো diagnosis এটাই: আপনার client-কে 10.8.0.1 ব্যবহার করার জন্য সেট করা আছে, কিন্তু 10.8.0.1 UDP port 53-এ উত্তর দিচ্ছে না।
এর দুটি কারণ হতে পারে। Server-এ কোনো resolver চালু নেই, অথবা query server-এ পৌঁছানোর আগেই server firewall সেটি drop করছে। Server-এ উভয় বিষয় পরীক্ষা করুন।
sudo ss -ulnp | grep ':53'
sudo nft list rulesetচালু থাকা এবং সঠিকভাবে bind করা resolver-এর output-এ 10.8.0.1:53 অথবা 0.0.0.0:53-সহ একটি line দেখা যায়। Ubuntu-তে সাধারণত সমস্যা হয় 127.0.0.53:53 নিয়ে। এটি systemd-resolved-এর stub listener, যা একটি loopback address-এ bind করে এবং ইচ্ছাকৃতভাবে অন্য machine থেকে unreachable থাকে। যে server-এ একমাত্র resolver হিসেবে এই stub চালু আছে, তার দিকে VPN client নির্দেশ করলে ঠিক এই timeout দেখা যায়।
সমাধান হলো এমন একটি resolver ব্যবহার করা, যা tunnel address-এ listen করে। পাশাপাশি peers যাতে সেখানে পৌঁছাতে পারে, তার জন্য একটি firewall rule যোগ করতে হবে।
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'এরপর শুধু tunnel traffic-এর জন্য port খুলুন। nftables ব্যবহার করলে /etc/nftables.conf-এ থাকা input chain-এ এই দুটি line যোগ করুন এবং sudo systemctl reload nftables দিয়ে reload করুন।
udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" acceptufw ব্যবহার করলে sudo ufw allow in on wg0 to any port 53 একই কাজ করে। Public Internet-এর জন্য কখনও port 53 খুলবেন না। খোলা recursive resolver কয়েক দিনের মধ্যেই scanner-রা শনাক্ত করে এবং denial-of-service attack বাড়াতে ব্যবহার করে। আপনি জানার আগেই আপনার provider সেই traffic শনাক্ত করতে পারে।
client থেকে dig +short @10.8.0.1 example.com আবার চালান। Output-এ একটি address দেখা গেলে resolver path কাজ করছে। এখন client-কে শুধু সেটি ব্যবহার করতে হবে। client-এর [Interface] block-এ line-টি যোগ করুন এবং sudo wg-quick down wg0 && sudo wg-quick up wg0 দিয়ে interface restart করুন।
[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1ব্যর্থতা দুই: DNS leak, কারণ split tunnel resolver-এ route করে না
এটি আরও গুরুতর, কারণ সবকিছু কাজ করছে বলে মনে হয়। নাম resolve হয়, পেজ load হয়, এবং query-গুলো cleartext অবস্থায় সেই local network-এর মধ্য দিয়ে যায়, যেটিকে আপনি trust করতে চাননি।
দুটি configuration এর কারণ। প্রথমটি হলো AllowedIPs = 0.0.0.0/0, ::/0-যুক্ত client, কিন্তু কোনো DNS = line নেই। wg-quick নিজের routing table-এ default route ইনস্টল করে এবং suppress_prefixlength 0-সহ একটি rule যোগ করে। এর ফলে আরও নির্দিষ্ট local route ইচ্ছাকৃতভাবে সক্রিয় থাকে, যাতে machine এখনও তার printer-এ পৌঁছাতে পারে। Client DHCP থেকে শেখা resolver, সাধারণত 192.168.1.1-এ থাকা router, ওই local route-গুলোর একটির সঙ্গে মিলে যায়। আপনার traffic tunnel-এর মধ্য দিয়ে যায়। কিন্তু local network এখনও আপনার lookup করা নামগুলোর সম্পূর্ণ তালিকা পায়।
দ্বিতীয়টি হলো split tunnel: AllowedIPs = 10.8.0.0/24-এর সঙ্গে DNS = 9.9.9.9। 9.9.9.9, AllowedIPs-এর ভিতরে নেই। তাই client-এর কাছে tunnel-এর মধ্য দিয়ে সেখানে যাওয়ার কোনো route থাকে না। ফলে প্রথম ক্ষেত্রের মতোই query local link দিয়ে বেরিয়ে যায়।
কোন resolver সত্যিই উত্তর দিচ্ছে, তা প্রমাণ করুন। whoami.akamai.net একটি public test name। এটি যে recursive resolver query করেছে, তার IP address দিয়ে উত্তর দেয়। তাই আপনি এর উত্তর server-এর public address-এর সঙ্গে তুলনা করতে পারেন।
resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53resolvectl status প্রতিটি link-এর জন্য একটি করে block print করে। আপনার ethernet বা wireless link-এর block-এ যদি এখনও Current DNS Server: 192.168.1.1 দেখা যায়, কিন্তু wg0 block-এ কিছু না থাকে, সেটিই leak। dig +short whoami.akamai.net আপনার server-এর address-এর বদলে home broadband address ফেরত দিলে দূরবর্তী প্রান্ত থেকেও বিষয়টি নিশ্চিত হয়। বিতর্ক নিষ্পত্তির জন্য tcpdump line-ই প্রমাণ: সঠিক output-এ machine থেকে বের হওয়া প্রতিটি port 53 packet wg0-এর মধ্য দিয়ে যায়, আর leak হলে সেগুলো wlan0 বা enp3s0-এর মধ্য দিয়ে যায়। lo-এ 127.0.0.53-এর দিকে বা 127.0.0.53 থেকে যাওয়া packet হলো local systemd-resolved stub-এর সঙ্গে application-এর যোগাযোগ। এগুলো leak নয়।
সমাধানের দুটি অংশ আছে, এবং দুটিই প্রয়োজন। DNS-কে tunnel-এর ভিতরের একটি address-এ সেট করুন। নিশ্চিত করুন, ওই 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 encrypted হয়ে server-এ পাঠানো হয়। split tunnel-এ public resolver ব্যবহার করতেই হলে সেটিকে host route হিসেবে যোগ করুন: AllowedIPs = 10.8.0.0/24, 9.9.9.9/32। এরপর packet tunnel-এর মধ্য দিয়ে যায়। তবে আগের session-গুলো থেকে local network এখনও বুঝতে পারে যে আপনি ওই provider বেছে নিয়েছেন। নিজে চালানো resolver ব্যবহার করলে এই প্রশ্ন এড়ানো যায়।
Resolver assignment হলো হাতে তৈরি WireGuard এবং সমন্বিত mesh-এর দৃশ্যমান পার্থ্যগুলোর একটি। এটি WireGuard এবং Tailscale-এর তুলনা-এ বর্ণিত tradeoff-এর অংশ। নিজে পরিচালিত Headscale control server চালালে third party-কে আপনার key material না দিয়েও এই coordination পাওয়া যায়। শেষ কথাটি যদি আপনার উদ্বেগের কারণ হয়, মনে রাখুন যে Tailscale আপনার traffic encrypt করা key কখনও ধরে রাখে না। আরও গুরুত্বপূর্ণ প্রশ্ন হলো, compromised coordination server বা চুরি হওয়া identity account আপনার network-এ কী অতিরিক্ত প্রবেশাধিকার দিতে পারে।
ব্যর্থতা তিন: Linux client-এ resolvconf এবং systemd-resolved-এর সংঘাত
macOS, Windows, iOS এবং Android client-গুলো official app-এর মাধ্যমে DNS = প্রয়োগ করে এবং সাধারণত খুব কম সমস্যা তৈরি করে। Linux-এ এই setting একটি shell script প্রয়োগ করে, যেটিকে অনুমান করতে হয় আপনি কয়েকটি resolver manager-এর কোনটি ব্যবহার করছেন।
প্রথম ব্যর্থতাটি স্পষ্টভাবে দেখা যায়। sudo wg-quick up wg0 এই বার্তাসহ বন্ধ হয়ে যায়:
resolvconf: command not foundwg-quick, resolvconf-কে ব্যবহার করে, কিন্তু ওই binary ইনস্টল করা নেই। Debian 12 এবং Ubuntu 24.04 বা পরবর্তী সংস্করণে systemd-resolved-এর সঙ্গে কাজ করা resolvconf command-টি systemd-resolved package-এ থাকে। তাই package-টি install করুন, তারপর interface-টি আবার চালু করুন। Ubuntu 22.04-এ এর জন্য আলাদা package নেই। সেখানে sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf ব্যবহার করে compatibility link নিজেই তৈরি করুন এবং install line বাদ দিন।
sudo apt install -y systemd-resolved
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0দ্বিতীয় ব্যর্থতাটি নীরবে ঘটে। এটিই troubleshooting-এ একটি সন্ধ্যা নষ্ট করতে পারে। Interface চালু হয়, resolvectl status wg0 সঠিকভাবে DNS Servers: 10.8.0.1 দেখায়, কিন্তু lookup এখনও পুরোনো resolver-এ পৌঁছায়। systemd-resolved wg0 উপেক্ষা করছে না। কোনো name routing domain-এর সঙ্গে না মিললে, default route হিসেবে গণ্য হওয়া প্রতিটি link-এর DNS server-এ সেটি পাঠানো হয়। DNS server থাকা এবং route-only domain না থাকা একটি link default route হিসেবে গণ্য হয়। তাই query একই সময়ে 10.8.0.1 এবং wireless link-এর router-এ যায়। প্রথম সফল উত্তরটি ব্যবহৃত হয়। Router এক hop দূরে থাকে এবং সাধারণত আগে উত্তর দেয়। যেকোনো ক্ষেত্রেই query ইতিমধ্যে cleartext-এ বাইরে চলে গেছে। Wireless link-এর নিজস্ব search domain-এর অধীন name, যেমন lan, শুধু ওই link-এ যায়, কারণ সবচেয়ে নির্দিষ্ট routing domain অগ্রাধিকার পায়।
যখন wg-quick systemd-এর নিজস্ব resolvconf wrapper-এর মাধ্যমে DNS = প্রয়োগ করে, তখন এটি আগে থেকেই catch-all routing domain চায়। তাই এই ব্যর্থতা সাধারণত ঘটে যখন অন্য কোনো resolvconf implementation বা network manager setting-টি প্রয়োগ করে। এটি স্পষ্টভাবে set করলে অনুমানের প্রয়োজন থাকে না।
একই ধাপে resolver set করুন এবং catch-all routing domain নির্ধারণ করুন। %i interface name-এ রূপান্তরিত হয়, তাই এই 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 = line মুছে দিন। অন্যথায় দুটি mechanism resolver state লিখবে, কিন্তু পরে cleanup করবে শুধু একটি। ~. argument-টিই গুরুত্বপূর্ণ অংশ। এটি wg0-কে প্রতিটি name-এর route-only domain করে। ফলে আরও নির্দিষ্ট কোনো domain-এর সঙ্গে না মেলা name systemd-resolved অন্য link-এ পাঠানো বন্ধ করে। অন্য link-এর নিজস্ব domain-এর অধীন name, যেমন lan, এখনও সেই link-এ যায়। Local host-এর জন্য সাধারণত এটাই প্রত্যাশিত আচরণ। এটি যাচাই করুন।
resolvectl status wg0সঠিক output-এ DNS Servers: 10.8.0.1 এবং DNS Domain: ~. থাকবে। ~. অনুপস্থিত হলে resolvectl domain অংশটি চালানো হয়নি, এবং wg0 আবার অন্য link-এর সঙ্গে প্রতিযোগিতা করছে। Check হিসেবে default route flag ব্যবহার করবেন না। ~. না থাকলেও এটি yes দেখায়, অথবা systemd version অনুযায়ী Protocols line-এ +DefaultRoute দেখায়। কারণ route-only domain না থাকা একটি link এমনিতেই default route হিসেবে গণ্য হয়।
আরেকটি পরিস্থিতির কথাও উল্লেখ করা দরকার। /etc/resolv.conf যদি /run/systemd/resolve/stub-resolv.conf-এর symlink না হয়ে একটি প্রকৃত file হয়, তাহলে অন্য কোনো component সাধারণত NetworkManager বা container runtime এটি নিয়ন্ত্রণ করছে। অন্য কিছু debug করার আগে ls -l /etc/resolv.conf চালান। কারণ network পরিবর্তনের সময় ওই file পুনরায় লেখে এমন কোনো tool আপনার করা পরিবর্তন সবচেয়ে অনুপযুক্ত সময়ে বাতিল করে দেবে।
টানেলের মাধ্যমে নিজের filtering resolver চালানো
কোয়েরিগুলো নির্ভরযোগ্যভাবে টানেলের মধ্য দিয়ে চলাচল করলে দূরের প্রান্তের resolver একটি নিয়ন্ত্রণকেন্দ্র হয়ে ওঠে। সেখানে AdGuard Home চালালে সংযুক্ত প্রতিটি ডিভাইস client software বা প্রতি ডিভাইসের আলাদা configuration ছাড়াই blocklist filtering এবং query log পায়। October 2026-এ যাচাই করা official install script-টি এক লাইনের।
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -vপ্রথমবার চালুর সময় setup wizard port 3000-এ listen করে। সেই port public না খুলে টানেলের মাধ্যমে http://10.8.0.1:3000 ঠিকানায় এটি খুলুন। Wizard-এ DNS listen address এবং admin listen address—দুটিই 10.8.0.1 সেট করুন। Failure one-এর unbound যদি এখনও একই address-এ চালু থাকে, আগে sudo systemctl disable --now unbound দিয়ে সেটি বন্ধ করুন। কারণ একই address-এ UDP port 53 ব্যবহারের জন্য দুটির মধ্যে সংঘাত হবে এবং AdGuard Home listen udp 10.8.0.1:53: bind: address already in use দিয়ে বন্ধ হয়ে যাবে।
Client config-এ কোনো পরিবর্তন দরকার নেই, যদি সেগুলোতে আগে থেকেই DNS = 10.8.0.1 লেখা থাকে। এখন প্রতিটি peer থেকে AdGuard Home-এ পৌঁছানো প্রতিটি lookup query log-এ দেখা যাবে, যদি না আপনি কোনো client-কে logging থেকে বাদ দেন। এটি বিনামূল্যে পাওয়া সুবিধা নয়; এটি প্রকৃত privacy সিদ্ধান্ত। এতে trust আপনার internet provider থেকে আপনার নিজের কাছে স্থানান্তরিত হয়, এবং ওই server patched রাখার দায়িত্বও আপনার। Internet-এ উন্মুক্ত server-এর ক্ষেত্রে আগে মৌলিক নিরাপত্তা ব্যবস্থা নিতে হবে। নতুন VPS-এ প্রথম দশ মিনিট এগুলো ব্যাখ্যা করে।
FAQ
আমার WireGuard tunnel সংযুক্ত হয়, কিন্তু name resolve হয় না কেন?
Tunnel packet বহন করে, কিন্তু name resolution পরিচালনা করে না। তাই tunnel সচল থাকলেও lookup ব্যর্থ হলে বুঝতে হবে, আপনি যে resolver নির্দিষ্ট করেছেন সেটি উত্তর দিচ্ছে না। Client থেকে dig +short @10.8.0.1 example.com দিয়ে পরীক্ষা করুন। communication timed out reply-এর অর্থ হলো ওই tunnel address-এ কোনো resolver listening করছে না। এর সাধারণ কারণ, systemd-resolved stub শুধু 127.0.0.53-এ bind করা আছে। আরেকটি কারণ হতে পারে, server firewall wg0-এ আসা UDP port 53-এর traffic বাদ দিচ্ছে। আগে listener ঠিক করুন। এরপর শুধু wg0-এর জন্য port খুলুন।
আমার DNS WireGuard-এর মাধ্যমে leak হচ্ছে কি না কীভাবে পরীক্ষা করব?
Client-এ sudo tcpdump -ni any -c 10 port 53 চালান এবং browse করার সময় interface column পর্যবেক্ষণ করুন। Machine থেকে বের হওয়া প্রতিটি packet wg0-এ থাকা উচিত। lo-এ 127.0.0.53-এর দিকে বা সেখান থেকে আসা packet স্থানীয় systemd-resolved stub-এর এবং এগুলো স্বাভাবিক। Packet wireless বা ethernet interface-এ দেখা গেলে query cleartext-এ বাইরে যাচ্ছে। dig +short whoami.akamai.net দিয়ে দ্বিতীয়বার যাচাই করা যায়। এটি যে recursive resolver query করেছে, তার public address দিয়ে উত্তর দেয়। তাই উত্তরটি আপনার server-এর address না হলে leak নিশ্চিত হয়।
Split tunnel ব্যবহার করলে DNS = line কি দরকার?
হ্যাঁ। Resolver address-টিও AllowedIPs-এর ভিতরে থাকতে হবে। তা না হলে client-এর কাছে সেখানে যাওয়ার route থাকবে না। AllowedIPs = 10.8.0.0/24 ব্যবহার করলে 10.8.0.1-এ থাকা resolver অন্তর্ভুক্ত হবে এবং query encrypted থাকবে। 9.9.9.9-এর মতো public resolver অন্তর্ভুক্ত নয়। তাই DNS line সঠিক দেখালেও query local link দিয়ে বাইরে চলে যাবে।
resolvectl সঠিক server দেখায়, কিন্তু lookup অন্যত্র যায় কেন?
systemd-resolved এমন name, যার সঙ্গে কোনো routing domain মেলে না, সব default-route link-এ একই সময়ে পাঠায়। এর মধ্যে wg0-ও রয়েছে। এরপর এটি প্রথম সফল উত্তরটি ফেরত দেয়, যা সাধারণত local router থেকে আসে। Client-এর [Interface] block-এ PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. যোগ করুন এবং DNS = line সরিয়ে দিন। এরপর resolvectl status wg0-এ DNS Domain: ~. report হওয়া উচিত। অন্য কোনো link আরও নির্দিষ্ট domain দিয়ে দাবি না করলে প্রতিটি name শুধু wg0-এ যাবে।
একাধিক client-এ সমস্যা হলে প্রথমে কোন client ঠিক করব?
একটি Linux client আগে ঠিক করুন। কারণ mechanism দেখাতে পারে একমাত্র এটিই। resolvectl status এবং tcpdump জানায় কোন resolver উত্তর দিয়েছে এবং কোন interface packet বহন করেছে। Phone ও desktop app কোনো দৃশ্যমান plumbing ছাড়াই একই DNS এবং AllowedIPs value প্রয়োগ করে। তাই Linux client সঠিক হওয়ার পর আপনি এমন একটি configuration কপি করবেন, যা আগে যাচাই করা হয়েছে।