অ্যাক্সেস লগের প্রতিটি লাইন একই loopback ঠিকানা দেখায়, অথচ পৃষ্ঠাটি লোড হয় এবং সার্টিফিকেট যাচাই সফল হয়। অগম্যতার অর্থে কিছুই ভাঙা নেই, আর ঠিক সেই কারণেই এটি মাসের পর মাস টিকে থাকে। যা ভেঙেছে তা হলো অ্যাট্রিবিউশন: ওয়েব ব্যাকএন্ড যে হপটি সংযোগটি ফরওয়ার্ড করেছে তার ঠিকানা রেকর্ড করে, এবং লগে বিশ্বাস করা প্রতিটি পরবর্তী সিদ্ধান্ত একই ভুল উত্তর উত্তরাধিকারসূত্রে পায়।

নিচের পরীক্ষাগুলো তার থেকে ভেতরের দিকে চলে। যে মানটি আপনি দেখছেন তা ব্যাখ্যা করে এমন প্রথম পরীক্ষায় থামুন, কারণ প্রতিটি পরবর্তী ধাপ ধরে নেয় আগেরগুলো ইতিমধ্যেই সত্য। এগুলোর কোনোটিই ফ্রন্ট-এন্ড কনফিগারেশন অনুমান করতে বলে না।

লগ লাইন নয়, দুটি সংযোগ অনুসরণ করুন

Port-Share একটি TCP লিসেনারে দুটি প্রোটোকল মাল্টিপ্লেক্স করে। OpenVPN আগত সংযোগের প্রথম বাইটগুলো পরীক্ষা করে, সেটি নিজের প্রোটোকলের মতো দেখালে ধরে রাখে, এবং বাকি সবকিছু দ্বিতীয় গন্তব্যে পাঠিয়ে দেয়। সেই গন্তব্য সাধারণত একই হোস্টের একটি ওয়েব সার্ভার, তাই ওয়েব সার্ভারের গৃহীত সকেটের পিয়ার হয় OpenVPN প্রক্রিয়া।

ss -ltnp 'sport = :443'
ss -tnp 'dport = :8080'

প্রথম কমান্ডটি দেখায় পাবলিক লিসেনারটি কার মালিকানাধীন। দ্বিতীয়টি ব্যাকএন্ড পোর্টে আসা সংযোগগুলো দেখায়, এবং তাদের পিয়ার ঠিকানাই সেই ঠিকানা যা অ্যাক্সেস লগ ছাপবে। যদি সেই পিয়ার loopback হয়, লগটি নির্ভুল এবং তথ্যটি এক স্তর আগেই হারিয়েছে। লগ ফিল্ডের নাম পরিবর্তন করা বা ক্লায়েন্ট-IP মডিউল সক্রিয় করা এমন ঠিকানা ফিরিয়ে আনবে না যা সংযোগটি কখনো বহন করেনি।

এই পরীক্ষাটিই বাকি সবকিছু নির্ধারণ করে। যদি ব্যাকএন্ডের পিয়ার ব্রাউজারের ঠিকানা হয়, তবে পোর্ট শেয়ারিং আপনার সমস্যা নয়, লগ ফরম্যাটটিই সমস্যা।

ট্রান্সপোর্ট ফরওয়ার্ডিং হেডার ইনজেকশন নয়

ফরওয়ার্ডার বাইট সরায়। এটি HTTP পার্স করে না, তাই যে অনুরোধকে কখনো অনুরোধ হিসেবে পড়ে না তাতে হেডার ঢোকাতে পারে না, এবং যে TLS স্ট্রিম অপরিবর্তিতভাবে পার করে দেয় তাতে কিছু যোগ করতে পারে না। প্রক্সি হেডার প্রত্যাশী কোনো মডিউল এমন একটি হেডারের জন্য অপেক্ষা করতেই থাকবে যা কোনো উপাদান তৈরি করছে না।

মূল ঠিকানা দুটি ব্যবস্থা বহন করতে পারে, এবং দুটিতেই দুই প্রান্তে সম্মতি প্রয়োজন। PROXY প্রোটোকল সংযোগের শুরুতে একটি ছোট হেডার যোগ করে, এবং ব্যাকএন্ডকে তা প্রত্যাশা করার জন্য কনফিগার করতে হয়; যে ব্যাকএন্ড তা প্রত্যাশা করে না সে ওই বাইটগুলোকে অনুরোধের শুরু হিসেবে ধরবে এবং বিভ্রান্তিকরভাবে ব্যর্থ হবে। X-Forwarded-For-এর মতো বিশ্বস্ত প্রক্সি হেডার কাজ করে যখন HTTP-সচেতন প্রক্সি সেগুলো সেট করে এবং ব্যাকএন্ডকে বলা হয় কোন পিয়ারগুলো তা দাবি করতে পারে।

কোনোটিই স্বয়ংক্রিয় নয়, এবং কোনোটিই ডিফল্টভাবে নিরাপদ নয়। যে ব্যাকএন্ড আগত যেকোনো ফরওয়ার্ডেড হেডারে বিশ্বাস করে সে ক্লায়েন্টকে নিজের ঠিকানা বেছে নিতে দেয়, যা লগের একটি ভুলকে অ্যাক্সেস-নিয়ন্ত্রণ এড়ানোর উপায়ে পরিণত করে। উৎপাদন পথে কোনো হেডারে বিশ্বাস করার আগে একবার স্টেজিং হোস্টে প্রদর্শনটি চালান।

curl -sS -H 'X-Forwarded-For: 127.0.0.1' https://site.example.com/health
grep -F '127.0.0.1' /var/log/nginx/access.log | tail -n 3

ঐচ্ছিক উৎস-ম্যাপিং ডিরেক্টরি

পোর্ট শেয়ারিংয়ের একটি ঐচ্ছিক ডিরেক্টরি আর্গুমেন্ট আছে যা যে প্ল্যাটফর্মে বৈশিষ্ট্যটি বাস্তবায়িত সেখানে মূল উৎস ঠিকানা ও ফরওয়ার্ডিং সংযোগের মধ্যে ম্যাপিং রেকর্ড করে। ওয়েব দিকটি সেই ম্যাপিং দেখবে বলে প্রত্যাশিত; এটি হেডার নয়, এবং এটি অনুরোধে ঢোকানো হয় না।

সেই নকশাটি জানার মতো, কারণ এটি ব্যাখ্যা করে কেন কিছু স্থাপনা প্রকৃত ঠিকানা রিপোর্ট করে আর কিছু করে না, আপাতদৃষ্টিতে অভিন্ন ফ্রন্ট-এন্ড কনফিগারেশন নিয়েও। পার্থক্যটি গ্রহণকারী দিকে, এবং সাধারণত এটি এমন একটি মডিউল বা প্যাচে থাকে যা বেশিরভাগ প্যাকেজকৃত ওয়েব সার্ভার ডিফল্টভাবে চালু করে না। আপনার ওয়েব সার্ভারের ডকুমেন্টেশনে এটি যে সঠিক হুক দেয় তা পড়ুন, এবং অনুমান না করে মডিউলটি লোড হয়েছে তা নিশ্চিত করুন।

এই পথে গেলে এটিকে দুই অংশের ইন্টিগ্রেশন হিসেবে দেখুন: ফরওয়ার্ডার যে ম্যাপিং লেখে এবং ব্যাকএন্ড যে অনুসন্ধান করে। দুটিই যাচাই করুন, এবং নিশ্চিত করুন ডিরেক্টরিটি রিস্টার্টের পরেও টিকে থাকে এবং ব্যস্ত লিসেনারে সীমাহীনভাবে বাড়ে না।

কোন উপাদান ঠিকানার জামিন দেবে তা ঠিক করুন

কনফিগারেশন বদলানোর আগে যে উপাদান ক্লায়েন্ট ঠিকানা দাবি করতে পারে এবং যে উপাদানগুলো তাতে বিশ্বাস করতে পারে তাদের নাম দিন। লিখিত একটি একক উত্তর স্বাভাবিক বিচ্যুতি রোধ করে, যেখানে তিনটি ভিন্ন স্তর তিনটি ভিন্ন উৎসে বিশ্বাস করে।

তিনটি নকশা বেশিরভাগ প্রয়োজন মেটায়। পাবলিক লিসেনার ও ওয়েব লিসেনার আলাদা রাখুন, যাতে ওয়েব সার্ভার প্রকৃত পিয়ার দেখে এবং কোনো অ্যাট্রিবিউশন সমস্যা না থাকে। সামনে HTTP-সচেতন রিভার্স প্রক্সি রাখুন, অনুরোধটি সেখানেই সমাপ্ত বা পার্স করুন, এবং কেবল সেই প্রক্সিতে বিশ্বাস করে এমন ব্যাকএন্ডের জন্য একটি ফরওয়ার্ডেড হেডার সেট করতে দিন। অথবা পোর্ট শেয়ারিং রেখে দিন এবং মেনে নিন যে লগ হপটি দেখায়, অ্যাক্সেস নিয়মগুলো ঠিকানার বদলে অ্যাপ্লিকেশন পরিচয়ের বিরুদ্ধে লেখা থাকবে।

যা কাজ করে না তা হলো ফরওয়ার্ডেড loopback ঠিকানাকে এই প্রমাণ হিসেবে ধরা যে অনুরোধটি এই মেশিনেই উৎপন্ন হয়েছে। এই অনুমান সম্পূর্ণ উল্টো: ঠিকানাটি আছে বলেই যে একটি স্থানীয় উপাদান সংযোগটি তৈরি করেছে। এভাবে লেখা নিয়ম লিসেনারে পৌঁছাতে পারে এমন যে কাউকে গ্রহণ করে।

পৃষ্ঠাটি নয়, নিয়মটিই পরীক্ষা করুন

যে পৃষ্ঠা লোড হয় তা প্রমাণ করে ফরওয়ার্ডিং পথ কাজ করে, অ্যাট্রিবিউশন সম্পর্কে কিছুই বলে না। ঠিকানার ব্যবহারকারীকে পরীক্ষা করুন: অ্যাক্সেস নিয়ম, রেট লিমিট, অডিট এন্ট্রি, জিও লুকআপ।

curl -sS -o /dev/null -w '%{http_code}\n' https://site.example.com/
tail -n 5 /var/log/nginx/access.log
grep -c '' /var/log/nginx/access.log

একটি অনুমোদিত বাহ্যিক অনুরোধকে ফ্রন্ট-এন্ড ও ব্যাকএন্ড উভয় রেকর্ডের সঙ্গে তুলনা করুন, এবং নিশ্চিত করুন যে নিয়মটির অস্বীকার করা উচিত তা এখনো অস্বীকার করে এবং যেটির অনুমোদন করা উচিত তা এখনো অনুমোদন করে। যদি লগ লাইনটি সঠিক হয় কিন্তু নিয়মটি এখনো সবাইকে স্থানীয় বলে আচরণ করে, তবে নিয়মটি আপনি যে ফিল্ডটি ঠিক করেছেন তার চেয়ে ভিন্ন একটি ফিল্ড পড়ছে।

ডিপ্লয়মেন্ট নোটে অ্যাট্রিবিউশন চেইনটি লিখে শেষ করুন: কোন লিসেনার, কোন ফরওয়ার্ডার, কোন হেডার বা ম্যাপিং, এবং কোন ব্যাকএন্ডকে তা বিশ্বাস করতে কনফিগার করা হয়েছে। পরবর্তী যে ব্যক্তি একটি লগ লাইন পড়বে তার চারটিই দরকার হবে, আর সেই নোটের অনুপস্থিতিই একই টিকিট এক বছর পরে আবার খোলার কারণ। এটি চ্যাট থ্রেডে নয়, কনফিগারেশনের পাশে রাখুন।

সম্পর্কিত নিবন্ধ