HTTPS و گواهی TLS؛ راهاندازی Certbot و بررسی تمدید خودکار
در این درس ۸ بخش
در پایان درس قبل، درخواست HTTP با نام واقعی دامنه به Nginx میرسید. HTTP ساده، بهتنهایی محرمانگی مسیر و محتوای تبادل را تأمین نمیکند. اکنون HTTPS را اضافه میکنیم تا کلاینت بتواند ارتباط رمزگذاریشده با هویت دامنهٔ مورد انتظار برقرار کند. پیشنیاز این درس، کنترل یک نام واقعی، رکوردهای DNS درست، دسترسی عمومی پورت۸۰ و سپس۴۴۳، و سلامت برنامهٔ پشت Nginx است. api.example.com و IPهای مستنداتی را نباید به فرمان صدور واقعی بدهید. اگر فعلاً فقط ماشین مجازی محلی دارید و دامنه به آن نمیرسد، میتوانید مفاهیم و آزمونهای محلی قبلی را انجام دهید، اما این مسیر HTTP-01 را کاملشده گزارش نکنید. هدف، رسیدن به یک گواهی معتبرِ نام واقعی و یک روش قابل مشاهده برای تمدید آن است.
برای پیوند با آموختههای قبلی، تعریف درخواست، پاسخ و تفاوت خطای ارتباط با پاسخ ناموفق را در این درس Node مرور کنید:
https://www.darsavand.com/courses/nodejs-windows-backend-start?lesson=12cd93b9-e8a7-44fb-b60b-928a8495a617
روش بررسی نام تا برنامه را هم در درس شبکهٔ لینوکس ببینید:
https://www.darsavand.com/courses/linux-advanced-administration?lesson=0e24bd50-d875-4979-8a88-69fea1b60aa9
در سمت کلاینت، #fetch-http-json در صورت شکست اعتبارسنجی TLS میتواند پیش از دریافت پاسخ برنامه شکست بخورد. این رخداد با JSON نامعتبر یا ۴۰۴ از سرور یکسان نیست. به همین دلیل، بعد از افزودن HTTPS دوباره همهٔ لایهها را آزمایش میکنیم و تنها به علامت مرورگر یا پیام موفق یک ابزار تکیه نمیکنیم.
گواهی، کلید خصوصی و TLS هرکدام چه هستند؟
TLS پروتکلی برای ساخت کانال محافظتشده در انتقال است. کلاینت در آغاز ارتباط، هویت مقصد را از روی نام درخواستشده، گواهی ارائهشده و زنجیرهٔ اعتماد بررسی میکند و دربارهٔ کلیدهای ارتباط توافق میشود. گواهی مدرکی عمومی است که نامها، کلید عمومی و اطلاعات اعتبار را همراه امضای صادرکننده در بر دارد. کلید خصوصی محرمانه میماند و افشای آن میتواند پیامد امنیتی داشته باشد. تشبیه گواهی به کارت شناسایی کمک میکند تفاوت نام و هویت را به خاطر بسپارید، ولی اعتبار آن به بررسی فنی زنجیره و نام و زمان وابسته است. HTTPS سالم، درستبودن منطق تجاری، مجوز کاربر یا امنبودن همهٔ کدها را تضمین نمیکند. برنامهٔ حافظهای این دوره پس از TLS هم پایگاه داده یا ورود کاربر پیدا نمیکند؛ مسیرهای عمومی نوشتن همچنان بسته میمانند.
در این معماری، Nginx گواهی را به کلاینت بیرونی ارائه میکند و ارتباط داخلی آن با Node روی loopback باقی میماند. از این موضوع نتیجه نگیرید که هر ارتباط داخلی در همهٔ معماریها میتواند بدون TLS باشد؛ اگر اجزا روی میزبانهای جدا باشند یا شبکهٔ داخلی بخشی از مدل تهدید باشد، طراحی دیگری لازم است. همچنین «رمزگذاری در انتقال» با نگهداری رمز یا فایل پشتیبان به شکل امن فرق دارد. #validation-contracts هنوز در مرز برنامه لازم است و #json صرفاً قالب داده است، نه روش محرمانهکردن آن. کاربر برنامه باید نام HTTPS صحیح را ببیند؛ استفاده از IP یا نام متفاوت ممکن است به دلیل تطابقنداشتن نام گواهی شکست بخورد، حتی اگر به همان دستگاه رسیده باشید. این آموزش مسیر مبتنی بر دامنه را پیاده میکند و دربارهٔ صدور گواهی برای IP، فرض یا دستور جایگزین ارائه نمیکند.
ACME و آزمون HTTP-01؛ چرا پورت۸۰ هنوز مهم است؟
ACME پروتکلی است که ابزار کلاینت و مرجع صدور برای درخواست، اثبات کنترل و تمدید گواهی استفاده میکنند. Certbot یک کلاینت شناختهشدهٔ این پروتکل است و افزونهٔ Nginx میتواند تنظیمات وبسرور را برای اثبات کنترل و نصب گواهی تنظیم کند. در روش HTTP-01، مرجع صدور باید پاسخِ یک مسیر ویژه زیر /.well-known/acme-challenge/ را از نام شما روی پورت۸۰ دریافت کند. این مسیر بهطور موقت توسط ابزار مدیریت میشود؛ لازم نیست برای آن در Node تابع تازه بنویسید. شنوندهٔ۳۰۰۰ یا موفقیت curl محلی کافی نیست، چون آزمون از بیرون انجام میشود. وجود A یا AAAA اشتباه، منع پورت۸۰، رسیدن نام به CDN یا دستگاه دیگر، همگی میتوانند این اثبات را ناموفق کنند. گواهی را برای نامی درخواست میکنیم که کنترلش را داریم و شواهد دسترسی آن را قبلاً ثبت کردهایم.
نصب یک روش مشخص، بدون مخلوطکردن بستههای مختلف
# روی اوبونتو؛ ابتدا فقط وضعیت نصب فعلی را ببینید.
command -v certbot || true
snap version
dpkg-query -W certbot python3-certbot-nginx 2>/dev/null || true
snap list certbot 2>/dev/null || trueمسیر اصلی این درس، بستهٔ رسمی معرفیشده در راهنمای Certbot با snap است. اگر Certbot قبلاً از apt، محیط Python یا روش دیگری نصب شده، نصب دوم را بیبررسی روی آن قرار ندهید؛ اول روش نگهداری و مهاجرت را از راهنمای رسمی همان نصب مشخص کنید. نبود خروجی dpkg یا پیام نبود بستهٔ snap، روی ماشین تازه قابل انتظار است. snap version باید وضعیت ابزار و سرویس snapd را نشان دهد؛ اگر فرمان وجود ندارد، طبق دستور بعد snapd را از مخزن اوبونتو نصب کنید و آمادهبودن سرویس آن را بررسی کنید. بهجای ساختن یک پیوند فرمان که شاید فایل دیگری را جایگزین کند، در این درس مسیر کامل /snap/bin/certbot را فراخوانی میکنیم. این تصمیم ابهام اینکه کدام نصب اجرا شده را کم میکند و شبیه همان توجه به process.execPath در دورهٔ Node است.
# روی ماشین تازهای که Certbot دیگری ندارد
sudo apt update
sudo apt install snapd
sudo snap install --classic certbot
sudo /snap/bin/certbot --versionگزینهٔ classic شیوهٔ محدودسازی بستهٔ snap را مشخص میکند و به این ابزار مدیریتی دسترسی گستردهتری نسبت به snap کاملاً محدود میدهد؛ به همین دلیل منبع رسمی و نام بسته اهمیت دارد. وجود این گزینه، دستور عمومی برای نصب هر بستهٔ ناشناس نیست. خروجی آخر باید نسخهٔ Certbot نصبشده را بدهد. اگر خطای ارتباط با فروشگاه یا آمادهنبودن snapd دارید، همان مرحله را بررسی کنید؛ کپیکردن فایل گواهی از یک سایت دیگر یا نصب همزمان چند ابزار راهحل مناسبی نیست. هیچکدام از فرمانها در این درس روی سرور واقعی شما توسط نویسنده اجرا نمیشود؛ آنها مراحل عمل یادگیرنده در ماشین آزمایشی خودش هستند. پیش از ادامه، مطمئن شوید آخرین آزمون HTTP با نام واقعی هنوز وضعیت سالم میدهد.
پیش از تغییر خودکار، نسخهٔ قابل برگشت بگیرید
# روی اوبونتو، همان نشست؛ مسیر پشتیبان را یادداشت کنید.
umask 077
TLS_BACKUP="$HOME/darsavand-nginx-before-tls-$(date +%Y%m%d-%H%M%S)"
mkdir -- "$TLS_BACKUP"
sudo cp -- /etc/nginx/sites-available/darsavand-node "$TLS_BACKUP/darsavand-node"
sudo cp -- /etc/nginx/conf.d/darsavand-trace.conf "$TLS_BACKUP/darsavand-trace.conf"
printf 'Backup directory: %s\n' "$TLS_BACKUP"
sudo nginx -tاین پشتیبان مشخصاً نسخهٔ دو فایل تنظیمات پیش از تغییر TLS است؛ پشتیبان کامل سرور، پایگاه داده یا کلیدهای خصوصی نیست. فرمان date بخشی از نام پوشه را میسازد تا نسخهٔ قبلی بیخبر بازنویسی نشود. umask محدودیت پیشفرض فایلها و پوشههای تازه را در همین شل تعیین میکند. مسیر چاپشده را در یادداشت عملیات بنویسید، چون در یک اتصال SSH تازه متغیر TLS_BACKUP خودبهخود وجود ندارد. اگر cp یا nginx -t شکست خورد، صدور را شروع نکنید؛ نخست فایل مبدأ، مجوز و نحو را اصلاح کنید. Certbot میتواند تنظیمات میزبان را تغییر دهد؛ دانستن اینکه قبل از تغییر چه داشتهاید، برای فهمیدن اختلاف و بازگشتِ سنجیده ضروری است. کلید خصوصی را در تیکت پشتیبانی، فایل عمومی پروژه یا مخزن Git قرار ندهید.
درخواست گواهی برای نام واقعی؛ نقش تصمیمهای تعاملی
# روی اوبونتو؛ مقدار را با نام واقعی داخل server_name یکسان کنید.
API_DOMAIN='api.example.com'
sudo /snap/bin/certbot --nginx -d "$API_DOMAIN"پیش از اجرا، api.example.com را به نام واقعی خودتان تغییر دهید. ابزار ممکن است نشانی ایمیل، پذیرش شرایط مرجع صدور و انتخابهای مربوط به تنظیم HTTPS را بپرسد؛ خودتان متن و اثر هر انتخاب را بخوانید و آگاهانه پاسخ دهید. این درس شرایط حقوقی را بهجای شما قبول نمیکند و گزینهٔ خودکار پذیرش را به فرمان اضافه نکرده است. پس از موفقیت، گزارش نصب باید نام درست و محل پیکربندی را نشان دهد. اگر چند نام یا سایت روی دستگاه دارید، تطبیق server_name را با دقت ببینید تا ابزار میزبان دیگری را تغییر ندهد. پس از پایان، با sudo nginx -t نحو را بررسی کنید و فایل میزبان را برای فهم تغییرها بخوانید. برای درس فعلی یک زیردامنه کافی است؛ نامهای www یا wildcard را فقط برای طولانیترشدن فرمان اضافه نکنید.
اگر اثبات کنترل شکست خورد، پیام آن را به یک فرضیهٔ قابل آزمون تبدیل کنید. unauthorized میتواند به پاسخ اشتباه از مسیر challenge یا دستگاه دیگر مربوط باشد؛ timeout میتواند منع پورت۸۰ یا مسیر نادرست باشد؛ خطای DNS را با اصلاح Nginx درمان نمیکنیم. A و AAAA را دوباره از بیرون بررسی کنید، HTTP معمولی را آزمایش کنید و وضعیت سرویس را بخوانید. بعضی شکستها محدودیت تعداد درخواست یا شرایط مرجع صدور دارند؛ درخواست صدور را بیوقفه تکرار نکنید. پس از اصلاح علت، روش آزمون یا محیط staging راهنمای رسمی را بررسی کنید. اگر ابزار نیمهکاره تغییری ایجاد کرده، اختلاف تنظیمات را با نسخهٔ قبل مقایسه کنید و فقط فایل مربوط را به وضعیت شناختهشده برگردانید؛ قبل از reload حتماً nginx -t موفق شود. بازگشت موقت به HTTP در آزمایش را با آمادهبودن امنیتی یک سامانهٔ واقعی برای اطلاعات حساس یکی ندانید.
آزمون HTTPS معتبر؛ دورزدن خطای گواهی نتیجهٔ درست نیست
# روی اوبونتو، با API_DOMAIN واقعی تعریفشده در همین نشست
sudo nginx -t
curl --fail --show-error --connect-timeout 5 --max-time 15 "https://${API_DOMAIN}/health"
curl -i --connect-timeout 5 --max-time 15 "https://${API_DOMAIN}/api/tasks"
curl -i --connect-timeout 5 --max-time 15 "http://${API_DOMAIN}/health"
sudo /snap/bin/certbot certificatesدرخواست HTTPS سلامت باید با بررسی عادی گواهی موفق شود و /api/tasks همچنان رفتار خواندنی قبلی را داشته باشد. اگر HTTP به HTTPS هدایت شده، وضعیت ۳xx و Location را ببینید؛ این هدایت با پاسخ مستقیم ۲۰۰ برنامه فرق دارد. فرمان certificates نامهای تحت پوشش و زمان اعتبار مشاهدهشده را نشان میدهد؛ بهجای حفظکردن یک عدد ثابت برای عمر همهٔ گواهیها، همان وضعیت واقعی و سیاست فعلی مرجع صدور را بررسی کنید. افزودن -k به curl، اعتبارسنجی گواهی را خاموش میکند و معیار قبولی این درس نیست. خطای نام، زنجیرهٔ نامعتبر یا ساعت نادرست دستگاه را پیدا و اصلاح کنید. آزمون را از رایانهٔ ویندوزی خود نیز با curl.exe و همان نام HTTPS تکرار کنید؛ موفقیت فقط روی سرور، مسیر واقعی کاربران بیرونی را کامل ارزیابی نمیکند.
تمدید یک فرایند عملیاتی است، نه کاری که فراموش شود
# روی اوبونتو؛ آزمون تمدید و مشاهدهٔ برنامهٔ زمانبندی
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all '*certbot*'
sudo ss -lntp '( sport = :80 or sport = :443 or sport = :3000 )'dry-run مسیر تمدید را با محیط آزمایشی بررسی میکند و نباید آن را صدور یک گواهی تولیدی تازه برای کاربران معرفی کرد. خروج موفق آن شاهد مفیدی است که پیشنیازهای فعلی تمدید کار میکنند؛ تضمین نمیکند یک ماه بعد DNS، فایروال یا سرویس تغییر نکرده باشد. نام timer را از خروجی واقعی بخوانید و برای بررسی لاگ همان واحد از journalctl استفاده کنید؛ نام سرویس را براساس یک آموزش قدیمی حدس نزنید. آزمون پورتها باید نشان دهد Nginx روی پورتهای عمومی لازم شنونده دارد و Node روی127.0.0.1:3000 مانده است. پورت۸۰ را فقط چون HTTPS امروز موفق شده کورکورانه نبندید؛ این روش اعتبارسنجی و مسیر تمدید همچنان به HTTP-01 وابسته است. هشدار انقضا و مشاهدهٔ شکست تمدید باید بخشی از برنامهٔ نگهداری شما باشد، نه یادداشتی که فقط هنگام خرابی دیده میشود.
تمرین نهایی و معیار تحویل HTTPS
یک برگهٔ تحویل بسازید: نام دامنهٔ واقعی، وضعیت A و AAAA، موفقیت HTTPS از دو محل، وضعیت هدایت HTTP، تاریخ اعتبار مشاهدهشده، نتیجهٔ dry-run و نام timer واقعی. سپس این سؤال را پاسخ دهید: چرا ۲۰۰ از curl -k معیار کافی نیست؟ پاسخ: با حذف بررسی گواهی، بخشی از چیزی را که میخواستیم بسنجیم کنار گذاشتهایم؛ برنامه شاید پاسخ دهد اما هویت معتبر مقصد تأیید نشده است. سؤال دوم: اگر درخواست GET موفق و POST عمومی۴۰۳ است، آیا TLS خراب است؟ خیر؛ TLS کانال را ساخته و قاعدهٔ روش HTTP طبق طراحی اعمال شده است. سؤال سوم: آیا TLS، دادههای Map را بعد از restart نگه میدارد؟ خیر؛ دوام داده موضوع ذخیرهسازی و بازیابی است. برای نوشتن گزارش شکست، از #try-catch و توضیح کنترلشدهٔ علت کمک بگیرید، اما خروجی دارای رمز یا کلید را منتشر نکنید. پس از ثبت این شواهد، به فصل نگهداری و بازیابی بروید.
منابع رسمی انگلیسی:
Certbot — Nginx on snap instructions: https://certbot.eff.org/instructions?os=snap&ws=nginx
Certbot — Command line reference: https://eff-certbot.readthedocs.io/en/stable/man/certbot.html
Ubuntu — Obtain TLS certificates: https://ubuntu.com/server/docs/how-to/security/obtain-tls-certificates/
Let’s Encrypt — Challenge types: https://letsencrypt.org/docs/challenge-types/
Let’s Encrypt — Integration guide: https://letsencrypt.org/docs/integration-guide/
curl — SSL certificate verification: https://curl.se/docs/sslcerts.html
متن فارسی، توضیح مفاهیم و سناریوهای عملی: تألیف مستقل تحریریه درسآوند.
متن درسهای عمومی را بدون ورود بخوانید.