دسترسی به کانتینرها در شبکه Gluetun
کانتینر متصل به Gluetun فاقد رابط شبکه مستقل است. برای حل مشکل ارتباط، پورتها را در Gluetun منتشر کرده و فقط زیرشبکههای ضروری را برای عبور از تونل VPN مجاز کنید.
وقتی یک کانتینر به شبکه gluetun متصل میشود چه اتفاقی میافتد
کانتینری که network_mode: service:gluetun را تنظیم میکند، هیچ رابط شبکه مستقلی ندارد. این کانتینر به فضای نام شبکه (network namespace) متعلق به gluetun میپیوندد؛ بنابراین انتشار پورتها و قوانین فایروال دیگر ویژگیهای آن کانتینر نیستند، بلکه به ویژگیهای سرویس gluetun تبدیل میشوند. تمام پاسخهای زیر از همین یک واقعیت ناشی میشوند.
فضای نام شبکه، کپی خصوصی هسته از یک پشته شبکه است: شامل رابطهای اختصاصی، جدول مسیریابی، قوانین فایروال و سوکتهای در حال گوش دادن (listening sockets) مخصوص به خود. Docker بهصورت پیشفرض برای هر کانتینر یکی از این فضاها را ایجاد میکند. وقتی شما network_mode: service:gluetun را مینویسید، Docker از آن مرحله صرفنظر کرده و کانتینر جدید را درون فضای نامی قرار میدهد که gluetun از قبل مالک آن است. کانتینر سیستم فایل خود و فایل /etc/hosts خود را حفظ میکند، و مورد دوم بعداً اهمیت پیدا میکند.
شما میتوانید این موضوع را مستقیماً مشاهده کنید.
docker inspect -f '{{.HostConfig.NetworkMode}}' qbittorrentاین دستور container: را به همراه شناسه کانتینر gluetun چاپ میکند، در حالی که یک کانتینر معمولی bridge را چاپ میکرد. این راهنما از جایی ادامه مییابد که مسیریابی ترافیک Docker از طریق VPN با gluetun متوقف شد: تونل کار میکند و اکنون هیچچیز نمیتواند با کانتینر ارتباط برقرار کند.
انتشار پورت روی gluetun، نه روی برنامه
قرار دادن یک بلوک ports: روی سرویسی که network_mode را تنظیم میکند، باعث میشود Docker از ایجاد کانتینر خودداری کند:
Error response from daemon: conflicting options: port publishing and the container type network modeدلیل این امر مستقیم است. انتشار پورت به معنای افزودن یک قانون NAT (ترجمه آدرس شبکه) است که پورت میزبان را به فضای نام شبکه کانتینر هدایت میکند، در حالی که این کانتینر چنین فضایی ندارد. نگاشت پورت را به سرویس gluetun منتقل کنید. شماره پورت تغییر نمیکند، زیرا برنامه همچنان در همان پورت داخل فضای نام مشترک گوش میدهد.
services:
gluetun:
ports:
- "8080:8080/tcp" # qBittorrent web UI
qbittorrent:
network_mode: "service:gluetun"
# no ports: block hereیک بلوک expose: روی سرویس وابسته نیز به همان اندازه بیفایده است و یک بلوک networks: در آنجا یک مانع جدی محسوب میشود: Compose گزارش میدهد که سرویس مقادیر متناقض network_mode و networks را اعلام کرده است و بهطور کلی از بارگذاری فایل خودداری میکند.
یک پیامد این موضوع بعداً نمایان میشود. هر کانتینر در این فضای نام، یک فضای پورت واحد را به اشتراک میگذارد؛ بنابراین دو برنامهای که هر دو بهصورت پیشفرض از پورت 8080 استفاده میکنند، با هم تداخل پیدا کرده و دومی که شروع به کار میکند با خطای address already in use مواجه میشود. یکی از آنها را در تنظیمات اختصاصی خودش تغییر دهید، برای مثال متغیر WEBUI_PORT در ایمیج qBittorrent از LinuxServer، سپس شماره پورت جدید را روی gluetun منتشر کنید.
کانتینرهای پشت gluetun چگونه به یکدیگر دسترسی پیدا میکنند؟
درون فضای نام (namespace)، آنها از قبل یک رابط loopback مشترک دارند. کانتینری که پشت gluetun قرار دارد، بدون درگیر کردن شبکه Docker، از طریق 127.0.0.1:<port> به کانتینر همخانواده خود متصل میشود.
از خارج از فضای نام، کانتینر هیچ نامی ندارد. DNS تعبیهشده در Docker، نام سرویس را به آدرس آن سرویس در یک شبکه تعریفشده توسط کاربر ترجمه میکند، اما این کانتینر در هیچ شبکهای آدرسی ندارد. بنابراین، یک کانتینر معمولی مانند Sonarr نمیتواند کلاینت تورنت را در http://qbittorrent:8080 پیدا کند. دسترسی به آن از طریق http://gluetun:8080 امکانپذیر است، زیرا سوکت در فضای نام gluetun و روی آدرس gluetun در حال گوش دادن است. این موضوع برای کسانی که با نحوه کارکرد شبکهها و نامگذاری سرویسها در Docker Compose آشنا هستند و انتظار دارند روال معمول نامگذاری اعمال شود، تعجبآور است. این روش همچنین بدون نیاز به publish کردن هیچ پورتی روی میزبان (host) کار میکند، زیرا هر دو کانتینر در یک شبکه Compose قرار دارند.
پیش از هرگونه عیبیابی، DNS را بررسی کنید. gluetun حلکننده (resolver) مخصوص خود را اجرا میکند و /etc/resolv.conf را در کانتینر خود بازنویسی میکند، اما /etc/resolv.conf فایلی است که برای هر کانتینر بهصورت مجزا وجود دارد؛ بنابراین فایلی که gluetun نوشته است، همان فایلی نیست که برنامه شما میخواند.
docker exec qbittorrent cat /etc/resolv.confچگونه به سرویسی که روی Docker host اجرا میشود دسترسی پیدا کنم؟
از host.docker.internal استفاده کنید. این کار به دو تنظیم در دو جای مختلف نیاز دارد، زیرا دو بخش متفاوت دچار مشکل هستند.
نام در اولویت است. /etc/hosts برای هر container تعریف میشود، بنابراین ورودی extra_hosts باید در فایل پیکربندی container برنامه قرار بگیرد، نه در gluetun.
prowlarr:
network_mode: "service:gluetun"
extra_hosts:
- "host.docker.internal:host-gateway"host-gateway یک مقدار ویژه است که Docker آن را با آدرس داخلی خودِ host جایگزین میکند. در یک نصب استاندارد Linux Docker، این آدرس همان bridge مربوط به docker0 است که معمولاً 172.17.0.1 میباشد. آدرس خود را با دستور ip -4 addr show docker0 روی VPS تأیید کنید. Docker Desktop این نام را بهصورت خودکار resolve میکند؛ به همین دلیل است که راهنماهایی که برای لپتاپ نوشته شدهاند، خط extra_hosts را نادیده میگیرند و همان فایل در سرور با خطا مواجه میشود.
مسیر (route) در اولویت دوم قرار دارد. افزودن نام فقط به container میگوید که از چه آدرسی استفاده کند. بسته (packet) همچنان از طریق مسیر پیشفرض gluetun، یعنی همان تونل، خارج میشود و فایروال gluetun آن را مسدود میکند. نشانهٔ این وضعیت، اتصالی است که معلق میماند و سپس timeout میدهد، نه اتصالی که رد (refuse) میشود. رد شدن به این معناست که بسته رسیده و پاسخی منفی دریافت کرده است، اما timeout به این معناست که بسته هرگز به مقصد نرسیده است.
gluetun:
environment:
- FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32سپس بررسی کنید که سرویسِ host واقعاً روی آن آدرس در حال گوش دادن (listening) باشد. یک سرور PostgreSQL که فقط به 127.0.0.1 متصل (bind) شده باشد، از هیچ containerای قابل دسترسی نیست (چه تونل فعال باشد چه نباشد)، زیرا 127.0.0.1 در داخل namespace، همان loopback خودِ namespace است. آن را به 172.17.0.1 متصل کنید: این کار باعث میشود اتصالات از سمت containerها پذیرفته شود و در عین حال سرویس روی اینترفیس عمومی قرار نگیرد. این مورد را با دستور ss -lntp | grep 5432 روی host بررسی کنید.
تغییرات واقعی FIREWALL_OUTBOUND_SUBNETS
مستندات gluetun این متغیر را به عنوان زیرشبکههایی که با کاما از هم جدا شدهاند تعریف میکند که gluetun و کانتینرهای متصل به شبکه آن، اجازه دسترسی به آنها را دارند. این تنظیم شامل تغییرات در فایروال و مسیریابی است و هر دو بخش اهمیت دارند. gluetun برای هر زیرشبکه ذکر شده، یک مسیر (route) از طریق gateway پل Docker اضافه میکند تا بستههای مربوط به آن آدرسها به جای تونل، از طریق eth0 ارسال شوند. همچنین این تنظیم فایروال را برای آن زیرشبکهها باز میکند، زیرا در غیر این صورت gluetun ترافیک خروجی که مقصد آن سرور VPN نباشد را مسدود میکند.
مقدار را بدون فاصله بعد از کاماها وارد کنید.
- FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,192.168.1.0/24,100.64.7.9/32دو ویژگی وجود دارد که نادیده گرفتن آنها آسان است. این یک تنظیم در سطح namespace است، بنابراین برای تمام کانتینرهای پشت gluetun اعمال میشود، نه فقط کانتینری که مد نظر شما بوده است. همچنین این تنظیم فقط مربوط به ترافیک خروجی است و اتصالات شروعشده توسط کانتینر را مدیریت میکند. اتصالاتی که به یک پورت منتشر شده (published port) میرسند، مسیر متفاوتی را طی میکنند و نیازی به ثبت در اینجا ندارند.
دسترسی به رابط کاربری وب از طریق یکی از اعضای Tailscale
Tailscale به هر ماشین یک نشانی در 100.64.0.0/10 اختصاص میدهد؛ این بازه برای carrier grade NAT رزرو شده است. هیچیک از این موارد در یک tailnet شخصی هزینهای ندارد، اما محدودیتهای کاربر و دستگاه در پلن رایگان مشخص میکنند که با نیاز افراد دیگر به دسترسی به همان رابطهای کاربری، این وضعیت همچنان برقرار بماند یا نه. از آنجا که صورتحساب بر اساس تعداد کاربران، نه تعداد ماشینها، محاسبه میشود، هزینه واقعی یک tailnet پولی به تعداد افرادی بستگی دارد که دعوت میکنید، نه تعداد containerهایی که برای آنها expose میکنید. این دو مسیر به کارهای متفاوتی نیاز دارند.
دسترسی ورودی سادهتر است. انتشار 8080:8080 روی gluetun، آن پورت را روی تمام آدرسهای میزبان bind میکند و از آنجا که رابط tailscale0 میزبان یکی از آنهاست، یک عضو شبکه با باز کردن http://<machine-name>:8080 به container میرسد. در این مسیر gluetun نقشی ندارد، زیرا قانون NAT داکر روی میزبان و خارج از namespace قرار دارد.
برای اینکه رابط کاربری فقط از طریق tailnet قابل دسترسی باشد، پورت منتشرشده را به جای تمام آدرسها، فقط به آدرس Tailscale میزبان bind کنید.
ports:
- "100.101.102.103:8080:8080/tcp"این آدرس را با استفاده از tailscale ip -4 روی میزبان پیدا کنید. در اینجا، bind کردن کنترل قویتری نسبت به قانون فایروال است، زیرا پورت اصلاً روی رابط عمومی باز نمیشود. اگر ترجیح میدهید به جای میزبان و پورت، از طریق یک نام HTTPS به رابط کاربری دسترسی داشته باشید، tailscale serve میتواند جلوی آن پورت منتشرشده قرار بگیرد، هرچند تفاوت عملکرد serve و funnel در تعیین نهایی دسترسیها مهم است و تنها یکی از این دو، رابط کاربری را در محدوده tailnet نگه میدارد. این روش همچنین مشکل انتشار مستقیم پورتهای داکر و دور زدن ufw را برطرف میکند.
دسترسی خروجی جایی است که FIREWALL_OUTBOUND_SUBNETS دوباره مطرح میشود. اگر یک container نیاز به فراخوانی یک عضو دیگر دارد، آدرس آن عضو را اضافه کنید و به جای کل /10، یک /32 برای هر عضو را ترجیح دهید. اگر دستگاهی که با آن تماس میگیرید در یک شبکه خصوصی قرار دارد که از طریق یک VPS که آن زیرشبکه را برای tailnet شما تبلیغ میکند در دسترس است، به جای آدرس 100.x خودِ روتر، محدوده تبلیغشده را لیست کنید و تأیید کنید که میزبان، آن مسیرها را پذیرفته است. نامهای MagicDNS داخل container حل (resolve) نمیشوند، زیرا container از resolver میزبان استفاده نمیکند؛ بنابراین از آدرس عددی 100.x استفاده کنید یا آن را با یک خط extra_hosts ثابت کنید. همین موضوع هنگام اجرای سرور کنترل Tailscale اختصاصی خود با Headscale نیز صدق میکند.
یک فایل compose کامل برای ساختار رایج
یک کلاینت دانلود پشت VPN، دو رابط کاربری وب که فقط روی tailnet پاسخ میدهند، و یک کانتینر که دیتابیس PostgreSQL در حال اجرا روی میزبان (host) را میخواند.
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "127.0.0.1:8000:8000/tcp" # gluetun control server, host only
- "100.101.102.103:8080:8080/tcp" # qBittorrent UI, tailnet only
- "100.101.102.103:9696:9696/tcp" # Prowlarr UI, tailnet only
volumes:
- ./gluetun:/gluetun
environment:
- VPN_SERVICE_PROVIDER=mullvad
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
- SERVER_CITIES=Amsterdam
- FIREWALL_OUTBOUND_SUBNETS=172.17.0.1/32,100.64.7.9/32
- TZ=Europe/Amsterdam
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
network_mode: "service:gluetun"
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
- WEBUI_PORT=8080
volumes:
- ./qbittorrent:/config
- /srv/downloads:/downloads
depends_on:
gluetun:
condition: service_healthy
restart: unless-stopped
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
network_mode: "service:gluetun"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
- PROWLARR__POSTGRES__HOST=host.docker.internal
- PROWLARR__POSTGRES__PORT=5432
- PROWLARR__POSTGRES__USER=prowlarr
- PROWLARR__POSTGRES__PASSWORD=${PROWLARR_DB_PASSWORD}
- PROWLARR__POSTGRES__MAINDB=prowlarr-main
- PROWLARR__POSTGRES__LOGDB=prowlarr-log
volumes:
- ./prowlarr:/config
depends_on:
gluetun:
condition: service_healthy
restart: unless-stoppedبه جای نام محصولات، به الگوی فایل توجه کنید. هر دو رابط کاربری روی gluetun منتشر شده و به آدرس tailnet میزبان متصل شدهاند، بنابراین فقط روی Tailscale پاسخ میدهند و جای دیگری در دسترس نیستند. فقط Prowlarr دارای خط extra_hosts است، زیرا Prowlarr کانتینری است که host.docker.internal را حل میکند. FIREWALL_OUTBOUND_SUBNETS دو آدرس مجزا را نام میبرد: آدرس bridge داکرِ میزبان، تا Prowlarr بتواند اتصال دیتابیس را برقرار کند، و یک peer در tailnet.
سرور PostgreSQL عمداً در این فایل قرار ندارد. این سرور روی VPS به عنوان یک سرویس سیستمی معمولی در حال اجراست و روی 172.17.0.1:5432 گوش میدهد. این همان لایهبندی در یک پشته arr روی Docker Compose است، با این تفاوت که دیتابیس به خارج از داکر منتقل شده است.
کلید خصوصی WireGuard را خارج از فایل compose نگه دارید. ${WIREGUARD_PRIVATE_KEY} آن را از یک فایل .env در کنار خود میخواند؛ الگویی که در فایلهای env و secrets برای Docker Compose پوشش داده شده است. بند condition: service_healthy از healthcheck که همراه ایمیج gluetun ارائه میشود استفاده میکند، بنابراین تا زمانی که تونل وضعیت up را گزارش نکند، هیچ چیزی شروع نمیشود. بررسیهای سلامت در Compose شکل کلی آن را توضیح میدهند.
انتشار روی تمام آدرسها به جای فقط tailnet
پیشوند آدرس و bindهای پورت را از 0.0.0.0 حذف کنید، که شامل IP عمومی VPS میشود. این کار را فقط پشت فایروالی که کنترل میکنید انجام دهید و ابتدا یادداشت ufw در بالا را مطالعه کنید.
ports:
- "8080:8080/tcp"تأیید عبور ترافیک از تونل
درخواست مشابهی را دو بار اجرا کنید؛ یک بار از داخل namespace و بار دیگر از روی host، سپس نتایج را مقایسه کنید.
docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org
curl -s https://api.ipify.orgدرخواست اول باید آدرس خروجی ارائهدهنده VPN شما را نمایش دهد. درخواست دوم باید آدرس VPS را نشان دهد. اگر این دو آدرس یکسان باشند، ترافیک container از طریق تونل عبور نمیکند و تا زمانی که این مشکل برطرف نشود، سایر اصلاحات این راهنما بیفایده خواهند بود.
جدول مسیریابی (routing table) نشان میدهد چه ترافیکی از تونل عبور میکند و چه ترافیکی خیر.
docker run --rm --network=container:gluetun alpine:3.22 ip route showمسیر پیشفرض (default route) باید به رابط تونل، یعنی tun0 اشاره کند. در زیر آن، باید به ازای هر ورودی در FIREWALL_OUTBOUND_SUBNETS یک مسیر مشاهده کنید که به gateway پل Docker اشاره دارد. هر مسیر دیگری که از طریق eth0 خارج شود، ترافیکی است که VPN را دور میزند.
سرور کنترلی Gluetun همان IP عمومی را روی پورت 8000 در /v1/publicip/ip گزارش میدهد. نسخههای جدید برای مسیرهای سرور کنترلی نیاز به پیکربندی احراز هویت دارند، بنابراین پیش از اتکا به آن، این مورد را تنظیم کنید.
نشت ناشی از یک subnet اشتباه
FIREWALL_OUTBOUND_SUBNETS حفرهای است که شما عمداً در فایروال ایجاد میکنید، بنابراین اندازه این حفره برابر با میزان ریسک آن است. چهار روش برای بیش از حد بزرگ کردن این حفره وجود دارد:
0.0.0.0/0همه چیز را به خارج از تونل میفرستد. دو بررسی IP در بالا، این مورد را در همان اجرای اول شناسایی میکنند، زیرا هر دو آدرس یکسانی را برمیگردانند.- دامنهای وسیعتر از هدف. باز کردن
10.0.0.0/8برای دسترسی به یک ماشین در10.0.1.7، تمام آدرسهایی را که یک همتای تورنت ممکن است در آن دامنه تبلیغ کند نیز باز میکند. از10.0.1.7/32استفاده کنید. - دامنهای که با آدرسهای خودِ تونل همپوشانی دارد. مستندات gluetun هشدار میدهد که این کار باعث میشود gluetun ترافیک VPN را از طریق bridge ارسال کند که این امر باعث اختلال در port forwarding میشود. پیش از باز کردن هر دامنه خصوصی، مقدار
WIREGUARD_ADDRESSESخود را بررسی کنید. 100.64.0.0/10برای Tailscale. این مقدار تقریباً چهار میلیون آدرس را باز میکند تا بتوان به یک همتا دسترسی پیدا کرد. همتایان مورد نیاز خود را به صورت ورودیهای/32لیست کنید.
به یاد داشته باشید که این تنظیم کل namespace را پوشش میدهد. باز کردن یک subnet برای اینکه یک indexer بتواند به یک سرویس میزبان دسترسی پیدا کند، همان subnet را برای کلاینت تورنت که از آن namespace استفاده میکند نیز باز میکند. پس از هر تغییر در این متغیر، بررسی IP عمومی را دوباره انجام دهید، زیرا این تنها آزمایشی است که نشان میدهد آیا تغییر اعمال شده همان چیزی بوده که مد نظر شما بوده است یا خیر.
هنگام راهاندازی مجدد gluetun چه چیزی از کار میافتد
سرویس gluetun مالک namespace است، بنابراین چرخه حیات namespace به چرخه حیات gluetun وابسته است. شروع یک container وابسته در حالی که gluetun خاموش است، بلافاصله با شکست مواجه میشود:
Error response from daemon: cannot join network of a non running containerراهاندازی مجدد gluetun در جای خود، نوعی شکست بیسروصدا است. containerهای وابسته به کار خود ادامه میدهند در حالی که namespaceای که به آن متصل شدهاند در زیر آنها بازسازی میشود، بنابراین docker ps وضعیت همه چیز را سالم گزارش میدهد اما هیچ پاسخی دریافت نمیشود. پس از هر تغییری در سرویس gluetun، به جای راهاندازی مجدد یک بخش، کل گروه را بازسازی کنید.
docker compose up -d --force-recreateهمین موضوع در مورد بهروزرسانی imageها نیز صدق میکند. دریافت (pull) یک image جدید برای gluetun و بازسازی تنها همان سرویس، باعث میشود سایر سرویسها به namespaceای اشاره کنند که دیگر وجود ندارد.
FAQ
چرا Docker خطای "port publishing and the container type network mode" را نمایش میدهد؟
به این دلیل که یک بلوک ports: روی سرویسی قرار دارد که network_mode: service:gluetun را نیز تنظیم کرده است. انتشار یک پورت، یک قانون NAT اضافه میکند که پورت میزبان را به فضای نام شبکه (network namespace) کانتینر هدایت میکند، در حالی که کانتینر در این حالت چنین فضایی ندارد. بلوک ports: را از آن سرویس حذف کنید و همان نگاشت (mapping) را به سرویس gluetun اضافه کنید. شماره پورت ثابت میماند، زیرا برنامه همچنان در همان فضای نام مشترک روی آن پورت گوش میدهد.
سایر کانتینرها چگونه به سرویسی که پشت gluetun قرار دارد دسترسی پیدا میکنند؟
کانتینرهای موجود در یک فضای نام مشترک از طریق 127.0.0.1 به یکدیگر متصل میشوند. کانتینرهای خارج از آن از نام سرویس gluetun استفاده میکنند، بنابراین http://gluetun:8080 کار میکند در حالی که http://qbittorrent:8080 کار نمیکند. کانتینر برنامه هیچ آدرسی در هیچ شبکه Docker ندارد، بنابراین سرور DNS داخلی چیزی برای حل کردن (resolve) نام آن ندارد. تا زمانی که هر دو کانتینر در یک شبکه Compose مشترک باشند، نیازی به انتشار پورت برای این کار نیست.
در FIREWALL_OUTBOUND_SUBNETS چه چیزی باید قرار دهم؟
فقط آدرسهایی که کانتینر پشت gluetun باید اتصال به آنها را آغاز کند، آن هم با دقیقترین حالت ممکن. یک ماشین تکی یک /32 است. دو ورودی رایج، میزبان Docker در 172.17.0.1/32 و یک /32 برای هر همتای Tailscale است که با آن تماس میگیرید. هرگز 0.0.0.0/0 را اضافه نکنید و هرگز محدودهای که با آدرسهای تونل VPN شما همپوشانی دارد را وارد نکنید. اتصالات ورودی به یک پورت منتشر شده، نیازی به ورودی در اینجا ندارند.
چرا کانتینر نمیتواند نامهای Tailscale MagicDNS من را resolve کند؟
MagicDNS با اشاره کردن resolver میزبان به سرور DNS شرکت Tailscale کار میکند، اما کانتینر از resolver میزبان استفاده نمیکند. کانتینر از هر آنچه در /etc/resolv.conf تنظیم شده استفاده میکند که در پشت gluetun، همان تنظیمات DNS خود gluetun است. این موضوع را با docker exec <container> cat /etc/resolv.conf تایید کنید. از آدرس عددی 100.x همتا استفاده کنید یا نام را با یک ورودی extra_hosts روی آن کانتینر ثابت کنید.
چگونه تایید کنم که ترافیک همچنان از طریق VPN عبور میکند؟
یک درخواست از داخل فضای نام و همان درخواست را از روی میزبان اجرا کنید، سپس پاسخها را مقایسه کنید. docker run --rm --network=container:gluetun curlimages/curl:latest -s https://api.ipify.org باید آدرس خروجی ارائهدهنده VPN شما را برگرداند، در حالی که curl -s https://api.ipify.org روی VPS باید آدرس همان VPS را برگرداند. اگر دو پاسخ یکسان باشند، یعنی تونل ترافیک کانتینر را هدایت نمیکند. این بررسی را پس از هر تغییر در FIREWALL_OUTBOUND_SUBNETS دوباره انجام دهید.