Docker-এ wg-easy দিয়ে WireGuard web UI সেটআপ
Docker Compose-এ wg-easy চালাতে ports, NET_ADMIN, প্রয়োজনীয় sysctls এবং ফোনে QR code দিয়ে onboarding দেখুন। Version 15-এর settings পরিবর্তনও জানুন।
আপনি যা তৈরি করছেন
wg-easy হলো একটি web interface-সহ WireGuard, যা একটি Docker container হিসেবে চলে। এটি আপনার হয়ে WireGuard interface পরিচালনা করে এবং client তৈরি করার জন্য browser UI যোগ করে। আপনার তৈরি করা প্রতিটি client একটি config file এবং একটি QR code পায়। তাই ফোনের camera screen-এর দিকে ধরলেই VPN-এ যুক্ত করা যায়।
Tunnel-টি নিজে সাধারণ WireGuard। Kernel module packet স্থানান্তর করে, তাই throughput হাতে লেখা setup-এর মতোই থাকে। আপনি যে সুবিধা পান, তা হলো client lifecycle পরিচালনা: SSH-এর মাধ্যমে config file সম্পাদনা না করে peer যোগ করা, disable করা এবং মুছে ফেলা। এর বিনিময়ে config-এর ওপর সরাসরি নিয়ন্ত্রণ কমে যায়। এই বিষয়টি VPS-এ manual WireGuard setup-এ আলোচনা করা হয়েছে।
আপনার একটি public IPv4 address-সহ KVM VPS, Compose plugin-সহ Docker Engine এবং root access প্রয়োজন। OpenVZ বা LXC-এর মতো host kernel ভাগ করে নেওয়া container virtualisation সাধারণত WireGuard module load করতে পারে না। ফলে container interface চালু করতে ব্যর্থ হবে।
Version 15-এ settings environment থেকে সরানো হয়েছে
আপনি যে অধিকাংশ guide পাবেন, সেগুলো wg-easy 14-এর জন্য লেখা। সেখানে WG_HOST-এ আপনার server address এবং PASSWORD_HASH-এ admin password-এর bcrypt hash সেট করতে হতো, দুটিই environment variable হিসেবে। Version 15 সম্পূর্ণ নতুন করে লেখা হয়েছে। Official migration notes-এ স্পষ্ট বলা আছে যে v15, v14-এর একই environment variable ব্যবহার করে না এবং এগুলোর অধিকাংশ web UI-এর admin panel-এ সরানো হয়েছে।
তাই WG_HOST এবং PASSWORD_HASH এখন আর কোনো কাজ করে না। আপনি পুরোনো compose file কপি করলে container চালু হবে, ওই line-গুলো উপেক্ষা করবে এবং browser-এ একটি admin account তৈরি করতে বলবে। এটি bug নয়। এটিই নতুন setup flow।
July 2026 অনুযায়ী pin করার জন্য major tag হলো 15। latest ব্যবহার না করে major version pin করুন, কারণ major upgrade on-disk config format পরিবর্তন করে এবং cleanভাবে rollback করা যায় না।
Compose ফাইল
Stack-এর জন্য একটি directory তৈরি করুন এবং এতে official Compose ফাইলটি লিখুন। এটি upstream ফাইল; এতে কোনো পরিবর্তন করা হয়নি।
sudo mkdir -p /etc/docker/containers/wg-easy
sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml \
https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.ymlবিষয়বস্তুটি এই রকম:
volumes:
etc_wireguard:
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
networks:
wg:
ipv4_address: 10.42.42.42
ipv6_address: fdcc:ad94:bacf:61a3::2a
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
- net.ipv6.conf.default.forwarding=1
networks:
wg:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 10.42.42.0/24
- subnet: fdcc:ad94:bacf:61a3::/64etc_wireguard হলো একটি named volume, যেখানে server key এবং আপনার তৈরি করা প্রতিটি client সংরক্ষিত থাকে। এই volume-এর backup রাখুন। তা না হলে rebuild করলে আপনার সব peer মুছে যাবে। এই ফাইলগুলো host filesystem-এ দেখতে চাইলে এর পরিবর্তে একটি bind mount ব্যবহার করুন। তবে আগে bind mount এবং named volume-এর পার্থক্য পড়ুন, কারণ দুই ক্ষেত্রের permission আলাদা আচরণ করে।
কেন NET_ADMIN, SYS_MODULE এবং sysctl সেটিংগুলো প্রয়োজন
ডিফল্টভাবে কোনো container-কে network stack পরিবর্তন করার অনুমতি দেওয়া হয় না। এই প্রতিটি line একটি নির্দিষ্ট বাধা দূর করে।
NET_ADMIN container-কে wg0 interface তৈরি করতে, এতে address নির্ধারণ করতে এবং route লিখতে দেয়। এটি না থাকলে interface চালু করার সময় container শুরু হয়ে পরে বন্ধ হয়ে যায়, কারণ ip link add wg0 type wireguard Operation not permitted ফেরত দেয়।
SYS_MODULE এবং read-only /lib/modules mount একসঙ্গে container-কে WireGuard kernel module load করতে দেয়, যদি host আগে থেকেই এটি load না করে থাকে। Module-টি image-এর ভিতরে নয়, host kernel-এ থাকে। তাই host directory-টি container-এর কাছে দৃশ্যমান করতে হয়। আধুনিক kernel-এ module-টি সাধারণত built in থাকে। host-এ sudo modprobe wireguard && echo ok চালিয়ে এটি নিশ্চিত করতে পারেন।
net.ipv4.ip_forward=1 kernel-কে এমন packet forward করতে দেয়, যেগুলো নিজে box-টির জন্য নির্ধারিত নয়। এটি না থাকলে client সংযোগ করে, handshake সফল হয়, কিন্তু internet-এর উদ্দেশ্যে পাঠানো প্রতিটি packet drop হয়। ফলে VPN connected দেখালেও ping 1.1.1.1 timeout হয়।
net.ipv4.conf.all.src_valid_mark=1 সেটিংটিই মানুষকে সবচেয়ে বেশি অবাক করে। WireGuard নিজের outgoing packet চিহ্নিত করে, যাতে সেগুলো আবার tunnel-এর ভিতর route না হয়। Strict reverse path filtering এমন একটি packet শনাক্ত করে, যার source address প্রত্যাশিত route-এর সঙ্গে মেলে না, এবং সেটি drop করে। এই sysctl kernel-কে marked packet গ্রহণ করতে বলে। এর ফলে full tunnel নিজেকেই অকার্যকর করে না।
এটি চালু করুন এবং admin account তৈরি করুন
cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -fstart এবং stop ব্যবহার না করে docker compose up এবং docker compose down ব্যবহার করুন। ভিন্ন settings-এ তৈরি করা container-এর ওপর start চালালে network-এর অবস্থা অসংগত হয়ে যায় বলে Upstream সতর্ক করেছে। Reboot-এর পরে stack আবার চালু করতে চাইলে restart: unless-stopped-এর মধ্যেই সেই ব্যবস্থা আছে, আর compose service-এর boot আচরণ-এ এই policy কী করে এবং কী করে না, তা ব্যাখ্যা করা হয়েছে।
Web UI TCP 51821-এ listening করে। প্রথমবার visit করলে একটি setup page দেখা যায়। সেখানে admin account তৈরি করতে হবে এবং client-রা server-এ পৌঁছাতে যে host address ব্যবহার করবে, তা নিশ্চিত করতে হবে। এই host address প্রতিটি client config-এর Endpoint line-এ বসে। তাই এটি VPS-এর public IP অথবা DNS name হতে হবে। এটি ভুল হলে ফোনে দেওয়া QR code এমন ঠিকানায় নির্দেশ করবে যেখানে পৌঁছানো যায় না, এবং handshake কখনো সম্পন্ন হবে না।
এই port সম্পর্কে আরও একটি বিষয় আছে: INSECURE=true set না করলে wg-easy 15 plain HTTP প্রত্যাখ্যান করে। Untrusted certificate ব্যবহার করে HTTPS-এর মাধ্যমে এতে পৌঁছানো, অথবা সামনে reverse proxy রেখে সেখানে TLS termination করা—দুটিই ঠিক আছে। Default settings-এ http:// ব্যবহার করে এতে পৌঁছানো ঠিক নয়।
UI port Internet-এ প্রকাশ করবেন না
Compose ফাইলটি প্রতিটি interface-এ 51821 port প্রকাশ করে। এটি এমন একটি box-এর login page, যা আপনার network traffic route করতে পারে। তাই এটি সবার জন্য উন্মুক্ত রাখা উচিত নয়। Docker-এ port প্রকাশ করলে DOCKER chain-এ rule লেখা হয়। এই chain ufw-এর আগে মূল্যায়ন করা হয়। তাই ufw deny rule ব্যবহার করলেও port-টি বন্ধ হয় না। এই বিষয়টি আলাদাভাবে বোঝা গুরুত্বপূর্ণ। Docker-এর প্রকাশিত port কেন ufw-এর নিয়ম উপেক্ষা করে নিবন্ধে এর সম্পূর্ণ ব্যাখ্যা আছে।
সহজ সমাধান হলো UI-কে loopback-এ bind করা এবং SSH tunnel-এর মাধ্যমে সেখানে প্রবেশ করা:
ports:
- "51820:51820/udp"
- "127.0.0.1:51821:51821/tcp"
environment:
- INSECURE=trueএরপর আপনার laptop থেকে:
ssh -L 51821:127.0.0.1:51821 youruser@your.server.addressআপনার laptop-এর browser-এ http://127.0.0.1:51821 খুলুন। SSH traffic-টি encrypted রাখে। অন্য কোনো client এই port-এ সাড়া পাবে না। এখানে INSECURE=true নিরাপদ, কারণ plain HTTP hop কখনো loopback interface ছেড়ে যায় না।
UDP 51820 port খুলুন এবং উভয় firewall পরীক্ষা করুন
WireGuard-এর জন্য Internet থেকে UDP 51820-এ পৌঁছানো সম্ভব হতে হবে। Docker portটি publish করে, কিন্তু অনেক provider VPS-এর সামনে আলাদা network firewall রাখে, যা Docker নিয়ন্ত্রণ করতে পারে না। উভয় জায়গাতেই portটি খুলুন। আপনি যদি ufw দিয়ে host firewall পরিচালনা করেন, তাহলে হাতে nftables লেখার চেয়ে VPS-এর জন্য প্রাথমিক ufw rule ব্যবহার করা সহজ।
Containerটি সত্যিই listen করছে কি না পরীক্ষা করুন:
sudo ss -ulnp | grep 51820আপনার একটি listening UDP socket দেখা উচিত। ওই লাইনে কিছু না থাকলে container interface চালু করতে পারেনি, এবং sudo docker compose logs wg-easy কারণটি দেখাবে।
একটি client তৈরি করে ফোনে scan করুন
UI-তে একটি client তৈরি করুন এবং এমন একটি নাম দিন যা পরে চিনতে পারবেন, যেমন এটি যে device-এর জন্য ব্যবহার করা হবে তার নাম। wg-easy পরবর্তী খালি tunnel address বরাদ্দ করে এবং আপনার জন্য key pair তৈরি করে। প্রতিটি client row-তে একটি QR code এবং download করার জন্য .conf file থাকে।
ফোনে অফিসিয়াল WireGuard app install করুন। QR code থেকে tunnel যোগ করার option বেছে নিন এবং screen-এ দেখানো code-এর দিকে camera ধরুন। Tunnel-টি আপনার দেওয়া নামসহ দেখা যাবে। এটি চালু করুন। এরপর UI-র client row-তে transfer counter এবং সাম্প্রতিক handshake time দেখা শুরু হবে। ফোন tunnel-এ যুক্ত হলে এমন service-এ পৌঁছাতে পারবে যেগুলো আপনি Internet-এ প্রকাশ করেননি। এইভাবে কোনো server-এর একটি port-ও public Internet-এর জন্য খোলা না রেখেও ফোন যেকোনো স্থান থেকে একটি self-hosted photo server-এ upload চালিয়ে যেতে পারে। একই পদ্ধতি media-এর ক্ষেত্রেও কাজ করে। 90s-এর video store হিসেবে পুনর্নির্মিত একটি Jellyfin library hotel room থেকে browse করা যায়, কিন্তু এটি আপনার LAN-এ থাকা অবস্থার মতোই private থাকে। একই tunnel-এ alert বিপরীত দিকেও কাজ করে। একটি self-hosted ntfy server কোনো backup job ব্যর্থ হওয়ার সঙ্গে সঙ্গেই ফোনে message push করতে পারে, public Internet থেকে কোনো request গ্রহণ না করেই।
কোনো client চালু করার পর handshake না দেখালে client-টি server-এ একেবারেই পৌঁছাচ্ছে না। এর কারণ সাধারণত UDP 51820, যা provider firewall অথবা configuration-এ সংরক্ষিত endpoint address-এ সমস্যা থাকার নির্দেশ করে। Handshake দেখা গেলেও Internet কাজ না করলে forwarding অথবা DNS-এ সমস্যা থাকার সম্ভাবনা বেশি।
Desktop-এ .conf file download করে আবার type না করে WireGuard client-এ import করুন। ওই file-এর private key একবারই তৈরি হয় এবং একবারই দেখানো হয়। SSH private key-এর মতোই এই file নিরাপদে সংরক্ষণ করুন।
কখন UI-এর সীমা অতিক্রম করবেন
আপনার peers যদি ব্যক্তি ও ফোন হয়, তাহলে wg-easy উপযুক্ত tool। Configuration file সম্পাদনার চেয়ে UI দ্রুত, এবং হারানো ফোনের access revoke করতে এক ক্লিকই যথেষ্ট।
UI-তে যেসব বিষয়ের মডেল নেই, সেগুলো চাইলে এর সীমায় পৌঁছাবেন। Site-to-site routing, যেখানে কোনো peer-এর AllowedIPs একটি মাত্র address-এর বদলে পুরো remote subnet নির্দিষ্ট করে, সাধারণত প্রথম বড় সীমা। Per-peer routing rule-সহ split tunnel, অথবা আপনার provisioning tool দ্বারা তৈরি configuration, পরের সীমা। তখন হাতে লেখা setup কঠিন নয়; এটি শুধু ভিন্ন পদ্ধতি। সাধারণ WireGuard guide-এ wg0.conf ব্যবহার করে একই tunnel তৈরি দেখানো হয়েছে। আপনি যদি control plane চালানোই বন্ধ করতে চান, তাহলে WireGuard ও Tailscale-এর তুলনা managed option-টি ব্যাখ্যা করে। এটি উপযুক্ত বিনিময় কি না, তা নির্ভর করে coordination server বাস্তবে কোন network-এ পৌঁছাতে পারে তার ওপর। আপনার network-এর দায়িত্ব দেওয়ার আগে Tailscale-এর trust model পড়া উচিত। পরের প্রশ্ন সাধারণত খরচ নিয়ে। Tailscale-এর free plan-এ আসলে কী অন্তর্ভুক্ত তা এমন যে একটি পরিবার বা ছোট team-এর কোনো খরচ নাও হতে পারে। এর পর billing device-এর বদলে user গণনা করে। আপনি আগে থেকেই যে VPS-এর জন্য অর্থ দিচ্ছেন, তার billing model থেকে এটি ভিন্ন। তাই কোনো team migrate করার আগে free plan-এর সীমা অতিক্রম করলে Tailscale-এর খরচ কত দেখে নিন। আপনি যে full tunnel তৈরি করেছেন, তার সেখানে সরাসরি সমতুল্য ব্যবস্থা আছে। VPS-কে Tailscale exit node হিসেবে advertise করা হলে server-এর মাধ্যমে একই route ব্যবহার করা যায়। পার্থক্য হলো, সেটি প্রতিটি client configuration-এ লেখার বদলে admin console-এ অনুমোদন করা হয়। Subnet-এর ক্ষেত্রেও সমতুল্য ব্যবস্থা আছে। VPS থেকে পুরো private network advertise করা হলে per-peer AllowedIPs সম্পাদনা ছাড়াই tailnet-এর প্রতিটি device সেই network ব্যবহার করতে পারে। আপনি যদি সেই dashboard এবং automatic mesh routing চান, কিন্তু অন্য কারও coordination server না চান, তাহলে VPS-এ নিজের NetBird server চালানো control plane আপনার মালিকানাধীন hardware-এ রাখে। এর বিনিময়ে DNS ও TLS setup করতে হবে, যা wg-easy আপনার কাছে কখনও চায়নি।
উপরের compose syntax-ই যদি অপরিচিত অংশ হয়ে থাকে, WireGuard নয়, তাহলে VPS-এ Docker Compose-এর প্রাথমিক বিষয় file format এবং প্রতিদিনের command-গুলো ব্যাখ্যা করে।
FAQ
wg-easy কেন আমার WG_HOST এবং PASSWORD_HASH উপেক্ষা করছে?
এই variable-গুলো wg-easy 14-এর জন্য প্রযোজ্য। Version 15 নতুন করে লেখা হয়েছে, এবং upstream প্রায় সব configuration web UI-এর admin panel-এ সরিয়ে নিয়েছে। Container কোনো variable-ই পড়ে না। তাই এটি স্বাভাবিকভাবে শুরু হয় এবং প্রথমবার visit করলে আপনাকে admin account তৈরি করতে বলে। Setup page-এ client-এর ব্যবহারের জন্য host address সেট করুন।
আমার kernel-এ WireGuard থাকলে কি SYS_MODULE দরকার?
না। SYS_MODULE এবং /lib/modules mount-এর উদ্দেশ্য হলো host-এ module না থাকলে container যেন তা load করতে পারে। যে host-এ sudo modprobe wireguard আগে থেকেই সফল হয়, সেখানে এই capability ব্যবহার হয় না। এটি সরিয়ে নেওয়া একটি যুক্তিসংগত hardening step। তবে NET_ADMIN যেকোনো অবস্থাতেই প্রয়োজন।
Client connect করছে, কিন্তু Internet নেই। সমস্যা কোথায়?
Traffic ছাড়া handshake প্রায় সবসময় forwarding সমস্যার ইঙ্গিত দেয়। net.ipv4.ip_forward=1 এবং net.ipv4.conf.all.src_valid_mark=1 এখনও compose file-এ আছে কি না নিশ্চিত করুন, কারণ হাতে সম্পাদিত copy-তে এগুলো প্রায়ই বাদ পড়ে। Forwarding চালু থাকলে client যে DNS server পেয়েছে, সেটি পরীক্ষা করুন। এমন একটি tunnel, যা সব traffic VPN-এর মাধ্যমে পাঠায় কিন্তু এমন DNS server নির্দেশ করে যেটিতে এটি আর পৌঁছাতে পারে না, browser-এ মৃত connection-এর মতোই দেখা যায়।
Client-গুলোর backup কীভাবে নেব?
সব data etc_wireguard named volume-এ, একটি wg0.json file-এ থাকে। UI-তে একটি backup button-ও আছে, যা একই data export করে। Upgrade করার আগে ওই file-এর একটি copy server-এর বাইরে সংরক্ষণ করুন। নতুন container-এর setup ধাপে upload করার মাধ্যমে restore করা যায়।
Reverse proxy-এর পেছনে কি wg-easy চালাতে পারি?
হ্যাঁ। TCP 51821-এর সামনে proxy বসান এবং সেখানেই TLS termination করুন। Container-এ INSECURE=true সেট করুন, যাতে এটি proxy থেকে আসা plain HTTP hop গ্রহণ করে। UDP 51820 সরাসরি publish রাখুন, কারণ VPN traffic UDP এবং HTTP proxy-এর মধ্য দিয়ে যায় না।