Docker-এ OpenVPN: ন্যূনতম ক্লায়েন্টের প্রয়োজনীয় রানটাইম চুক্তি
একটি পর্যালোচিত ইমেজ, একটি সুরক্ষিত প্রোফাইল, TUN অ্যাক্সেস এবং NET_ADMIN. কনটেইনার কী পায়, কী পাওয়া উচিত নয় এবং টানেল কাজ করছে বলে কীভাবে প্রমাণ করবেন.
একটি কনটেইনারে VPN ক্লায়েন্ট রাখলে আপনি ভেতরে একটি টানেলসহ একটি নেমস্পেস পান, এমন হোস্ট নন যা তার সব ট্রাফিক VPN দিয়ে রুট করে। এগুলো আলাদা প্রকল্প, আর এগুলোকে এক হিসেবে দেখা ঠিক সেভাবেই কনটেইনারগুলোকে অনিচ্ছাকৃতভাবে প্রিভিলেজড করে তোলে। এই রানবুক কেবল প্রথমটিই কভার করে: একটি পর্যালোচিত ইমেজ, একটি সুরক্ষিত প্রোফাইল এবং নিজের নেটওয়ার্ক নেমস্পেসে সীমাবদ্ধ একটি ক্লায়েন্ট প্রসেস।
এমন একটি আর্টিফ্যাক্ট দিয়ে শুরু করুন যা কেউ ইতিমধ্যে অনুমোদন করেছে। নিচের ধাপগুলো এমন ইমেজ ধরে নেয় যাতে openvpn এক্সিকিউটেবল এবং সম্পূর্ণ ক্লায়েন্ট প্রোফাইল আছে, যার সার্টিফিকেট ও কী উপাদান মাউন্টের ভেতরেই সমাধান করা। এগুলো সফটওয়্যার বানায় না, ক্রেডেনশিয়াল তৈরি করে না, আর রুটলেস রানটাইম ও নেটিভ Windows কনটেইনার কভার করে না, যার প্রতিটি উত্তর বদলে দেয়।
কনটেইনার কী পায় আর কী পায় না
প্রোফাইল এবং এটি যা কিছু রেফার করে তা সীমিত অনুমতিসহ একটি স্থানীয় ডিরেক্টরিতে রাখুন, শুধু-পঠন হিসেবে মাউন্ট করা। প্রোফাইলের ভেতরের আপেক্ষিক পাথগুলো ওই মাউন্টের ভেতরেই সমাধান হতে হবে; এটি এমন একটি খুঁটিনাটি যা মানুষ মিস করে যখন সার্টিফিকেট প্রোফাইলের এক ধাপ ওপরে থাকে আর কনটেইনার তা কখনো দেখে না।
services:
vpn:
image: ${VPN_CLIENT_IMAGE:?set the reviewed image reference}
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./vpn:/vpn:ro
working_dir: /vpn
entrypoint: ["openvpn"]
command: ["--config", "/vpn/client.ovpn"]
ওই ফাইল তিনটি জিনিস দেয় এবং আর কিছু চায় না। ইমেজ রেফারেন্স ডাইজেস্ট দিয়ে পিন করা উচিত যদি আপনার ডিপ্লয়মেন্ট নীতি তা বলে, আর প্রোফাইলটি হওয়া উচিত পর্যালোচিতটি, হোস্টে কেউ জায়গায় সম্পাদনা করা একটি কপি নয়।
এখানে শুধু-পঠন মানে কনটেইনার আপনার হোস্ট ফাইল বদলাতে পারে না; এর মানে নয় যে কনটেইনার আপনার মাউন্ট করা প্রাইভেট কী পড়তে পারে না। মাউন্টের পুরো উদ্দেশ্যই এটাই, আর এজন্যই ফাইল অনুমতি, মালিকানা এবং গোপনীয়তা হোস্টে কীভাবে এলো তা এখনো গুরুত্বপূর্ণ। কখনো কমান্ড লাইনে পাসওয়ার্ড দেবেন না বা ইমেজ লেয়ারে সেঁধিয়ে দেবেন না, কারণ দুটোই এমন জায়গায় টিকে থাকে যা পরে পরিষ্কার করতে পারবেন না।
ক্ষমতা, entrypoint এবং তাদের মধ্যবর্তী ফাঁদ
দুটি সেটিংস দরকার এবং তারা সম্পর্কহীন কাজ করে। NET_ADMIN কনটেইনারের নেমস্পেসের ভেতরে ইন্টারফেস ও রুট নাড়াচাড়ার অনুমতি দেয়। /dev/net/tun ডিভাইস এন্ট্রি প্রসেসটিকে আদৌ টানেল ডিভাইস খোলার অনুমতি দেয়। একটিকে অন্যটি ছাড়া দিলে স্পষ্ট ব্যর্থতা হয়, যা সুবিধাজনক: অনুপস্থিত TUN ডিভাইস খোলার সময়ই ব্যর্থ হয়, আর অনুপস্থিত ক্ষমতা ব্যর্থ হয় যখন ক্লায়েন্ট ইন্টারফেস কনফিগার করার চেষ্টা করে।
এর কোনোটিই privileged: true সমর্থন করে না। একটি প্রিভিলেজড কনটেইনার সব ক্ষমতা এবং ডিভাইস অ্যাক্সেস পায়, আর এটি এই ওয়ার্কলোডের প্রয়োজনের চেয়ে অনেক বড় একটি অনুমোদন। রানটাইম প্রিভিলেজ ও ক্ষমতার রেফারেন্স ব্যাখ্যা করে এই অনুমোদন কতদূর পৌঁছায়। কোনো chart বা ব্লগ পোস্ট এটি চাইলে আগে যাচাই করুন যে প্রয়োজনটি আসলে অনুপস্থিত ডিভাইস পাথ ছিল, নাকি অনুপস্থিত ক্ষমতা।
entrypoint ওভাররাইডই হলো কামড় দেওয়ার অংশ। openvpn সরাসরি চালানো সাধারণ ইমেজের জন্য ঠিক, কিন্তু যেকোনো প্রোডাক্ট ইমেজের জন্য ভুল যার স্টার্টআপ স্ক্রিপ্ট ডেমন চালানোর আগে ফায়ারওয়াল নিয়ম, রেজলভার ফাইল বা ক্রেডেনশিয়াল প্রস্তুত করে। ওই স্ক্রিপ্ট ওভাররাইড করলে প্রস্তুতি বাদ পড়ে এবং অর্ধ-কনফিগার করা নেমস্পেসে ওঠা টানেল ডিবাগ করতে হয়।
ক্লায়েন্টকে ফোরগ্রাউন্ডে চালান এবং এটি কনটেইনারের মূল প্রসেস হতে দিন। যে প্রসেস ডেমন হয়ে যায় সে সঙ্গে সঙ্গে বেরিয়ে যায়, কনটেইনার লুপে রিস্টার্ট হয়, আর আপনার প্রয়োজনীয় লগগুলো একইরকম দেখতে রিস্টার্টগুলোর মধ্যে ছড়িয়ে থাকে।
নেমস্পেসের ভেতর থেকে যাচাই করুন, হোস্ট থেকে নয়
স্ট্যাক তুলুন, লগে নিশ্চিত করুন ক্লায়েন্ট সংযুক্ত অবস্থায় পৌঁছেছে, তারপর যে নেমস্পেস আসলে টানেল ধরে আছে তা পরীক্ষা করুন।
docker compose up -d
docker compose logs --tail 40 vpn
docker compose exec vpn ip -br addr
docker compose exec vpn ip route
অ্যাড্রেস তালিকায় পুলের ঠিকানাসহ একটি টানেল ইন্টারফেস দেখা উচিত, আর রুট টেবিলে তার দিকে নির্দেশ করা পুশ করা গন্তব্যগুলো দেখা উচিত। ইন্টারফেস না থাকলে ক্লায়েন্ট কখনো তা তৈরির পর্যায়ে পৌঁছায়নি। এটি থাকলেও যদি কোনো রুট তা ব্যবহার না করে, তবে খোঁজার জায়গা হলো push বা রুট অপশন।
তারপর আপনার প্রয়োজনীয় গন্তব্যটি যে নেমস্পেস টানেল ব্যবহার করার কথা সেখান থেকে, শুধু ping নয় বরং অ্যাপ্লিকেশন দিয়ে পরীক্ষা করুন। নাম সমাধান একটি আলাদা ব্যর্থতা যা স্পষ্টভাবে পরীক্ষা করার যোগ্য: পুশ করা DNS ঠিকানা নিজে থেকে কনটেইনারের রেজলভার আপডেট করে না, আর push উপেক্ষা করা স্বতন্ত্র ক্লায়েন্ট ইমেজ যা কনফিগার করেছে তার মাধ্যমে নাম সমাধান করবে।
বিচ্ছিন্ন অবস্থা চুক্তিরই অংশ
পুনঃসংযোগের আচরণের নিজস্ব পরীক্ষা দরকার, কারণ রিস্টার্ট নীতি প্রমাণ করে প্রসেস রিস্টার্ট হয় আর এর মধ্যে ট্রাফিক কী করে তা নিয়ে কিছু বলে না। টানেল থামান, উদ্দিষ্ট গন্তব্য চেষ্টা করুন এবং দেখুন সংযোগ প্রত্যাখ্যাত হয়, আটকে থাকে, নাকি কনটেইনারের সাধারণ রুট দিয়ে চুপচাপ সফল হয়। শেষ ফলাফলটিই গুরুত্বপূর্ণ, আর তা সুস্থ লগে অদৃশ্য।
যদি ওয়ার্কলোড কখনো সাধারণ পথ ব্যবহার করতে না পারে, তবে ফায়ারওয়াল নিয়ম ও নেমস্পেসের ভেতরে রেডিনেস সংকেত দিয়ে তা স্পষ্টভাবে ডিজাইন করুন। Kill switch হলো এমন নীতি যা আপনি লেখেন ও পরীক্ষা করেন, কনটেইনারে ক্লায়েন্ট চালিয়ে পাওয়া কোনো বৈশিষ্ট্য নয়।
শেষে সীমাটি মনে রাখুন: এই রুটগুলো কনটেইনারের নেমস্পেসে বাস করে এবং হোস্টের রাউটিং টেবিল প্রতিস্থাপন করে না। Docker-এর ফরওয়ার্ডিং, অনুবাদ ও ফায়ারওয়াল নিয়ম বাইরের পথের অংশ থাকে, আর প্রকাশিত পোর্ট VPN ইন্টারফেস নয়।