একটি Linux হোস্টে fwmark দিয়ে দুইটি OpenVPN ক্লায়েন্ট চেইন করা
দুই ক্লায়েন্টই ডিফল্ট রুট চায়। হোস্ট রুট দিয়ে ভেতরের সার্ভারকে পিন করুন, দ্বিতীয় ক্লায়েন্টকে টেবিল পুনর্লিখন থেকে বিরত রাখুন, তারপর fwmark দিয়ে ফ্লো পরিচালনা করুন।
একটি Linux হোস্টে দ্বিতীয় OpenVPN ক্লায়েন্ট স্তূপ করলে দুই জায়গার একটিতে ব্যর্থ হয়। হয় ভেতরের ক্লায়েন্ট নিজের সার্ভারে পৌঁছাতে পারে না, কারণ বাইরের টানেল ওই ট্রান্সপোর্ট দখল করে নিয়েছে, নয়তো দ্বিতীয় ক্লায়েন্ট রুট টেবিল পুনর্লিখন করে এবং প্রথম টানেল কাজ করা বন্ধ করে দেয়। দুটি সমস্যাই রাউটিংয়ে, এনক্রিপশনে নয়, এবং নতুন টানেল কোনো অ্যাপ্লিকেশন ট্রাফিক বহনের আগেই দুটোই সমাধান হয়। এখানে ঠিকানাগুলো উদাহরণমাত্র: tun0 বাইরের টানেল, 203.0.113.9 ভেতরের সার্ভার, এবং TCP 9999 হলো সেই ফ্লো যা ভেতরের পথ ব্যবহার করতেই হবে।
দুটি ব্যর্থতার ধরন, একটি রুট টেবিল
ভেতরের ক্লায়েন্ট তার সার্ভারে একটি UDP সকেট খোলে, এবং সেই সকেট অন্য যেকোনোটির মতো চলমান রুট টেবিল অনুসরণ করে। বাইরের টানেলের কাছে ইতিমধ্যে ডিফল্ট থাকলে হ্যান্ডশেক প্যাকেটগুলো বাইরের টানেলে চলে যায়। এটি কেবল তখনই কাজ করে যখন বাইরের সার্ভার ভেতরের সার্ভারে পৌঁছাতে পারে এবং ওপাশের পক্ষ ফ্লোটি বহনে সম্মত হয়; নাহলে হ্যান্ডশেক আটকে যায় এবং ক্লায়েন্ট আবার চেষ্টা করে।
দ্বিতীয় ব্যর্থতা আসে সার্ভারের push থেকে। স্বাভাবিক push হলো redirect-gateway def1, যা ডিফল্ট রুট প্রতিস্থাপনের বদলে 0.0.0.0/1 এবং 128.0.0.0/1 ইনস্টল করে। তাই যে দুই ক্লায়েন্ট নিজ নিজ /1 জোড়া ইনস্টল করে তারা সংঘর্ষে পড়ে, এবং প্রায় প্রতিটি গন্তব্যের জন্য নতুন জোড়াটিই জেতে। দ্বিতীয়টি সংযুক্ত হওয়ার পর কোনো টানেল মরে গেলে টেবিলে ওই দুই প্রিফিক্স খুঁজুন, কারণ ডিফল্ট রুট নিজেই হয়তো কখনো বদলায়নি।
ফ্লো চিহ্নিত করুন এবং তাকে একটি টেবিল দিন
নির্বাচিত ট্রাফিককে mangle টেবিলে চিহ্নিত করে এবং নীতি নিয়মে সেই চিহ্ন মিলিয়ে পরিচালনা করুন। নিয়মটি একটি নির্দিষ্ট টেবিলের দিকে ইঙ্গিত করে যার ডিফল্ট রুট ভেতরের ডিভাইস, তাই কেবল চিহ্নিত প্যাকেটই ভেতরের পথ নেয়।
iptables -t mangle -A PREROUTING -p tcp -m multiport --dport 9999 -j MARK --set-mark 0x9999
ip rule add fwmark 0x9999 table 200 pref 1000
ip route add default dev tun1 table 200এই নিয়মসেটের বেশিরভাগ ব্যর্থতা আসে দুইটি ভুল থেকে। ফায়ারওয়াল যে চিহ্ন মান সেট করে এবং নিয়ম যে মান মেলায় সেগুলো অবশ্যই অভিন্ন হতে হবে, কারণ অমিল কোনো ত্রুটি বা প্রভাব তৈরি করে না। আর PREROUTING কেবল বাইরে থেকে আসা প্যাকেট দেখে, তাই এই হোস্টে শুরু হওয়া সংযোগের চিহ্নিত হতে হলে আগে একই মিলযুক্ত একটি OUTPUT নিয়ম দরকার।
দ্বিতীয় ক্লায়েন্টের কাছ থেকে রুটের মালিকানা কেড়ে নিন
দুইটি অপশন দ্বিতীয় ক্লায়েন্টকে টেবিল সম্পাদনা থেকে বিরত রাখে, এবং তারা ভিন্ন ভিন্ন বিন্দুতে কাজ করে। route-nopull পুশ করা রুট অপশন উপেক্ষা করে, তাই কিছুই ইনস্টল হয় না। route-noexec পুশ করা অপশন ক্লায়েন্টের পরিবেশে গ্রহণ করে কিন্তু টেবিল পরিবর্তন hook-এর ওপর ছেড়ে দেয়, যা আপনি চান যখন সার্ভার কী দিয়েছে তা দেখতে হয়।
Hook তার পরিবেশে পুশ করা মান নিয়ে চলে, এবং এটি কেবল আপনার অনুমোদিত এন্ট্রিগুলো যোগ করা উচিত। এটিকে idempotent করুন: পুনর্নির্মাণের আগে সে যে টেবিলের মালিক সেটি ফ্লাশ করুন, আর ধরে নেবেন না যে আগের রান পরিষ্কার অবস্থা রেখে গেছে। পুনঃসংযোগ hook আবার ডাকে, এবং যে hook পরিষ্কার না করে যোগ করে তা নকল রুট জমায়।
একজন মালিক বেছে নিন এবং তা লিখে রাখুন. যে প্রোফাইল দুই অপশনই সেট করে, বা যে hook ম্যানুয়াল স্ক্রিপ্টের সঙ্গে প্রতিযোগিতা করে, তা এমন টেবিল তৈরি করে যা পরে কেউ ব্যাখ্যা করতে পারে না।
দুই সার্ভার ঠিকানাই পিন করুন
ভেতরের ক্লায়েন্ট সংযুক্ত হওয়ার আগে ভেতরের সার্ভারের বাইরের ডিভাইস দিয়ে একটি হোস্ট রুট দরকার। সেই এন্ট্রিই ভেতরের ট্রান্সপোর্টকে বাইরের টানেলের ভেতরে রাখে, ডিফল্ট অনুসরণ করতে দেয় না।
ip route add 203.0.113.9/32 dev tun0
ip route get 203.0.113.9মূল টেবিলেও বাইরের সার্ভারের জন্য মিলযুক্ত হোস্ট রুট যোগ করুন, নাহলে পুনঃসংযোগ বাইরের ক্লায়েন্টের নিজের ট্রান্সপোর্টকে তার নিজের টানেলে পাঠাতে পারে। ক্রমটি ঐচ্ছিক নয়: সংশ্লিষ্ট ইন্টারফেস তৈরি হওয়ার পরে এই এন্ট্রিগুলো যোগ করুন, অর্থাৎ সংযোগের পরে চলে এমন hook বা স্ক্রিপ্ট দিয়ে। যে রুট বন্ধ ডিভাইসের দিকে ইঙ্গিত করে তা সরানো বা উপেক্ষা করা হয়, তাই বুটে লেখা এন্ট্রি আপনার ধরে নেওয়া ক্রমটি টিকিয়ে রাখে না।
পুরো টেবিল পড়ার বদলে লুকআপ পরীক্ষা করুন। রুট লুকআপের আউটপুট কার্নেল যে ডিভাইস ও সোর্স ঠিকানা ব্যবহার করবে তা বলে দেয়, যা ক্লায়েন্ট তার সকেট খোলার সময় ঠিক যা পায়।
চেইন পরীক্ষা করুন এবং ব্যর্থতা দৃশ্যমান করুন
ভেতরের ক্লায়েন্ট সংযুক্ত হওয়ার সময় বাইরের ইন্টারফেসে ক্যাপচার করুন। ভেতরের হ্যান্ডশেক বাইরের টানেলের ভেতরে এনক্রিপ্টেড পেলোড হিসেবে দেখা উচিত; যদি এটি 203.0.113.9-এ সাধারণ UDP হিসেবে দেখা যায়, তবে হোস্ট রুট অনুপস্থিত এবং চেইন কখনো তৈরি হয়নি।
যে চেইন নিঃশব্দে ভৌত আপলিংকে ফিরে যায় সেটিই সবচেয়ে খারাপ ফল, কারণ ইন্টারফেসের অবস্থা কিছু ঘোষণা করে না এবং ভেতরের ট্রাফিক ভুল সোর্স ঠিকানা থেকে বেরোয়। বাইরের টানেল পড়ে গেলে দ্বিতীয় ক্লায়েন্ট সরাসরি সংযুক্ত হতে পারলে সেই পথ বন্ধ করুন বা বাইরের ইন্টারফেসে বেঁধে দিন, যাতে ব্যর্থতা দৃশ্যমান হয়।
দুই ফ্লো আর একবার নিয়ম ডাম্প দিয়ে শেষ করুন: চিহ্নিত সংযোগ ও অচিহ্নিত সংযোগ ভিন্ন পথ নেওয়া উচিত, এবং ip rule show-এ আপনার ইচ্ছিত অগ্রাধিকার তালিকাভুক্ত হওয়া উচিত। দুটি ফ্লো একই পথ নিলে, চিহ্নটি আপনি যেখানে ভাবছেন সেখানে প্রয়োগ হচ্ছে না।