mitmproxy, DevTools, Gradle: VPN-এর পেছনে তিনটি স্তর
loopback কখনও টানেলে ঢোকে না। উপসর্গটিকে লিসেনার, ট্রাস্ট স্টোর বা প্যাকেট ফিল্টার হিসেবে আলাদা করুন, আর যে স্তরটি সত্যিই ভেঙেছে সেটিই মেরামত করুন।
VPN সংযুক্ত হয়, আর তিনটি সম্পর্কহীন টুল তিনটি ভিন্নভাবে ব্যর্থ হয় একই বিকেলে। mitmproxy অনুরোধ দেখা বন্ধ করে, Chrome ডিবাগার প্রত্যাখ্যান করে, আর Gradle BindException স্থানীয় সকেট বাঁধার সময় ছুড়ে দেয়। সাধারণ সন্দেহ হলো টানেলটি localhost আটকায়। তা হতে পারে না: 127.0.0.1 কার্নেলের ভেতরে সামলানো হয় এবং কখনও অ্যাডাপ্টারে পৌঁছায় না, গেটওয়ের কথা তো দূরের।
তিনটি স্তর, তিনটি ভিন্ন ভাঙন
ক্লায়েন্টের সঙ্গে যা বদলেছে তা loopback-এর পথ নয়। এটি একটি স্থানীয় প্রক্সি, একটি মেশিন ট্রাস্ট স্টোর আর একটি প্যাকেট ফিল্টার, এবং প্রতিটিই ডেভেলপারের কর্মপ্রবাহের ভিন্ন অংশ ভাঙে। উপসর্গকে ঠিক সারিতে ফেলা মানেই ডায়াগনোসিসের বড় অংশ:
| Symptom | Layer | First check |
|---|---|---|
| Proxy sees no traffic | Local proxy or listener | ss -lntp, or netstat -ano on Windows |
| TLS error in one tool only | Trust store | Which CA bundle does that runtime read? |
| Local bind or connect refused | Packet filter or port owner | Who holds the port, and what rule drops it? |
এই ধরনের প্রায় প্রতিটি প্রতিবেদন শেষ সারি থেকে শুরু করে ওপরে ওঠে, যা উল্টো। টানেলই একমাত্র স্তর যা প্রমাণসিদ্ধভাবে loopback ট্র্যাফিক দেখতে পারে না, তাই তার প্রতি সন্দেহ সবচেয়ে কম হওয়া উচিত। প্রক্রিয়াটি কী বেঁধেছে তা দিয়ে শুরু করুন, তারপর জিজ্ঞাসা করুন এটি কোন সার্টিফিকেট বিশ্বাস করে, তারপরেই মেশিনের নীতিমালা দেখুন।
টানেল নয়, লিসেনার দিয়ে শুরু করুন
যখন 127.0.0.1:8080 ঠিকানায় একটি প্রক্সি ট্র্যাফিক পাওয়া বন্ধ করে, তখন তিনটির একটি ঘটেছে: এটি কখনও শুরু হয়নি, ভিন্ন পোর্ট বেঁধেছে, বা ফিল্টার loopback সংযোগ ফেলে দিয়েছে। শোনা সকেটগুলো তালিকা করলে এক কমান্ডেই উত্তর মেলে, আর দুটি টুল কে আগে ছিল তা নিয়ে দ্বিমত পোষণ করলে কোন প্রক্রিয়া পোর্টটির মালিক তাও দেখায়।
রুট লুকআপে একটি স্থানীয় ঠিকানা অন্য দিক থেকেই একই কথা নিশ্চিত করে। ip route get loopback গন্তব্যের দিকে এটি loopback ডিভাইস রিপোর্ট করে, টানেল কখনও নয়, এবং কেউ সমাধান হিসেবে রাউটিং পরিবর্তনের প্রস্তাব দিলে এটা প্রস্তুত রাখার মতো উপকারী বাক্য।
প্রক্সি স্তর: পরিবেশ মানেই কনফিগারেশন নয়
অনেক কর্পোরেট ক্লায়েন্ট HTTP প্রক্সি চালায় এবং অ্যাপ্লিকেশনগুলো তা ব্যবহার করবে বলে আশা করে। HTTPS_PROXY একটি শেলে সেট করা তা মান্য করা কমান্ড-লাইন টুল ঠিক করে, কিন্তু নিজের সেটিংস ফাইল পড়া টুলের জন্য কিছুই করে না। Gradle, Selenium আর gRPC প্রত্যেকেই নিজের কনফিগারেশন দেখে, আর এক টার্মিনালে এক্সপোর্ট করা প্রক্সি IDE-র চালু করা ডেমন দেখতে পায় না।
কনটেইনার টুলিং-এই এটি সাধারণত চোখে পড়ে। কনটেইনারের loopback তার নিজের, তাই 127.0.0.1 কনটেইনারের ভেতরে কখনও হোস্টের প্রক্সি বোঝায় না; হোস্টের ঠিকানা বা হোস্ট-নেটওয়ার্ক সেটিং দরকার। যে সমাধান শেলে কাজ করে কিন্তু বিল্ডে ব্যর্থ হয়, সেটি সাধারণত এই সারি, টানেল নয়।
ট্রাস্ট স্তর: টুলটি অন্য স্টোর পড়ে
ইন্টারসেপশন দ্বিতীয় পরিবর্তন, আর এটি সবচেয়ে বিভ্রান্তিকর ত্রুটি তৈরি করে। ক্লায়েন্ট পরিদর্শিত সংযোগের জন্য নিজের সার্টিফিকেট উপস্থাপন করে, তাই যে ব্রাউজার কর্পোরেট রুট বিশ্বাস করে সে সফল হয়, অথচ বান্ডল করা CA স্টোর আছে এমন টুল ব্যর্থ হয়। certifi-র মাধ্যমে Python requests ক্লাসিক উদাহরণ: অপারেটিং সিস্টেমের স্টোর ঠিক থাকতে পারে এবং অনুরোধ তবুও ব্যর্থ হয়, কারণ ওই রানটাইম তা কখনও পড়ে না। ত্রুটির লেখায় ইন্টারসেপশনের কথা বিরল, তাই এই সারিটিকে ভাঙা এন্ডপয়েন্ট বলে ভুল নির্ণয় করা হয়।
এন্ডপয়েন্ট আসলে কী উপস্থাপন করে আর টুলটি কী যাচাই করেছে তা মিলিয়ে দেখুন। openssl s_client সার্ভার যে চেইন পাঠিয়েছে তা দেখায়; টুলের নিজের যাচাইয়ের আউটপুট দেখায় সে কী সিদ্ধান্ত নিয়েছে। যখন তারা মেলে না, REQUESTS_CA_BUNDLE বা SSL_CERT_FILE ওই রানটাইমের জন্য লিভার, আর কর্পোরেট রুট সেই স্টোরে থাকা উচিত যা ওই রানটাইম পড়ে, এমন বৈশ্বিক জায়গায় নয় যা কেবল কিছু টুল মানে।
যে ভার্সন পরিবর্তন একটি SSL ত্রুটি সারায় তা ট্রাস্ট ও সাইফার নীতির প্রমাণ, VPN-এর নয়। Python ডাউনগ্রেড যা একটি SOAP ক্লায়েন্ট চালু করল, তা বান্ডল করা CA স্টোর আর ডিফল্ট TLS সেটিংস বদলেছে; নেটওয়ার্ক পথ নড়েনি।
ফিল্টার স্তর: এটি loopback-ও ফেলে দিতে পারে
তৃতীয় পরিবর্তনটি প্যাকেট ফিল্টার, আর কিছু পণ্য এমন নিয়ম বসায় যা টানেলে না ঢোকা ট্র্যাফিক ফেলে দেয়। স্থানীয় লিসেনারও এর মধ্যে থাকতে পারে। এতে কাজ করা সকেট প্রত্যাখ্যাত বা সময়-শেষ হওয়া সংযোগে পরিণত হয়, আর ফলাফল ত্রুটি মেশিনের নীতি নয়, অ্যাপ্লিকেশনের বাগ বলে মনে হয়।
পোর্টের মালিকানা এই স্তরের অন্য অর্ধেক। BindException বোঝায় সকেটটি খোলা যায়নি, যা সংযোগ প্রত্যাখ্যাত হওয়ার চেয়ে ভিন্ন ব্যর্থতা। ফিল্টারকে দোষ দেওয়ার আগে দেখুন অন্য কোনো প্রক্রিয়া পোর্টটি ধরে আছে কি না, তারপর স্থানীয় ট্র্যাফিক ফেলে দেওয়া নিয়ম খুঁজে পণ্যটি যে সর্বনিম্ন অনুমতি সমর্থন করে তা যোগ করুন। একই ডিবাগ পোর্টের জন্য প্রতিযোগী দুটি টুল নীতিগত ব্লকের মতোই হুবহু একই বার্তা দেয়, আর তাদের একটিকেই ফায়ারওয়াল পরিবর্তনে সারানো হয়।
Chrome-এর ডিবাগ পোর্ট একই পরিবারে ব্যর্থ হয়। ব্রাউজার শুরু হয়, ড্রাইভার DevTools এন্ডপয়েন্টে পৌঁছাতে পারে না, আর বার্তায় নেটওয়ার্ক নয়, DevTools-এর নাম থাকে। অতিরিক্ত লঞ্চ ফ্ল্যাগ আর অপ্রয়োজনীয় ড্রাইভার ম্যানেজার কোন প্রক্রিয়া পোর্টের মালিক তা বদলে দেয়, তাই সেগুলো সরালে মাঝে মাঝে VPN সারানো হয়েছে বলে মনে হয়।
যে স্তরটি ভেঙেছে সেটিই মেরামত করুন
টানেল নয়, যে স্তরটি ভেঙেছে সেটিই মেরামত করুন। পৌঁছানো যায় না এমন loopback লিসেনারের লিসেনার বা পোর্ট ঠিক করা দরকার। অবিশ্বস্ত সার্টিফিকেটের জন্য কর্পোরেট রুট ঠিক যে জায়গায় ওই রানটাইম খোঁজে সেখানে ইনস্টল করতে হবে, ব্যতিক্রম সংকীর্ণ রেখে। স্থানীয় ট্র্যাফিক ফেলে দেওয়া ফিল্টারের জন্য নির্দিষ্ট অনুমতি নিয়ম দরকার, নিষ্ক্রিয় ফিল্টার নয়, কারণ নিষ্ক্রিয় করলে এটি যা রক্ষা করছিল তার সবকিছুই আবার খুলে যায়।
VPN আবার সংযুক্ত করা এই স্তরগুলোর একটিও পরীক্ষা করে না। এটি কেবল সেই পরিবর্তনটিই আবার চালায় যা সেগুলো ভেঙেছিল, তাই একই তিনটি ব্যর্থতা প্রতিবার পুনঃসংযোগের পর ফিরে আসে এবং ক্লায়েন্ট নিষ্ক্রিয় রাখে এমন প্রতিটি রিবুটের পর মিলিয়ে যায়।