علامت‌گذاری ترافیک یک cgroup بخش آسان ماجراست. سه تصمیم جداگانه باید هم‌راستا شوند پیش از آنکه یک برنامه از رابط فیزیکی خارج شود و بقیه چیزها در تونل بمانند: کدام socket به استثنا تعلق دارد، کدام جست‌وجوی مسیریابی با این علامت فعال می‌شود، و کدام آدرس مبدأ را بسته هنگام خروج حمل می‌کند. بیشتر تلاش‌های ناموفق اولی را درست انجام می‌دهند، دومی را نیمه‌کاره رها می‌کنند و هرگز به سومی نگاه نمی‌کنند.

سه تصمیم، نه یکی

دسته‌بندی پاسخ می‌دهد چه کسی مستثنی است. مسیریابی پاسخ می‌دهد بسته علامت‌خورده به‌جای آن کجا می‌رود. انتخاب مبدأ پاسخ می‌دهد طرف دور چه می‌بیند و پاسخ باید کجا بازگردد.

این‌ها مستقل از هم شکست می‌خورند. بسته می‌تواند دسته‌بندی شود، به رابط فیزیکی بازمسیریابی شود و باز هم آدرس خصوصی حمل کند که هیچ روتر بالادستی نمی‌تواند آن را برگرداند؛ نتیجه یک پایان مهلت است که دقیقاً شبیه مسدودسازی دیوار آتش به نظر می‌رسد. هیچ چیز در گام دسته‌بندی درباره آن هشدار نمی‌دهد. هیچ چیز در گام مسیریابی هم.

ترتیب هم مهم است. دسته‌بندی هنگام تولید بسته رخ می‌دهد، مسیریابی پس از آن، و ترجمه در نقطه خروج.

اگر نمی‌توانید بگویید کدام‌یک از این سه مرحله خراب است، در حال اشکال‌زدایی نیستید، دارید قاعده‌ها را جابه‌جا می‌کنید و امید دارید.

با cgroup‌ای تطبیق دهید که فردا هم وجود دارد

nftables می‌تواند عضویت یک socket در cgroup v2 را بررسی کند، که واقعاً کارآمد است چون فرایند را دنبال می‌کند نه مقصد را. گیر کار شناسه است. برنامه دسکتاپی که از یک منو اجرا می‌شود غالباً در scope گذرایی زندگی می‌کند که نامش در اجرای بعدی تغییر می‌کند.

nft list ruleset
systemctl status session.slice 2>/dev/null
cat /proc/self/cgroup

کپی کردن نام یک scope از یک نشست و ثابت‌کردن آن قاعده‌ای می‌سازد که امروز کار می‌کند و فردا بی‌صدا از تطبیق بازمی‌ماند. به‌جای آن برنامه را در بافت اجرای آگاهانه و پایدار قرار دهید، مثلاً یک scope یا unit اختصاصی که خودتان کنترل می‌کنید، و با همان تطبیق دهید. آن‌گاه قاعده از راه‌اندازی‌های مجدد و به‌روزرسانی بسته‌ها جان سالم به در می‌برد. همچنین چیزی می‌شود که همکارتان بدون تاریخچه نشست شما بتواند درباره‌اش استدلال کند.

درباره اینکه کدام سازوکار را به کار می‌برید هم دقیق باشید. دسته‌بندی net_cls در cgroup v1 و تطبیق socket در cgroup v2 دو چیز متفاوت‌اند، و یک فیلد با نام مشابه در مستندات آن‌ها را قابل تعویض نمی‌کند.

زنجیره‌ای از نوع route است که جست‌وجوی دوم را فعال می‌کند

برای ترافیک تولیدشده محلی، نوع زنجیره تعیین می‌کند آیا پس از تنظیم علامت چیزی رخ می‌دهد. زنجیره خروجی از نوع filter می‌تواند بسته را علامت بزند و هسته همچنان از مسیری که پیش‌تر انتخاب کرده استفاده می‌کند. زنجیره از نوع route همان سازوکاری است که جست‌وجوی تازه می‌خواهد.

table inet bypass {
  chain mark {
    type route hook output priority mangle; policy accept;
    socket cgroupv2 level 2 "app.slice" meta mark set 0x1 comment "app bypass"
  }
}
ip rule add fwmark 0x1 lookup 200
ip route add default via 203.0.113.1 dev wlan0 table 200

سپس علامت به قاعده سیاستی نیاز دارد که آن را به یک جدول بفرستد، و آن جدول باید کامل باشد: یک مسیر پیش‌فرض، هر مقصد محلی که برنامه هنوز لازم دارد، و دروازه بالادستی که واقعاً پاسخ می‌دهد.

مقدار علامت را آگاهانه انتخاب کنید. WireGuard از fwmark خودش برای ترافیک حمل بیرونی استفاده می‌کند و افتادن تصادفی روی همان عدد می‌تواند استثنای مسیریابی خود تونل را به شکل‌هایی مختل کند که تشخیصشان دردناک است.

آدرس مبدأ از مسیر پیروی نمی‌کند

socket‌ای که پیش‌تر آدرس مبدأ را برای مسیر تونل انتخاب کرده، پس از بازمسیریابی همان آدرس را نگه می‌دارد. هیچ چیز در تغییر مسیریابی آن را بازنویسی نمی‌کند، و به همین دلیل بسته با مبدأ خصوصی‌ای از رابط فیزیکی خارج می‌شود که شبکه بالادست نمی‌تواند آن را برگرداند.

نشانه آشکار برنامه‌ای است که به نظر می‌رسد داده می‌فرستد و هرگز پاسخی نمی‌گیرد، و گزارشی از دیوار آتش که چیزی نشان نمی‌دهد چون بسته هرگز برنگشته است.

اصلاح دقیق، قاعده ترجمه مبدأیی است که به علامت آن برنامه و رابط خروج واقعی‌اش محدود می‌شود و به‌جای جایگزینی NAT موجود، با آن یکپارچه می‌شود:

table inet bypass {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    meta mark 0x1 oifname "wlan0" masquerade
  }
}

استثنا را ترجمه کنید و هیچ چیز دیگر. یک قاعده masquerade فراگیر نشانه را برطرف می‌کند و توانایی شما را برای گفتن اینکه کدام ترافیک کدام مسیر را رفت از بین می‌برد. روی علامت، رابط خروج واقعی و در صورت امکان آدرس خود برنامه تطبیق دهید. قاعده‌های گسترده آسان نوشته می‌شوند و بعداً تقریباً غیرقابل ممیزی‌اند.

چه چیزهایی هنوز از استثنا می‌گریزند

تجزیه نام ممکن است از فرایند resolver جداگانه در cgroup دیگری بیاید، پس دسته‌بندی socket مرورگر جست‌وجوهایش را دسته‌بندی نمی‌کند. اگر پرس‌وجوهای resolver در تونل بمانند در حالی که اتصال بیرون از آن خارج می‌شود، پاسخ‌های split-horizon ممکن است با مسیر نخوانند. این ناسازگاری خرابی‌هایی می‌سازد که شبیه باگ‌های DNS هستند و نیستند.

فرایندهای فرزند در بیشتر تنظیمات cgroup را به ارث می‌برند، اما کمکی که خود را به scope خودش منتقل می‌کند این‌طور نیست. بررسی کنید نه اینکه فرض کنید، به‌ویژه برای هر چیزی که خودش را دوباره اجرا می‌کند.

در پایان تصمیم بگیرید وقتی تونل قطع است چه باید بشود. استثنایی که بی‌صدا به دور زدن کامل تبدیل می‌شود، وضعیت امنیتی متفاوتی از استثنایی است که بسته شکست می‌خورد، و فقط یکی از آن دو همان چیزی است که منظورتان بود.

مقالات مرتبط