একটি 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, এবং সেটির সঙ্গে মেলান। তখন নিয়মটি রিবুট ও প্যাকেজ আপডেটের পরেও টিকে থাকে। এটি এমন কিছুও হয়ে ওঠে যা নিয়ে সহকর্মী আপনার সেশন-ইতিহাস ছাড়াই ভাবতে পারেন।

আপনি কোন প্রক্রিয়া ব্যবহার করছেন সে সম্পর্কেও সুনির্দিষ্ট হোন। cgroup v1 net_cls শ্রেণিবিন্যাস এবং cgroup v2 socket মিলানো ভিন্ন জিনিস, আর ডকুমেন্টেশনে একই রকম নামের একটি ফিল্ড থাকলেই সেগুলো বিনিময়যোগ্য হয়ে যায় না।

route ধরনের চেইনই দ্বিতীয় লুকআপ চালু করে

স্থানীয়ভাবে তৈরি ট্র্যাফিকের ক্ষেত্রে চেইনের ধরনই ঠিক করে চিহ্ন বসানোর পরে কিছু ঘটবে কি না। filter ধরনের output চেইন একটি প্যাকেট চিহ্নিত করতে পারে এবং কার্নেল আগেই বেছে নেওয়া রুটটিই ব্যবহার করতে থাকবে। 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 নিয়ম উপসর্গ ঠিক করে কিন্তু কোন ট্র্যাফিক কোন পথে গেছে তা বলার ক্ষমতা নষ্ট করে দেয়। চিহ্ন, প্রকৃত আউটপুট ইন্টারফেস এবং সম্ভব হলে অ্যাপ্লিকেশনের নিজস্ব অ্যাড্রেসে মেলান। প্রশস্ত নিয়ম লিখতে সহজ এবং পরে নিরীক্ষা করা প্রায় অসম্ভব।

ব্যতিক্রম থেকে এখনও কী কী বেরিয়ে যায়

নাম রিজলিউশন ভিন্ন cgroup-এ থাকা আলাদা resolver প্রসেস থেকে আসতে পারে, তাই ব্রাউজারের socket শ্রেণিবিন্যস্ত করলে তার লুকআপগুলো শ্রেণিবিন্যস্ত হয় না। যদি resolver-এর কোয়েরি টানেলে থেকে যায় আর সংযোগটি তার বাইরে বেরোয়, তবে split-horizon উত্তরগুলো পথের সঙ্গে মিলতে পারে না। সেই অমিল এমন ব্যর্থতা তৈরি করে যা DNS বাগের মতো দেখায়, কিন্তু তা নয়।

চাইল্ড প্রসেস বেশিরভাগ সেটআপে cgroup উত্তরাধিকার পায়, কিন্তু যে helper নিজেকে নিজের scope-এ সরিয়ে নেয় সে পায় না। অনুমান না করে যাচাই করুন, বিশেষত যা নিজেকে পুনরায় চালায় তার ক্ষেত্রে।

সবশেষে, টানেল বন্ধ থাকলে কী হওয়া উচিত তা ঠিক করুন। যে ব্যতিক্রম চুপচাপ সম্পূর্ণ বাইপাসে পরিণত হয় তা বন্ধ-ব্যর্থতার চেয়ে ভিন্ন নিরাপত্তা অবস্থান, আর দুটির মধ্যে কেবল একটি-ই আপনার উদ্দেশ্য ছিল।

সম্পর্কিত নিবন্ধ