কন্টেইনার VPN-শুধু API-তে পৌঁছাতে পারছে না? এটিকে হোস্টে প্রক্সি করুন
টানেলটি হোস্টে থাকে, তাই কন্টেইনারের বেসরকারি ঠিকানায় কোনো রুট নেই। একটি সংকীর্ণ রিভার্স প্রক্সি প্রকাশ করুন এবং এটিকে host.docker.internal হিসেবে কল করুন।
আপনার Compose সার্ভিস এমন একটি API-র বিরুদ্ধে সময় শেষ হয়ে যায় যা একই মেশিনের ব্রাউজার কোনো অভিযোগ ছাড়াই খোলে। এই পার্থক্যই সূত্র: যে অনুরোধ কাজ করে তা VPN টানেলযুক্ত হোস্ট থেকে যায়, আর যে অনুরোধ ঝুলে থাকে তা নিজের নেটওয়ার্ক স্ট্যাকযুক্ত কন্টেইনার থেকে যায়। কন্টেইনারের ভিতরে কিছুই টানেলের রুট জানে না, তাই নাম যেভাবেই লেখা হোক বেসরকারি গন্তব্যটি অপ্রাপ্য থাকে। সমাধান হলো হোস্টের মধ্য দিয়ে একটি ইচ্ছাকৃত লাফ, আরও চতুর রুট টেবিল নয়।
দুই পথ কেন আলাদা তা দিয়ে শুরু করুন, তারপর টানেল কোথায় থাকা উচিত তা ঠিক করুন।
কেন হোস্ট কাজ করে আর কন্টেইনার করে না
টানেল তোলা হোস্টকে একটি ইন্টারফেস, কিছু রুট এবং সাধারণত VPN-প্রদত্ত একটি রিজলভার এন্ট্রি দেয়। কন্টেইনার এর কিছুই উত্তরাধিকার পায় না: এটি ইঞ্জিনের ব্রিজে একটি ইন্টারফেস এবং সেই ব্রিজের দিকে নির্দেশ করা ডিফল্ট রুট নিয়ে শুরু করে। হোস্টে ইনস্টল করা রুট ওই নেমস্পেসের ভিতরে চলা কিছুর কাছেই অদৃশ্য।
এজন্যই উপসর্গগুলো শুধু নষ্ট নয়, বরং অসঙ্গত দেখায়। হোস্ট বেসরকারি নামটি রিজলভ করে এবং সংযোগ করে, কারণ নাম ও রুট দুটোই সেখানে আছে। কন্টেইনার তার প্যাকেট এমন বেসরকারি ঠিকানায় পাঠায় যেখানে ব্রিজ পথের কিছুই পৌঁছাতে পারে না, আর অনুরোধটি সময় শেষে ব্যর্থ হয়। কন্টেইনারের hosts ফাইলে নামটি যোগ করলে কিছুই বদলায় না, কারণ কন্টেইনার যেখানে বসে আছে সেখান থেকে ঠিকানাটিই অপ্রাপ্য।
কন্টেইনারের ভিতর থেকে হোস্টে পৌঁছান
কন্টেইনারের ভিতরে যে প্রক্রিয়া localhost-এ ডায়াল করে সে কন্টেইনারকেই ডায়াল করে। Docker Desktop host.docker.internal নামটি প্রকাশ করে যা ইঞ্জিন চালানো মেশিনের দিকে ফিরে নির্দেশ করে; Linux ইঞ্জিনগুলো সার্ভিস না চাইলে এটি অনির্ধারিত রাখে, আর নিচের ম্যাপিং ঠিক তাই করে। এটি থাকলে অ্যাপ্লিকেশন বেসরকারি ঠিকানার বদলে একটি নাম ও পোর্ট লক্ষ্য করে।
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
INTERNAL_API_HOST: "host.docker.internal:8666"টানেল লাফটির হোস্ট দিকেই থাকে, আর সেই ভেরিয়েবলের পোর্টটি হলো যেটিতে প্রক্সি শুনবে। টানেল হারানো কন্টেইনার এখন কখনো না-থাকা রুট চেষ্টা করার বদলে একটি জানা লাফেই ব্যর্থ হয়।
টানেল কোথায় থাকবে তা বেছে নিন
বেসরকারি API দরকার এমন Compose সার্ভিসের জন্য তিনটি রূপ সম্ভব, আর তারা আলাদা হয় কোন প্রক্রিয়া টানেলের মালিক তা নিয়ে। একটি VPN ক্লায়েন্ট কন্টেইনার ইমেজের ভিতরে চলতে পারে, যার জন্য টানেল ডিভাইস উন্মুক্ত করা এবং তার সঙ্গে আসা নেটওয়ার্ক প্রশাসন সক্ষমতা দেওয়া লাগে। অ্যাপ্লিকেশনটি একটি পৃথক টানেল কন্টেইনারের নেটওয়ার্ক নেমস্পেস ভাগ করতে পারে, ফলে দুই প্রক্রিয়াই একটি স্ট্যাক ও একটি রুট টেবিল দেখে। অথবা টানেল হোস্টেই থাকতে পারে, হোস্ট থেকে কন্টেইনারে একটি সংকীর্ণ ফরওয়ার্ডার দিয়ে।
প্রথম দুটি প্রোফাইল ফাইল, পুনঃসংযোগ আচরণ ও রিজলভার নীতি প্রকল্পে টেনে আনে, এবং ফাইলের প্রতিটি সার্ভিসকে টানেলের স্বাস্থ্যের উপর নির্ভরশীল করে তোলে। একটি API কলে এত সংযুক্তি বেশি। একটি মাত্র অভ্যন্তরীণ এন্ডপয়েন্ট পড়া ডেভেলপমেন্ট স্ট্যাকের জন্য হোস্ট-পাশের ফরওয়ার্ডারই সবচেয়ে ছোট পরিবর্তন, যা ক্রেডেনশিয়াল প্ল্যাটফর্ম দল যেখানে পরিচালনা করে সেখানেই রাখে।
হোস্টে একটি সংকীর্ণ পথ প্রকাশ করুন
ফরওয়ার্ডার যা উত্তরাধিকার পায় তা দিয়ে শুরু করুন: এটিকে যে অ্যাকাউন্ট টানেলের মালিক সেই অ্যাকাউন্টে চালান, তাহলে এর বহির্গামী সকেটগুলো হোস্টের রুট ও হোস্টের রিজলভার গ্রহণ করবে। একটি nginx location ব্লক একটি পথ কভার করে।
server {
listen 8666;
location /search/ {
proxy_pass http://10.20.30.40:8090/;
proxy_set_header Host internal-api.example;
proxy_read_timeout 60s;
}
}ঠিকানা ও পোর্টকে অস্থায়ী ধরুন এবং API যে জোড়ায় সত্যিই শোনে তা বসিয়ে দিন। প্রতিটি অতিরিক্ত upstream নিজের location ব্লক পাওয়ার যোগ্য, আর প্রতিটি ব্লকে কেবল সেই পথগুলোই অনুমোদিত হওয়া উচিত যা এই সার্ভিস ডাকে, যাতে প্রক্সি বেসরকারি নেটওয়ার্কে একটি সাধারণ দরজা না হয়ে যায়। যে ক্লায়েন্ট host-gateway দিয়ে আসে তাদের জন্য লিসেনারকে ব্রিজ ঠিকানায় বাঁধুন, ক্লায়েন্ট হোস্ট নেটওয়ার্ক নেমস্পেস ভাগ করলে কেবল loopback-এ, আর কখনোই 0.0.0.0-এ নয়। এমন পোর্টও বেছে নিন যা এখনো মুক্ত: আগেই দখল করা নম্বর প্রক্সিকে শুনতে না দেয় আর কন্টেইনার সময় শেষ হতে থাকে, যা আপনি যে নেটওয়ার্ক ত্রুটি দিয়ে শুরু করেছিলেন তার থেকে আলাদা করা যায় না।
পোর্ট, নাম এবং TLS সিদ্ধান্ত
নাম রিজলিউশন রাউটিংয়ের মতোই ভাগ হয়। কন্টেইনারের পাবলিক লুকআপ ইঞ্জিনের রিজলভার দিয়ে যায়, তাই হোস্টে রিজলভ হওয়া বেসরকারি নাম কন্টেইনারের ভিতরে রিজলভ হবে না। প্রক্সিই সেই উপাদান যা বেসরকারি নামটি রিজলভ করে, কারণ সে হোস্টের রিজলভার উত্তরাধিকার পায়।
TLS হলো সেই সিদ্ধান্ত যা পিছিয়ে দেওয়া হয় আর পরে পরের ব্যর্থতাটি ব্যাখ্যা করে। প্রক্সি বাইট অপরিবর্তিত পাঠালে কন্টেইনার API-র নিজের সার্টিফিকেট দেখে এবং তার ট্রাস্ট স্টোরে অভ্যন্তরীণ CA দরকার হয়। প্রক্সি TLS শেষ করলে সেই আস্থা হোস্টে চলে যায় এবং অ্যাপ্লিকেশন যে host হেডার পায় তা থেকে লিঙ্ক গড়তে শুরু করে। একটি বিন্যাস বেছে নিন এবং প্রতিটি রিসোর্সে তা প্রয়োগ করুন, কারণ অন্যটির দেওয়া রিডাইরেক্ট এমন হোস্টনামের দিকে নির্দেশ করে যা কন্টেইনার কখনো রিজলভ করতে পারবে না।
সার্ভিসের ভিতর থেকে যাচাই করুন
অনুরোধটি হোস্ট থেকে নয়, কন্টেইনার থেকে পাঠান: প্রক্সি পথে একটি সাধারণ curl, তারপর অ্যাপ্লিকেশনের নিজের স্বাস্থ্য পরীক্ষা। প্রক্সি উত্তর দিলেও অ্যাপ্লিকেশন এখনো ব্যর্থ হলে তিনটি জিনিস মিলান: এটি যে বেস URL গড়ে, তার পরিবেশে থাকা যেকোনো প্রক্সি ভেরিয়েবল, এবং যে CA-তে আস্থা রাখে। সবই কন্টেইনারের পরিবেশ ও লগে থাকে, আর যেকোনো একটি ঠিক করলেই VPN অক্ষত থাকে।
বেছে নেওয়া বিন্যাসটি সদ্য থাকতেই প্রকল্পের README-তে লিখে রাখুন। নাহলে পরের যিনি এই সময়-শেষে পড়বেন তিনিও একই দুপুর টানেলটি হোস্টে থাকে তা নতুন করে আবিষ্কার করেই কাটাবেন।