UID নিয়ম দিয়ে একটি Linux অ্যাপকে VPN-এ বাঁধার উপায়
UID নিয়ম কেবল স্থানীয় সকেট দেখে, আর চিরচেনা loopback ব্যতিক্রমই টানেলটিকে মেরে ফেলে। আগে অ্যাকাউন্টকে পথ দিন, তারপর ছাঁকুন আর প্রতিটি ধাপ যাচাই করুন।
Linux-এ একটি প্রোগ্রামকে VPN-এ বাঁধা মূলত রাউটিংয়ের সমস্যা, আর ফায়ারওয়াল নিয়ম কেবল পথ বাছাইয়ের পরে কী হয় তা ঠিক করে। যদি টানেল ডিফল্ট রাউটের মালিক হওয়ার আগেই OUTPUT-এ প্রোগ্রামের UID মেলান, তবে নিয়মটি ঠিক সেই ট্রাফিকই ফেলে দেয় যা আপনি রক্ষা করতে চেয়েছিলেন। একই নিয়ম প্রোগ্রামের DNS জিজ্ঞাসার উত্তর দেওয়া রিজলভার প্রসেসটিকেও দেখতে পায় না। ক্রমটি ঠিক করুন, আর একটি অনধিকারহীন অ্যাকাউন্টই নির্ভরযোগ্য স্প্লিট-টানেল নির্বাচক হয়ে ওঠে।
রায়ের আগেই অ্যাকাউন্টকে একটি পথ দিন
মালিক-মিলন কেবল একটি প্রশ্নের উত্তর দেয়: এই UID-এর স্থানীয় সকেট কি প্যাকেটটি তৈরি করেছে? এটি কোনো ইন্টারফেস বাছাই করে না, আর মূল টেবিল যে ট্রাফিক ইতিমধ্যে ভৌত NIC দিয়ে পাঠায় তা সরাতেও পারে না। তাই মিলে যাওয়া পথ ছাড়া একটি UID নিয়ম প্রোগ্রামটিকে ঘুরিয়ে না পাঠিয়ে ব্লক করে।
অ্যাকাউন্টটিকে আগে তার নিজের টেবিলে পাঠান, তারপর ফায়ারওয়ালকে ফলাফল নিশ্চিত করতে দিন। এটি এমন হোস্টে কাজ করে যার iproute2 uidrange বোঝে, যা ip rule help এক লাইনে জানায়। টেবিল 100 টানেল দিয়ে একটি ডিফল্ট রাউট রাখে, আর নিয়মটি কেবল UID 1000-কে ওই টেবিলে পাঠায়। বাকি সব মূল টেবিলে থাকে এবং ভৌত সংযোগ ব্যবহার চালিয়ে যায়।
ip rule add uidrange 1000-1000 table 100 ip route add default dev tun0 table 100
ভিন্ন টানেল দরকার এমন দ্বিতীয় অ্যাকাউন্ট পায় টেবিল 101 আর নিজের নিয়ম, কারণ দুটি প্রোফাইল একই টেবিলে একসাথে ডিফল্ট রাউটের মালিক হতে পারে না। NetworkManager প্রোফাইল সিস্টেম-ব্যাপী, তাই এই অংশটি হাতে থেকেই যায়, আর রিবুটে নিয়মগুলো মুছে যায় যদি কিছু সেগুলো ফিরিয়ে না আনে। টানেল সংজ্ঞার পাশে বুট স্ক্রিপ্টে টেবিলগুলো লিখুন, যাতে পুনঃসংযোগ কোনো অ্যাকাউন্টকে ভুল পথে ফেলে না যায়।
কেন loopback ব্যতিক্রম টানেলটিকে মেরে ফেলে
নিয়মটির বেশিরভাগ নকল করা সংস্করণ loopback দিয়ে না বেরোনো প্রতিটি প্যাকেট ফেলে দেয়। টানেল loopback নয়, তাই ওই রূপ টানেলের ট্রাফিকও ফেলে দেয়, আর ফলটা ফায়ারওয়ালের ভুলের বদলে মৃত VPN-এর মতো দেখায়।
OUTPUT চালানোর সময় নিউক্লিয়াস ইতিমধ্যে একটি নির্গমন ইন্টারফেস বেছে নিয়েছে, তাই -o প্যাকেটের হেডারের বদলে রাউটের উত্তরের সাথে মেলানো হয়। মালিক-মিলন কেবল OUTPUT ও POSTROUTING-এ দেখা হয়, আর ফরওয়ার্ড করা প্যাকেটে এটি পড়ার মতো কোনো সকেট থাকে না। এই মিলে যাওয়া অর্থ হলো ব্যতিক্রমটিকে টানেল ইন্টারফেসের নাম বলতে হবে এবং ওই মিলনকে অস্বীকার করতে হবে, loopback-কে অস্বীকার করার বদলে।
loopback এখনও DROP-এর উপরে নিজের ACCEPT পাওয়ার যোগ্য, কারণ প্রোগ্রামের প্রথম DNS প্যাকেট স্থানীয় স্টাবের কাছে যায় lo দিয়ে। দুটি নিয়ম এই ক্রমে রাখুন, আর অ্যাকাউন্টটি স্টাবে পৌঁছায়, তারপর টানেলে, আর অন্য কোথাও নয়।
iptables -I OUTPUT 1 -o lo -m owner --uid-owner vpnapp -j ACCEPT iptables -A OUTPUT -m owner --uid-owner vpnapp ! -o tun0 -j DROP
IPv6 আলাদা নিয়ম-সমষ্টি, আর কেবল v4-এর জোড়া ip6tables অক্ষত রাখে। যে প্রোগ্রাম AAAA রেকর্ড পছন্দ করে সে তখন DROP-এ কখনও না পৌঁছেই ভৌত সংযোগ দিয়ে বেরিয়ে যাবে। উভয় নিয়ম ip6tables-এ প্রতিফলিত করুন, বা টানেল v6 ঠিকানা না বহন করলে হোস্টে IPv6 বন্ধ করুন। হাতে যোগ করা নিয়মও রিবুটে হারিয়ে যায়, তাই সেগুলো netfilter-persistent save দিয়ে সংরক্ষণ করুন বা একই যুক্তি বুট ইউনিটের লোড করা nftables টেবিল হিসেবে আবার গড়ুন।
DNS উত্তর আসে অন্য প্রসেস থেকে
প্রোগ্রামের 127.0.0.53-এ জিজ্ঞাসা loopback ACCEPT পার হয়, আর ঠিক সেখানেই UID মিলন থামে। স্টাব রিজলভার নিজের সেবা-অ্যাকাউন্ট থেকে উর্ধ্বমুখে ফরওয়ার্ড করে, তাই vpnapp-এ বাঁধা নিয়ম দ্বিতীয় জিজ্ঞাসা কখনও দেখে না। রিজলভার সংযোগের জন্য LAN সার্ভার বাছলে উত্তর ফিরে আসে ভৌত NIC দিয়ে, আর প্রোগ্রামের নিজের প্যাকেট টানেলে থাকে। দৃশ্যমান ফল হলো ভাঙা নামের সাথে কাজ করা টানেল, বা এমন সার্ভারের দেওয়া নাম যা আপনি বাছেননি।
নাম-সমাধান টানেল সংযোগে ঠিক করুন, অ্যাপে নয়। টানেল প্রোফাইলে একটি DNS সার্ভার আর রাউটিং ডোমেইন দিন, তারপর resolvectl query আর resolvectl status tun0 দিয়ে নিশ্চিত করুন যে রিজলভার গুরুত্বপূর্ণ নামগুলোর জন্য এটি ব্যবহার করে। নেটওয়ার্ক নামস্থানের ভিতরে সমস্যাটি মিলিয়ে যায়, কারণ নামস্থানের রিজলভার কেবল টানেল চেনে।
অ্যাকাউন্ট থেকেই নিয়মটি যাচাই করুন
নিউক্লিয়াসকে জিজ্ঞাসা করুন একটি UID কোন পথ পায়, তারপর ওই অ্যাকাউন্ট হিসেবে অনুরোধ করুন আর প্রস্থান-ঠিকানাটি নিজের অ্যাকাউন্টের সাথে তুলনা করুন। দুটোই একই ঠিকানা জানালে, নিয়মটি আপনি যে ট্রাফিক পরীক্ষা করেছেন তার বদলে অন্য কিছুর সাথে মিলছে। নিচের প্রথম কমান্ডটি প্যাকেট না পাঠিয়েই পথ-অনুসন্ধান দেখায়, যা রাউটিং ভুলকে ফায়ারওয়াল ভুল থেকে আলাদা করে।
ip route get 1.1.1.1 uid 1000 sudo -u vpnapp curl -s https://ifconfig.co
কাউন্টার দেখায় কোনো নিয়ম কখনও চালু হয়েছিল কি না। চালান iptables -vnL OUTPUT, প্রতিটি নিয়মের পাশে প্যাকেট-সংখ্যা পড়ুন, আর বাস্তব ট্রাফিকের পরে DROP-এর পাশে শূন্যকে মিলনের ভুল ধরে নিন।
ফরওয়ার্ড করা ট্রাফিক এই নকশার বাইরে থাকে। সেতুর পিছনে থাকা কন্টেইনার বা VM-এর vpnapp-এর মালিকানাধীন স্থানীয় সকেট নেই, তাই মালিক-মিলন কখনও চালু হয় না এবং ওই প্যাকেটগুলো বরং FORWARD নীতি অনুসরণ করে। যদি ওই কাজভার নিশ্চিত করতে হয়, আলাদা করে পরীক্ষা করুন আর সেতু বা কন্টেইনার-ঠিকানায় বাঁধা নীতি লিখুন। নকশাটি টেকে যখন টেবিল, ইন্টারফেস ব্যতিক্রম, রিজলভার আর v6 নিয়ম সবাই একই দিকে ইশারা করে; যেকোনো একটি বাদ দিলে অ্যাকাউন্ট হয় ফাঁস হয়, নয় সংযোগ হারায়।