ساخت گواهی self-signed در Ubuntu 24.04
آموزش ساخت TLS certificate با دستور openssl و تنظیم SAN در nginx/Apache برای رفع خطای مرورگر در Ubuntu 24.04 بدون نیاز به استفاده از curl -k.
آنچه در حال ساخت آن هستید
یک گواهی TLS خود-امضا (self-signed) که مرورگرها و کلاینتهای مدرن آن را بپذیرند؛ شامل subjectAltName صحیح، دسترسیهای امن (permissions) مناسب برای کلید، و متصل به nginx یا Apache. همچنین بخش مهمی که تقریباً تمام راهنماها از آن غافل میشوند: اینکه کلاینتهای شما واقعاً به آن اعتماد کنند، به جای اینکه از هشدارهای امنیتی عبور کنند و curl -k را برای همیشه در اسکریپتها hard-code کنند. در پایان، یک CA خصوصی با 5 دستور، برای زمانی که تعداد سرویسهای داخلی شما از یک به 6 برسد.
ابتدا، تصمیمگیری؛ زیرا گواهی خود-امضا بسیار کمتر از آنچه تصور میشود، ابزار مناسبی است. اگر سرویس شما از طریق یک نام DNS واقعی در اینترنت عمومی در دسترس است، مطالعه را متوقف کنید و به جای آن یک گواهی رایگان Let's Encrypt با استفاده از certbot در nginx یا معادل آن در Apache تهیه کنید. این کار هزینهای ندارد، خودش بهروزرسانی میشود و تمام مرورگرهای جهان از قبل به آن اعتماد دارند. استفاده از گواهی خود-امضا در یک سایت عمومی، کاربران شما را عادت میکند که از هشدارهای امنیتی عبور کنند، که این عادت از پروتکل ساده HTTP هم بدتر است.
استفاده از حالت self-signed زمانی ابزار مناسبی است که اینترنت عمومی در میان نباشد: یک پنل مدیریت متصل به یک آدرس تونل WireGuard در VPS شما، یک سرور staging در یک شبکه خصوصی، ترافیک سرویس-به-سرویس بین بکاندها، یک دستگاه در آزمایشگاه خانگی (home-lab)، یا جایگزینی برای گواهی موقتی که Webmin برای خود در پورت 10000 ایجاد میکند. در هر صورت Let's Encrypt نمیتواند برای 10.8.0.1 یا git.internal.lan گواهی صادر کند؛ هیچ CA عمومیای یک IP خصوصی یا یک TLD ساختگی را در گواهی قرار نمیدهد. برای آن نامها، شما خودتان CA هستید.
تمام مراحل زیر روی یک سیستم تازه با Ubuntu 24.04 اجرا میشود که دارای OpenSSL 3.0.x است (برای تایید از openssl version استفاده کنید). هیچکدام از این مراحل به دسترسی اینترنت نیاز ندارند؛ همه چیز در حالت air-gapped کار میکند.
چرا دستور تکخطی قدیمی گواهیهایی تولید میکند که Chrome آنها را رد میکند
دستوری که در تمام آموزشهای پیش از سال 2017 ارائه میشود، به این صورت است:
# Do not run this — shown so you recognise it in old guides
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout selfsigned.key -out selfsigned.crtاین دستور مجموعهای از سوالات تعاملی را میپرسد، hostname شما را در فیلد Common Name قرار میدهد و گواهیای بدون افزونه subjectAltName تولید میکند. آن گواهی از همان ابتدا نامعتبر است. Chrome در نسخه 58، در آوریل 2017، خواندن Common Name را متوقف کرد — RFC 2818 پیش از آن در سال 2000 تطبیق CN را منسوخ کرده بود — و Firefox، Safari، curl و Python نیز به همین صورت عمل میکنند. یک گواهی، سرور خود را از طریق افزونه SAN شناسایی میکند و اگر این افزونه وجود نداشته باشد، سرور شناسایی نمیشود؛ مرورگر دقیقاً با این عبارت این موضوع را به شما اعلام میکند:
NET::ERR_CERT_COMMON_NAME_INVALID
This server could not prove that it is git.internal.lan; its security
certificate does not specify Subject Alternative Names.هیچ میزان تغییر در trust-store این خطا را برطرف نمیکند، زیرا گواهی واقعاً هیچ نامی را مشخص نمیکند. اگر اکنون با NET::ERR_CERT_COMMON_NAME_INVALID مواجه شدهاید، گواهی شما فاقد SAN (یا دارای SAN اشتباه) است و باید یک گواهی جدید بسازید. خوشبختانه راه حل، تنها با یک دستور انجام میشود.
ساخت گواهینامه مورد پذیرش مرورگرها: تنها با یک دستور
در نسخه 1.1.1، پرچم -addext به OpenSSL اضافه شد؛ این یعنی دیگر نیازی به پیچیدگیهای فایل کانفیگ در راهنماهای قدیمی برای تزریق SAN ندارید. در Ubuntu 24.04:
sudo openssl req -x509 -newkey rsa:4096 -sha256 -days 730 -noenc \
-keyout /etc/ssl/private/git.internal.key \
-out /etc/ssl/certs/git.internal.crt \
-subj "/CN=git.internal.lan" \
-addext "subjectAltName=DNS:git.internal.lan,IP:10.8.0.1"عملکرد هر پرچم:
-x509به جای درخواست امضا (CSR)، مستقیماً یک گواهینامه self-signed صادر میکند.-newkey rsa:4096در همان مرحله، یک کلید جدید تولید میکند. RSA 4096 با کلاینتهای قدیمی مشکلی ندارد؛ اما اگر تمام کلاینتهای متصل شما مدرن هستند،-newkey ec -pkeyopt ec_paramgen_curve:P-256کوچکتر و سریعتر است.-noencمعادل جدید-nodesدر OpenSSL 3.x است: بدون رمز عبور برای کلید. هر دو حالت کار میکنند. کلید دارای رمز عبور باعث میشود nginx در هر بار اجرا، منتظر دریافت ورودی بماند؛ بنابراین برای کلید سرور، از این حالت استفاده کنید.-days 730— دو سال؛ جزئیات بیشتر در بخش انقضا.-subjبه سوالات تعاملی به صورت inline پاسخ میدهد. CN اکنون جنبه ظاهری دارد، اما با این حال آن را روی نام اصلی تنظیم کنید؛ برخی ابزارها آن را نمایش میدهند.-addext "subjectAltName=..."پرچم اصلی و حیاتی است. تمام نامها و تمام IPهایی که کلاینتها تایپ میکنند را لیست کنید: ورودیهایDNS:برای نامهای میزبان (wildcardهایی مثلDNS:*.internal.lanمجاز هستند)، ورودیهایIP:برای آدرسها. اگر کسی بخواهد بهhttps://10.8.0.1مراجعه کند، ورودیIP:10.8.0.1حتماً باید آنجا باشد — یک SAN که فقط شامل DNS باشد، باعث بروز خطایNET::ERR_CERT_COMMON_NAME_INVALIDمیشود.
قبل از هرگونه اتصال، از ثبت شدن SAN اطمینان حاصل کنید:
openssl x509 -in /etc/ssl/certs/git.internal.crt -noout -ext subjectAltNameخروجی صحیح:
X509v3 Subject Alternative Name:
DNS:git.internal.lan, IP Address:10.8.0.1اگر خروجی به جای آن، No extensions in certificate را چاپ کرد، یعنی گواهینامه فاقد SAN است و مرورگرها آن را رد میکنند؛ به جای ادامه کار، دوباره گواهینامه را بسازید.
Lock down the key
A private key readable by every user on the box is not a private key. On Ubuntu, /etc/ssl/private is already 710 root:ssl-cert, which keeps casual eyes out, but set the file itself explicitly:
sudo chown root:root /etc/ssl/private/git.internal.key
sudo chmod 600 /etc/ssl/private/git.internal.keynginx and Apache both read certificates as root before dropping privileges, so root:root mode 600 works for them. If the key is for a service that runs as its own user and loads the key itself — a Node app, Gitea, a Python daemon — chown it to that service user instead, still mode 600. What you never do: mode 644, a copy in a git repository, or a copy in /tmp.
Wire it into nginx
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name git.internal.lan;
ssl_certificate /etc/ssl/certs/git.internal.crt;
ssl_certificate_key /etc/ssl/private/git.internal.key;
location / {
proxy_pass http://127.0.0.1:3000;
}
}sudo nginx -t && sudo systemctl reload nginxnginx -t باید پیش از اعمال تغییرات توسط reload، مقادیر syntax is ok و test is successful را چاپ کند. اگر به جای آن SSL_CTX_use_PrivateKey_file() failed ... key values mismatch چاپ شود، یعنی گواهینامه (certificate) و کلید (key) از دو دوره تولید متفاوت هستند — بخش failure-modes را بررسی کنید.
اتصال به Apache
sudo a2enmod ssl proxy proxy_httpدر اینجا ssl به تنهایی کافی نیست: vhost زیر از ProxyPass استفاده میکند و بدون mod_proxy و mod_proxy_http، تست پیکربندی با خطای Invalid command 'ProxyPass', perhaps misspelled or defined by a module not included in the server configuration مواجه میشود. فایل vhost را در مسیر /etc/apache2/sites-available/git-internal.conf ذخیره کنید:
<VirtualHost *:443>
ServerName git.internal.lan
SSLEngine on
SSLCertificateFile /etc/ssl/certs/git.internal.crt
SSLCertificateKeyFile /etc/ssl/private/git.internal.key
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>sudo a2ensite git-internal
sudo apache2ctl configtest && sudo systemctl reload apache2باید Syntax OK را در پاسخ configtest دریافت کنید. اکنون از یک سیستم کلاینت تست کنید:
curl -v https://git.internal.lan/و با این خطا مواجه خواهید شد:
curl: (60) SSL certificate problem: self-signed certificateاین یک باگ نیست. این عملکرد صحیح TLS است: curl قبلاً اطلاعاتی درباره گواهی (certificate) شما نداشته است و از برقراری ارتباط با سروری که نمیتواند احراز هویت کند، خودداری میکند. بخش بعدی راه حل اصلی است — و برخلاف آنچه نیمی از کاربران اینترنت در حال حاضر انجام میدهند، این روش صحیح است.
ایجاد اعتماد در کلاینتها — و الگوهای اشتباهی که باید از آنها پرهیز کنید
ابتدا روشهای اشتباه را بررسی میکنیم. اگر از curl -k (یا --insecure) در یک اسکریپت، verify=False در Python requests، یا NODE_TLS_REJECT_UNAUTHORIZED=0 در Node استفاده کنید، هیچکدام از اینها باعث معتبر شدن گواهی شما نمیشوند. این روشها تاییدیه گواهی (certificate verification) را خاموش میکنند؛ این یعنی کلاینت بدون هیچ مشکلی با هر سروری که هر گواهیای ارائه دهد صحبت خواهد کرد، از جمله سروری که یک مهاجم در مسیر قرار داده است. در این حالت، شما بار اضافی TLS را حفظ میکنید اما قابلیت احراز هویت را که هدف اصلی آن بود، از دست میدهید. بدتر از آن، این پرچمها (flags) سرایت میکنند: ابتدا در یک cron job، سپس در یک اسکریپت استقرار (deploy script) و در نهایت در کد اصلی (production code) کپی میشوند، تا جایی که دیگر کسی یادش نمیماند کدام اتصالات قرار بود موقتی باشند. اگر یک verify=False فراتر از جلسه عیبیابی که باعث ایجاد آن شده باقی بماند، طراحی سیستم اشتباه است.
راه حل درست این است که به هر سیستمعامل کلاینت آموزش دهید که این گواهی یک ریشه مورد اعتماد (trusted root) است. در کلاینتهای Ubuntu و Debian:
sudo cp git.internal.crt /usr/local/share/ca-certificates/git.internal.crt
sudo update-ca-certificatesخطی که در خروجی اهمیت دارد (یک بلوک Running hooks in /etc/ca-certificates/update.d... بعد از آن میآید):
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.دو نکته مهم در این خطوط وجود دارد. فایل باید به .crt ختم شود؛ پسوند .pem نادیده گرفته میشود و شما بدون هیچ پیام خطایی با 0 added مواجه میشوید. همچنین محتوا باید از نوع PEM باشد؛ فایل با -----BEGIN CERTIFICATE----- شروع میشود؛ ابتدا یک باینری DER را با استفاده از openssl x509 -inform der -in file.der -out file.crt تبدیل کنید. اضافه کردن خودِ گواهی خود-امضا (self-signed) به عنوان ریشه، کار میکند زیرا یک گواهی خود-امضا، در واقع ریشه خودش است.
پس از آن، curl، wget، git، apt و هر ابزار دیگری که از OpenSSL برای استفاده از باندل سیستم استفاده میکند، بدون نیاز به هیچ پرچمی به سرور اعتماد خواهد کرد. تعداد اندکی از کلاینتها مخزن اعتماد (trust store) مخصوص خود را دارند و نیاز به تنظیمات جداگانه دارند:
- Chrome/Chromium در Linux: به جای مخزن سیستم، یک پایگاه داده NSS را میخواند:
sudo apt install libnss3-tools، سپسcertutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "git.internal" -i git.internal.crtبرای هر کاربر. - Firefox: مخزن مخصوص خود را دارد: Settings → Privacy & Security → Certificates → Import، یا در
about:configمقدارsecurity.enterprise_roots.enabledرا بهtrueتغییر دهید تا از مخزن سیستم استفاده کند. - Python requests: باندل CA مخصوص خود (certifi) را دارد و مخزن سیستم را نادیده میگیرد: از
verify="/usr/local/share/ca-certificates/git.internal.crt"استفاده کنید یاREQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crtرا export کنید. - Node.js: مقدار
NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/git.internal.crtرا export کنید.
در کلاینتهای Windows، روی .crt دوبار کلیک کنید و آن را در Trusted Root Certification Authorities نصب کنید؛ در macOS، آن را به System keychain در Keychain Access اضافه کرده و وضعیت آن را روی Always Trust قرار دهید.
یک ریشه برای سرویسهای متعدد: یک CA خصوصی کوچک
اعتماد به هر گواهینامه به صورت جداگانه، مقیاسپذیری را بلافاصله متوقف میکند: 6 سرویس ضربدر 4 ماشین کلاینت برابر است با 24 نصب اعتماد، و هر سرویس جدید این تعداد را بیشتر میکند. راه حل، یک CA خصوصی است — کلاینتها به یک ریشه اعتماد میکنند و شما گواهینامه هر سرویس را با آن امضا میکنید.
گزینه کاربرپسند mkcert است که در مخازن Ubuntu 24.04 موجود است و با NSS stores (مانند Chrome و Firefox) که update-ca-certificates نادیده میگیرد، کار میکند:
sudo apt install -y mkcert libnss3-tools
mkcert -install
mkcert git.internal.lan "*.internal.lan" 10.8.0.1mkcert -install یک ریشه ایجاد کرده و آن را در تمام trust storeهای آن ماشین ثبت میکند؛ دستور سوم، git.internal.lan+2.pem و git.internal.lan+2-key.pem را خروجی میدهد که برای استفاده در قطعهکدهای nginx یا Apache بالا آماده هستند. فرض طراحی این ابزار، یک ماشین توسعه است — کلید ریشه در هر سیستمی که -install را اجرا کرده است باقی میماند — بنابراین برای یک لپتاپ توسعه عالی است اما برای مجموعهای از سرورها مناسب نیست.
برای سرورها، OpenSSL ساده تمام مراحل CA را در 5 دستور انجام میدهد:
openssl genrsa -out lab-ca.key 4096
openssl req -x509 -new -key lab-ca.key -sha256 -days 3650 \
-out lab-ca.crt -subj "/CN=Lab Internal CA" \
-addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
openssl genrsa -out git.key 2048
openssl req -new -key git.key -out git.csr -subj "/CN=git.internal.lan" \
-addext "subjectAltName=DNS:git.internal.lan,IP:10.8.0.1"
openssl x509 -req -in git.csr -CA lab-ca.crt -CAkey lab-ca.key \
-CAcreateserial -days 730 -sha256 -copy_extensions copy -out git.crtتله در دستور آخر نهفته است: openssl x509 -req به صورت پیشفرض تمام افزونهها را از CSR حذف میکند، از جمله SAN که با دقت اضافه کرده بودید. -copy_extensions copy (یک گزینه در OpenSSL 3.x، بنابراین در 24.04 کار میکند) آنها را منتقل میکند؛ اگر آن را حذف کنید، گواهینامه امضا شده فاقد SAN خواهد بود و Chrome دوباره با NET::ERR_CERT_COMMON_NAME_INVALID به شما خوشآمد میگوید. با همان بررسی openssl x509 -noout -ext subjectAltName قبلی، آن را تایید کنید.
lab-ca.crt را از طریق مراحل trust-store بالا به کلاینتها توزیع کنید — فقط یک بار برای هر ماشین، برای همیشه. از lab-ca.key مانند یک گنج محافظت کنید: mode 600، و در حالت ایدهآل در سیستمی نگه داشته شود که جزو سرورهایی که برای آنها امضا میکند نباشد، زیرا هر کسی آن را در اختیار داشته باشد، میتواند برای هر نامی که کلاینتهای شما باور کنند، گواهینامه صادر کند.
Expiry and rotation
مدت اعتبار گواهیهای CA عمومی در حال کاهش است. CA/Browser Forum در مارس 2026، سقف اعتبار گواهیهای جدید با اعتماد عمومی را از 398 روز به 200 روز کاهش داد. این مقدار در سال 2027 به 100 روز و تا مارس 2029 به 47 روز خواهد رسید. اما این قوانین فقط شامل CAهای با اعتماد عمومی میشود. CA خصوصی شما تحت این قوانین نیست و مرورگرها آنها را برای ریشههای (roots) نصب شده به صورت دستی اجبار نمیکنند. یک محدودیت واقعی وجود دارد: پلتفرمهای Apple هر گواهی TLS سروری را که اعتبار آن بیش از 825 روز باشد، رد میکنند؛ فارغ از اینکه توسط چه کسی صادر شده باشد. بنابراین اگر قرار است iPhone یا Mac به سرور متصل شوند، اعتبار گواهیهای leaf را روی 2 سال یا کمتر نگه دارید. -days 730 این استاندارد را در همه جا رعایت میکند؛ ترکیب یک root ده ساله با leafهای دو ساله، یک ساختار داخلی مناسب است.
گواهیهای با عمر طولانی فقط به یک صورت با شکست مواجه میشوند: به صورت بیصدا، همزمان و در تاریخی که هیچکس انتخاب آن را به یاد ندارد. وضعیت فعلی خود را بررسی کنید:
openssl x509 -in /etc/ssl/certs/git.internal.crt -noout -enddateزمان تمدید را در یک تقویم واقعی ثبت کنید، یا از cron بخواهید 30 روز قبل از انقضا به شما هشدار دهد — openssl x509 -checkend 2592000 -in cert.crt زمانی که انقضا در آن تعداد ثانیه باقیمانده باشد، با کد خروجی غیرصفر پایان مییابد. اگر از Uptime Kuma برای مانیتورینگ وضعیت استفاده میکنید، مانیتورهای HTTPS آن انقضای قریبالوقوع گواهی را به صورت رایگان اعلام میکنند.
چرخش (Rotation) با یک CA خصوصی بسیار ساده است: دستورات CSR-and-sign را مجدداً اجرا کنید، فایلها را جایگزین کنید و وبسرور را reload کنید. از آنجایی که root تغییر نکرده است، هیچ کلاینتی متوجه تغییری نمیشود.
حالتهای خطا و پیامهایی که مشاهده خواهید کرد
NET::ERR_CERT_AUTHORITY_INVALID — این حالت، وضعیت مورد انتظار قبل از نصب trust است و نشاندهنده نقص در گواهینامه نیست. اگر این وضعیت بعد از نصب root باقی ماند: در Linux، مرورگر Chrome به جای مخزن سیستم، از NSS استفاده میکند (به مرحله certutil مراجعه کنید)؛ یا فایل کپی شده به .crt ختم نشده و update-ca-certificates پیام 0 added را داده است؛ یا سرور گواهینامهای متفاوت از آنچه شما اعتماد کردهاید ارائه میدهد — اثر انگشتها (fingerprints) را با استفاده از openssl s_client -connect git.internal.lan:443 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256 مقایسه کنید.
NET::ERR_CERT_COMMON_NAME_INVALID — گواهینامه فاقد SAN است، یا SAN شامل نام موجود در نوار آدرس نیست. حالت رایج: SAN شامل DNS:git.internal.lan است اما کاربر به https://10.8.0.1 مراجعه کرده است. تغییرات در trust-store این مشکل را حل نمیکند؛ باید گواهینامه را با ورودی مفقود مجدداً صادر کنید.
curl: (60) SSL certificate problem: self-signed certificate — ابزار curl به گواهینامه اعتماد ندارد. حالت self-signed certificate in certificate chain نیز برای گواهیهای امضا شده توسط CA خصوصی شما، معنای مشابهی دارد. راه حل موقت: استفاده از curl --cacert lab-ca.crt https://...؛ راه حل دائمی: اضافه کردن به trust store. نه استفاده از -k.
unable to load certificate ... Expecting: TRUSTED CERTIFICATE (یا Expecting: CERTIFICATE REQUEST، یا no start line) — اشتباه در فرمت PEM. شما فایل اشتباهی را به OpenSSL دادید: یک key یا CSR در جایی که انتظار certificate بود، یا یک فایل binary از نوع DER در جایی که انتظار PEM بود. دستور head -1 filename نوع فایل واقعی شما را مشخص میکند — یک certificate با -----BEGIN CERTIFICATE----- شروع میشود. برای تبدیل DER، از openssl x509 -inform der -in file.der -out file.crt استفاده کنید.
nginx: [emerg] SSL_CTX_use_PrivateKey_file(...) failed (SSL: error ... key values mismatch) — گواهینامه و key با هم مطابقت ندارند؛ معمولاً به این دلیل که دستور تولید دو بار اجرا شده و فایلها با هم مخلوط شدهاند. با مقایسه openssl x509 -in git.internal.crt -noout -pubkey | sha256sum در برابر openssl pkey -in git.internal.key -pubout | sha256sum صحت آنها را تایید کنید؛ تطابق hashها به معنای جفت بودن آنهاست. اگر متفاوت هستند، هر دو را با هم مجدداً تولید کنید.
FAQ
چرا پس از ایجاد یک self-signed certificate، Chrome همچنان عبارت "Not secure" را نمایش میدهد؟
اگر خطا NET::ERR_CERT_AUTHORITY_INVALID باشد، گواهینامه سالم است؛ Chrome صرفاً هنوز دلیلی برای اعتماد به آن ندارد. گواهینامه (یا root CA خصوصی خود) را در trust store کلاینت نصب کنید. به یاد داشته باشید که در Linux، Chrome از طریق certutil از پایگاه داده NSS استفاده میکند، نه از store سیستم. اگر خطا NET::ERR_CERT_COMMON_NAME_INVALID باشد، گواهینامه فاقد Subject Alternative Name مطابق با URL است و باید با استفاده از -addext "subjectAltName=..." مجدداً صادر شود.
چگونه میتوانم curl را بدون استفاده از flag -k، به یک self-signed certificate اعتماد دهم؟
گواهینامه (با فرمت PEM و پسوند .crt) را در /usr/local/share/ca-certificates/ کپی کنید و دستور sudo update-ca-certificates را اجرا کنید؛ خروجی باید 1 added باشد. از آن پس، curl مانند هر گواهینامه عمومی دیگر، آن را تایید میکند. برای یک درخواست موردی بدون تغییر در سیستم، از curl --cacert /path/to/cert.crt برای تایید فقط از طریق آن فایل استفاده کنید؛ دستور -k تایید را کاملاً غیرفعال میکند و نباید در هیچ اسکریپتی استفاده شود.
مدت اعتبار یک self-signed certificate چقدر میتواند باشد؟
از نظر فنی هر چقدر که بخواهید؛ محدودیتهای CA/Browser Forum (در حال حاضر 200 روز، و تا سال 2029 برابر با 47 روز) فقط شامل CAهای مورد اعتماد عمومی میشود، نه اعتماد خصوصی. در عمل، اعتبار گواهینامههای سرور را روی 825 روز محدود کنید، زیرا دستگاههای Apple هر چیزی را با مدت اعتبار بیشتر از آن، بدون توجه به صادرکننده، رد میکنند. یک root خصوصی ده ساله با گواهینامههای leaf دو ساله (-days 730) یک تنظیم پیشفرض منطقی است؛ فقط زمان تمدید را در تقویم یادداشت کنید، زیرا انقضای یک گواهی داخلی باعث از کار افتادن بیخبر تمام سرویسها در تاریخی میشود که کسی آن را به خاطر ندارد.
آیا باید از self-signed certificate استفاده کنم یا Let's Encrypt؟
اگر سرویس دارای یک DNS name عمومی است و از طریق اینترنت قابل دسترسی است، همیشه از Let's Encrypt استفاده کنید؛ این سرویس رایگان، خودکار و از قبل توسط تمام کلاینتها مورد اعتماد است. self-signed (یا یک CA خصوصی) برای مواردی است که Let's Encrypt نمیتواند صادر کند: IPهای خصوصی، نامهای میزبان داخلی مانند .lan، شبکههای air-gapped، و سرویسهایی که عمداً پشت یک VPN پنهان شدهاند. تصمیمگیری بر اساس قابلیت دسترسی و نامگذاری است، نه قدرت رمزنگاری؛ رمزنگاری هر دو یکسان است.
چرا گواهینامه من حتی پس از اضافه شدن به /usr/local/share/ca-certificates رد میشود؟
سه مورد را بررسی کنید. فایل باید با پسوند .crt تمام شود؛ پسوند .pem نادیده گرفته میشود و update-ca-certificates خطای 0 added را گزارش میدهد. محتوا باید متن PEM و با شروع -----BEGIN CERTIFICATE----- باشد، نه فرمت باینری DER. همچنین برنامه باید واقعاً از store سیستم استفاده کند؛ Chrome در Linux، Firefox، Python requests، Node.js و Java هر کدام trust store اختصاصی خود را دارند و نیاز دارند که گواهینامه به صورت جداگانه برای آنها اضافه شود.