کشِ لوکاپِ گواهی تو Caddy با یه ماژول کوچیک
خلاصهٔ کاملتر
نویسنده (Mattias Geniar) توضیح میده که تو Oh Dear، صفحههای وضعیت میتونن روی دامنهی خودِ مشتری اجرا بشن و همیشه با گواهیِ on-demandِ ACME (از Let’s Encrypt، توسطِ Caddy) سرو میشدن. حالا دارن یه گزینهی دوم اضافه میکنن: اینکه مشتری گواهیِ خودش رو بیاره. برای این حالت، Caddy چیزی provision نمیکنه و گواهی رو از یه backendِ HTTP تو خودِ اپشون میگیره؛ روی هر handshake یه GET با SNI بهعنوان پارامتر میزنه و اپ گواهیِ متناظر رو برمیگردونه.
موقعِ ساختِ این قابلیت، نویسنده متوجه یه نکته شده: کش وجود نداره. طبقِ توضیح، certmagic — کتابخانهی گواهیِ زیرِ Caddy — نتیجهی یه get_certificate manager رو کش نمیکنه و بهطورِ طراحیشده روی هر handshake صداش میزنه (یه issue بازِ مرتبط هم هست). برای getterی که از دیسک میخونه اشکالی نداره، ولی برای یکی که درخواستِ HTTP میزنه، یعنی روی هر handshake یه رفتوبرگشت به backend، برای گواهیای که شاید هر چند ماه یهبار عوض میشه.
راهحلش یه ماژولِ کوچیک به اسم caddy-get-certificate-cacheه که تحتِ Apache-2.0 متنبازه. این یه جایگزینِ drop-in برای getterِ استانداردِ httpه که همون fetchِ HTTP رو تو یه کشِ درونفرایندی میپیچه؛ هنوز از backend میگیره، ولی نه روی هر handshake. رو hit، مسیرِ داغ فقط یه map lookupه — چند میکروثانیه، بدونِ شبکه. تو Caddyfile فقط کافیه http رو با cached_http عوض کنی.
تصمیمِ طراحیِ موردعلاقهی نویسنده اینه که ماژول هدرِ Cache-Controlی که backend روی پاسخ میذاره رو محترم میشماره: مقدارِ s-maxage تعیین میکنه Caddy چهقدر گواهی رو نگه داره، پس کنترل دستِ اپ میمونه؛ البته میشه بهجاش یه ttl ثابت تو Caddyfile هم پین کرد. رفتارش اینطوره: پاسخِ 200 پارس و کش میشه؛ پاسخِ 204 (یعنی «برای این نام گواهی نیست») بهعنوانِ یه entryِ منفی برای negative_ttl کش میشه تا یه دامنهی ناشناس backend رو نکوبه؛ و هر چیزِ دیگه یا خطای پارس، بهعنوانِ خطا برگردونده و کش نمیشه تا یه اختلالِ گذرا خودش رو تو handshakeِ بعدی جبران کنه.
دو نکتهی دیگه هم زیرِ بار مهمن: cache missهای همزمان برای یه نام با singleflight به یه درخواستِ upstream واحد ادغام میشن، پس یه رگبار handshake برای یه دامنهی سرد فقط یه fetch میسازه نه صدتا؛ و خودِ fetch عمداً context مربوط به handshake رو به ارث نمیبره تا اگه کلاینتی که lookupِ سرد رو تریگر کرده قطع شد، fetch برای بقیهی handshakeهای منتظر ادامه پیدا کنه (با timeoutِ ۱۰ ثانیهای مستقل). یه cache_dirِ اختیاری هم هست که بستهها رو روی دیسک نگه میداره تا بعد از restart، Caddy گرم برگرده؛ چون کلیدهای خصوصی کش میشن، باید این دایرکتوری فقط برای کاربرِ Caddy خوندنی باشه.
نکات کلیدی:
- Caddy (از راهِ certmagic) نتیجهی get_certificate رو کش نمیکنه و روی هر handshake یه درخواست به backend میزنه.
- ماژولِ متنبازِ caddy-get-certificate-cache یه جایگزینِ drop-in برای getterِ httpه که fetch رو تو یه کشِ درونفرایندی میپیچه (cached_http).
- هدرِ Cache-Control و s-maxageِ backend رو محترم میشماره؛ 204 بهعنوانِ کشِ منفی و خطاها بدونِ کش برگردونده میشن.
- با singleflight درخواستهای همزمان ادغام میشن و fetch به context مربوط به handshake وابسته نیست.
- یه cache_dirِ اختیاری بستهها رو روی دیسک نگه میداره؛ چون کلید خصوصی کش میشه باید دسترسیش محدود بمونه.




