خطای client-connect در OpenVPN: آیا پاکسازی در disconnect تضمین شده است؟
کلاینت ردشده قاعده فایروال یا رکورد نوشتهشده را برنمیگرداند. قرارداد callback را با بازگردانی که hook شما باید خودش انجام دهد بسنجید.
وقتی یک اسکریپت client-connect با کد غیرصفر خارج میشود، OpenVPN کلاینت را رد میکند و اپراتورها معمولاً فرض میکنند اسکریپت client-disconnect متناظر همهچیز را جمع میکند. اغلب هرگز اجرا نمیشود، چون پردازش disconnect به این وابسته است که مرحله connect چه چیزی را توانسته کامل کند. یکسان دانستن رد و بازگردانی همان چیزی است که یک ورود ناموفق را به قاعده فایروال نشتکرده یا رکورد بیصاحب تبدیل میکند. اینها دو تعهد جداگانهاند و تنها یکی از آنها خودکار است. پیش از سرزنش سرور برای باقی گذاشتن وضعیت، مسیر خروج خود hook را بخوانید.
خروج غیرصفر کلاینت را رد میکند؛ کار شما را برنمیگرداند
وضعیت خروج client-connect یک تصمیم پذیرش است. صفر یعنی پذیرش، هر چیز دیگر یعنی فروپاشی نمونه پیش از آنکه کاملاً برپا شود. هیچچیز در آن قرارداد بررسی نمیکند که اسکریپت پیشتر با دنیای بیرون چه کرده است. اگر اسکریپت قاعدهای به iptables افزوده، سطری نوشته یا شمارندهای را بالا برده باشد، آن تغییرها از رد جان سالم به در میبرند.
همین عدم تقارن برای افزونهها هم صدق میکند، جایی که چند تابع connect میتوانند برای یک نمونه اجرا شوند. شکست بعدی موفقیت پیشین را با اثر رجعی باطل نمیکند، و تابع پیشین هیچ راهی ندارد که بفهمد تابع خواهر شکست خورده است.
رد خودکار است، پاکسازی نه
راهنمای OpenVPN 2.6 پردازش client-disconnect را به موفقیت پردازش connect پیشین گره میزند. یک استثنای مستند زنجیرههای آبشاری را پوشش میدهد: اگر یک گام connect پیشتر موفقیت برگردانده باشد، گامهای disconnect متناظر هنگام فروپاشی نمونه باز هم اجرا میشوند، حتی اگر گام connect بعدی شکست خورده باشد. آن استثنا باریک است و به شمار callbackهای پیکربندیشده بستگی دارد، از این رو آزمودن یک اسکریپت shell هیچ چیزی درباره یک زنجیره به شما نمیگوید.
پیش از طراحی بر پایه آن، پیکربندی واقعی را نقشهبرداری کنید: کدام اسکریپت، کدام افزونه و به چه ترتیبی اجرا میشوند. جدول زیر فهرست میکند که یک راهاندازی نیمهکاره چه چیزی برجای میگذارد.
| What the hook did before failing | What the client sees | What stays behind |
|---|---|---|
| Created a firewall entry | A rejection, with no tunnel | A rule matching an address nobody owns |
| Wrote a state row | A rejection, with no tunnel | A record of a session that never started |
| Allocated a tunnel address | A rejection, with no tunnel | An address marked busy until something releases it |
هر سطر برای کلاینت یکسان و برای میزبان متفاوت پایان مییابد. کلاینت نمیتواند بگوید کدامیک رخ داده است، پس hook باید پیش از برگرداندن وضعیت شکست، آنچه انجام داده را برگرداند.
در همان اسکریپتی که شکست خورده جبران کنید
پیش از دست زدن به وضعیت مشترک هر چه میتوانید اعتبارسنجی کنید: فیلدهای گواهی، دسترسپذیری نشانی، گروهی که کلاینت به آن تعلق دارد. ارزانترین رد همان است که پیش از هر تخصیصی رخ میدهد. وقتی راهاندازی چند عملیات بیرونی را در بر میگیرد، ثبت کنید کدامها کامل شدهاند و پیش از برگرداندن وضعیت شکست، آنها را به ترتیب معکوس برگردانید.
یک نمونه مشخص: اسکریپت یک ورودی فایروال برای هر کلاینت میسازد، سپس در نوشتن سطر وضعیتی که آن را توصیف میکند شکست میخورد. شاخه شکستخورده باید ورودیای را که همین حالا ساخته حذف کند و سپس با کد غیرصفر خارج شود. انتظار برای رویداد disconnect که ممکن است هرگز زمانبندی نشود، همان راهی است که یک قاعده از نشستی که برایش در نظر گرفته شده بود بیشتر عمر میکند.
جبران را خودتکرارپذیر و دارای مالک کنید
پاکسازی باید اجرای دوبارهاش ایمن و درباره اینکه چه چیزی را میتواند حذف کند دقیق باشد. رویداد دیرهنگام از تلاش پیشین نباید تخصیص نشست تازهتری را فقط به این دلیل که هر دو نام مشترک یا نشانی تونل بازیافتی دارند حذف کند. دستگیره یا شناسه عملیات مشخص را همراه منبع ثبت کنید و بر اساس همان کلید حذف کنید، نه بر اساس نام. تراکنشهای پایگاه داده تنها در تغییرهای خود پایگاه داده کمک میکنند؛ قاعده فایروال بیرون از آن مرز به گام جبران صریح نیاز دارد.
فرض کنید فرایند میتواند میان تغییر و پاکسازی بمیرد، و یک آشتیدهنده اضافه کنید که بر اساس زمانبندی وضعیت بیرونی را با فهرست کلاینتهای پذیرفتهشده مقایسه میکند.
سه مسیری را که واقعاً تفاوت دارند بیازمایید
سه حالت را تمرین کنید: ردی پیش از هر تخصیصی، ردی پس از تخصیص جزئی، و یک نشست پذیرفتهشده عادی که بعداً پایان مییابد. این سه خطوط گزارش و باقیماندههای متفاوتی تولید میکنند. پس از هر یک وضعیت بیرونی را بررسی کنید، نه فقط پیام شکست کلاینت را، چون شکست کلاینت در دو مورد از سه مورد نتیجه مورد انتظار است.
مرحله hook، وضعیت خروج و آنچه تخصیص یا آزاد شد را بدون اطلاعات اعتبارنامه ثبت کنید. گزارش hookی که منبع ساختهشده را نام میبرد همان چیزی است که قاعده نشتکرده را شش ماه بعد یافتنی میکند.