SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

آموزش افزودن CA اختصاصی به Trust Store در اوبونتو

با کپی کردن گواهی ریشه در مسیر /usr/local/share/ca-certificates و اجرای دستور update-ca-certificates، خطاهای SSL در سرویس‌های داخلی اوبونتو 24.04 را به سادگی برطرف کنید.

افزودن CA اختصاصی به trust store در اوبونتو

برای افزودن CA اختصاصی خود به trust store در اوبونتو، گواهی ریشه (root certificate) را در /usr/local/share/ca-certificates/ با نامی که به .crt ختم می‌شود کپی کنید و سپس دستور sudo update-ca-certificates را اجرا نمایید. یک CA (مرجع صدور گواهی) یک جفت کلید است که گواهی آن اجازه دارد سایر گواهی‌ها را امضا کند. هنگامی که سیستم به ریشه شما اعتماد کند، تمام گواهی‌هایی که توسط آن ریشه امضا شده‌اند پذیرفته می‌شوند؛ در نتیجه، خطاهای تأیید اعتبار در ارتباطات HTTPS بین سرویس‌های داخلی شما برطرف خواهد شد.

این راهنما کل زنجیره را به‌صورت آفلاین با openssl ایجاد می‌کند. شما یک کلید ریشه و یک گواهی ریشه می‌سازید، یک گواهی leaf برای یک سرور صادر می‌کنید، سپس ریشه را نصب کرده و مشاهده می‌کنید که خروجی دستور تأیید اعتبار تغییر می‌کند. این ترتیب اهمیت دارد: تأیید اعتبار قبل و بعد از نصب، روشی است که متوجه شوید نصب گواهی عامل تغییر نتیجه بوده است.

اوبونتو 24.04 به‌صورت پیش‌فرض با OpenSSL 3 و بسته ca-certificates عرضه می‌شود، بنابراین نیازی به نصب هیچ موردی در ابتدا نیست (بررسی‌شده در اوت 2026).

چه زمانی باید CA اختصاصی خود را راه‌اندازی کنید؟

یک CA عمومی مانند Let's Encrypt به نامی در DNS عمومی و سروری که در دسترس باشد نیاز دارد. نام‌های داخلی واجد شرایط نیستند. یک پایگاه داده در شبکه خصوصی یا پنل مدیریتی که به یک تونل محدود شده است، نمی‌تواند گواهی عمومی دریافت کند و هیچ‌کدام از آن‌ها نباید صرفاً برای دریافت گواهی، در معرض اینترنت قرار بگیرند.

یک گواهی خودامضا در Ubuntu تنها مشکل یک میزبان را حل می‌کند. هر کلاینت باید به آن یک گواهی اعتماد کند و برای میزبان بعدی، همین فرآیند دوباره تکرار می‌شود. یک CA خصوصی، تصمیم‌گیری را یک سطح بالاتر می‌برد. کلاینت‌ها یک‌بار به ریشه (root) اعتماد می‌کنند و پس از آن، هر گواهی که توسط آن ریشه امضا شود مورد اعتماد خواهد بود؛ این شامل گواهی‌های مربوط به میزبان‌هایی که هنوز وجود ندارند نیز می‌شود.

هزینه این کار واقعی است. کلید ریشه می‌تواند هر چیزی را که محدودیت‌ها اجازه می‌دهند امضا کند، بنابراین هر کسی که ca.key را بخواند می‌تواند گواهی‌هایی صادر کند که ماشین‌های شما آن‌ها را می‌پذیرند. از آن همان‌طور محافظت کنید که از یک کلید خصوصی در مدیریت کلیدهای SSH محافظت می‌کنید. اگر سرویسی دارای نام DNS عمومی است، از تمام این مراحل صرف‌نظر کرده و از یک CA عمومی استفاده کنید: Certbot با nginx و Let's Encrypt کار کمتری می‌برد و نیازی به نصب هیچ‌چیزی در سمت کلاینت ندارد.

ایجاد کلید CA و گواهی ریشه

در دایرکتوری‌ای کار کنید که فقط کاربر شما امکان باز کردن آن را دارد. کلید ریشه هرگز نباید از این دایرکتوری خارج شود.

install -d -m 700 ~/ca
cd ~/ca
openssl genrsa -aes256 -out ca.key 4096
chmod 600 ca.key

دستور -aes256 کلید را با رمز عبوری که انتخاب می‌کنید رمزنگاری می‌کند و هر دستور بعدی که با این کلید امضا انجام دهد، آن رمز را درخواست خواهد کرد. اگر -aes256 را حذف کنید، کلید به‌صورت متن ساده (clear) روی دیسک ذخیره می‌شود؛ در این صورت، یک نسخه پشتیبان یا دسترسی یک مدیر سیستم دیگر کافی است تا فرد بتواند گواهی‌هایی صادر کند که ماشین‌های شما به آن‌ها اعتماد دارند.

اکنون گواهی ریشه را ایجاد کنید که کلید CA آن را برای خودش امضا می‌کند.

openssl req -x509 -new -key ca.key -sha256 -days 3650 \
  -subj "/O=Example Internal/CN=Example Internal Root CA" \
  -addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
  -addext "keyUsage=critical,keyCertSign,cRLSign" \
  -addext "subjectKeyIdentifier=hash" \
  -addext "nameConstraints=critical,permitted;DNS:internal.example" \
  -out ca.crt

عبارت internal.example را با پسوند نامی که واقعاً استفاده می‌کنید جایگزین کنید و پیش از حفظ کردن آن پسوند نهایی، بخش بعدی را مطالعه کنید.

هر extension یک وظیفه مشخص دارد:

  • basicConstraints به همراه CA:TRUE همان چیزی است که این گواهی را به یک گواهی CA تبدیل می‌کند. بدون آن، کلاینت هر گواهی‌ای که این کلید امضا کند را رد می‌کند، حتی اگر خود امضا معتبر باشد.
  • pathlen:0 مشخص می‌کند که CA مجاز به امضای گواهی‌های نهایی (leaf certificates) است و نمی‌تواند CAهای دیگری در زیرمجموعه خود ایجاد کند.
  • keyUsage کلید را محدود به امضای گواهی‌ها و لیست‌های ابطال (revocation lists) می‌کند تا از استفاده اشتباه آن به عنوان کلید سرور TLS جلوگیری شود.
  • subjectKeyIdentifier به ریشه یک شناسه می‌دهد که گواهی‌های نهایی به آن ارجاع می‌دهند؛ این همان روشی است که کلاینت از طریق آن صادرکننده صحیح را در میان صدها گواهی موجود در مخزن خود پیدا می‌کند.
  • nameConstraints نام‌هایی که این CA مجاز به تأیید آن‌هاست را محدود می‌کند.

به جای فرض کردن اینکه دستور دقیقاً همان کاری را انجام داده که مد نظر شما بوده، خروجی را بازبینی کنید.

openssl x509 -noout -subject -issuer -serial -dates -in ca.crt
openssl x509 -noout -text -in ca.crt

بخش‌های Subject و issuer رشته یکسانی را نمایش می‌دهند، زیرا یک گواهی ریشه خودش را امضا می‌کند. شماره سریال و دو تاریخ ذکر شده از فایلی که همین الان ایجاد کردید استخراج می‌شوند، بنابراین آن‌ها را از خروجی همان دستور بردارید و نه از راهنمای هیچ شخص دیگری.

محدود کردن دامنه‌های مجاز برای امضای CA

یک ریشه (root) در مخزن سیستم، برای تمام نام‌های موجود در اینترنت قابل اعتماد است، مگر آنکه خلاف آن را تعیین کنید. این حجم عظیمی از اختیارات است که در یک فایل روی یک سرور نگهداری می‌شود. nameConstraints این اختیارات را کاهش می‌دهد. با وجود permitted;DNS:internal.example در ریشه، زنجیره‌ای از این CA برای نامی خارج از internal.example رد می‌شود، حتی اگر امضا معتبر باشد.

به جای اعتماد کردن، آن را تست کنید.

openssl req -new -newkey rsa:2048 -nodes -keyout /tmp/outside.key -out /tmp/outside.csr \
  -subj "/CN=www.example.com"
openssl x509 -req -in /tmp/outside.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 30 -sha256 \
  -extfile <(printf 'subjectAltName=DNS:www.example.com\n') -out /tmp/outside.crt
openssl verify -CAfile ca.crt /tmp/outside.crt
echo $?

گواهی صادر می‌شود، زیرا CA شما هر چه از آن بخواهید را امضا می‌کند. مرحله تایید (Verification) جایی است که فرآیند متوقف می‌شود: وضعیت خروجی غیر صفر است و OpenSSL محدودیتی که با آن برخورد کرده را نام می‌برد. این ارزش این افزونه (extension) است. یک کلید CA سرقت‌شده همچنان نمی‌تواند یک گواهی معتبر برای نامی خارج از زیردرخت (subtree) تولید کند. پس از اتمام کار، فایل‌های باقی‌مانده را با rm /tmp/outside.* حذف کنید.

چهار نکته که باید پیش از اعمال محدودیت بدانید. این مورد با critical علامت‌گذاری شده است، بنابراین کلاینتی که این افزونه را درک نمی‌کند باید زنجیره را رد کند و نه اینکه آن را نادیده بگیرد؛ این رویکرد امن است اما ممکن است کتابخانه‌های قدیمی TLS را غافلگیر کند. یک زیردرخت مجاز برای نام‌های DNS، محدودیت‌های SAN برای آدرس‌های IP را اعمال نمی‌کند، زیرا نوع نامی که زیردرخت برای آن لیست نشده باشد بدون محدودیت باقی می‌ماند؛ بنابراین اگر گواهی‌های شما شامل آدرس‌های IP هستند، permitted;IP:10.0.0.0/255.255.0.0 را نیز در همان افزونه اضافه کنید. زیردرخت باید تمام نام‌هایی که در آینده صادر خواهید کرد را پوشش دهد، از جمله نام‌های میزبان کوتاه؛ بنابراین گواهی برای نام ساده app در برابر مثال بالا با شکست مواجه می‌شود. همچنین این محدودیت در ریشه نهادینه شده است، بنابراین تغییر نظر شما به معنای نیاز به یک گواهی ریشه جدید و نصب مجدد روی تمام کلاینت‌ها خواهد بود.

صدور گواهی leaf امضا شده توسط CA شما

گواهی leaf همان گواهی است که سرور به کلاینت‌ها ارائه می‌دهد. کار را با کلید اختصاصی آن و یک CSR (درخواست امضای گواهی) شروع کنید؛ این فایل حاوی کلید عمومی و نام درخواستی است و با کلید leaf امضا می‌شود تا ثابت کند درخواست‌کننده، نیمه خصوصی کلید را در اختیار دارد.

openssl req -new -newkey rsa:2048 -nodes \
  -keyout app.key -out app.csr \
  -subj "/CN=app.internal.example"
chmod 600 app.key

نام‌هایی که اهمیت دارند باید در یک فایل extension قرار بگیرند، نه در CSR. کلاینت‌ها نام میزبان را با subjectAltName (SAN) تطبیق می‌دهند و Common Name را کاملاً نادیده می‌گیرند. بنابراین، گواهی‌ای که فقط دارای CN باشد و فاقد SAN باشد، در تمام کلاینت‌های امروزی در مرحله اعتبارسنجی نام میزبان شکست می‌خورد، فارغ از اینکه CN چه چیزی را نشان می‌دهد.

basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:app.internal.example, DNS:api.internal.example
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

آن را با نام app.ext ذخیره کنید و سپس درخواست را با CA امضا کنید.

openssl x509 -req -in app.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 397 -sha256 -extfile app.ext -out app.crt

-CAcreateserial فایل ca.srl را در کنار CA می‌نویسد که حاوی شماره سریال بعدی است تا هیچ دو گواهی‌ای از این CA شماره یکسانی نداشته باشند. این فایل را در دایرکتوری CA نگه دارید. -days 397 یک انتخاب است، نه محدودیت ابزار. طول عمر کوتاه در اینجا اهمیت بیشتری نسبت به یک CA عمومی دارد، زیرا یک CA خصوصی زیرساخت ابطال (revocation) ندارد: هیچ CRL و پاسخ‌دهنده OCSP وجود ندارد مگر اینکه خودتان یکی بسازید؛ بنابراین اگر کلید leaf لو برود، تا زمان انقضای گواهی قابل استفاده باقی می‌ماند.

پیش از آنکه به سراغ trust store بروید، نتیجه را بررسی کنید.

openssl x509 -noout -subject -issuer -serial -dates -in app.crt
openssl x509 -noout -ext subjectAltName -in app.crt

خط issuer اکنون نام CA را نشان می‌دهد، نه خودِ leaf را. خط SAN نام‌هایی را فهرست می‌کند که گواهی برای آن‌ها معتبر است و کلاینت فقط با آن فهرست تطبیق انجام می‌دهد و نه هیچ چیز دیگر.

تأیید با استفاده از -CAfile صریح، پیش از نصب هر چیزی

openssl verify -CAfile ca.crt app.crt
echo $?

این دستور یک پرسش محدود را مطرح می‌کند: آیا app.crt به گواهی موجود در ca.crt زنجیره می‌شود؟ این دستور هیچ چیزی درباره آنچه این ماشین به آن اعتماد دارد نمی‌گوید، زیرا شما root را در خط فرمان به OpenSSL داده‌اید. شکست در اینجا به معنای وجود مشکل در خود گواهی‌ها است، بنابراین پیش از ادامه، آن را رفع کنید.

اکنون از ماشین پرسش کنید.

openssl verify app.crt
echo $?

بدون -CAfile، ابزار OpenSSL به دایرکتوری گواهی پیش‌فرض خود بازمی‌گردد. openssl version -d دایرکتوری پایه مورد استفاده در build شما را چاپ می‌کند و در Ubuntu، دایرکتوری certs در زیر آن به /etc/ssl/certs ارجاع می‌دهد. root شما هنوز در آنجا نیست، بنابراین تأیید شکست می‌خورد: زنجیره به صادرکننده‌ای می‌رسد که در store موجود نیست و جای دیگری برای جستجو باقی نمانده است. به وضعیت خروج (exit status) توجه کنید. این همان چیزی است که دو مرحله بعد تغییر خواهد کرد.

یک کلاینت واقعی تست بهتری نسبت به openssl verify ارائه می‌دهد، زیرا علاوه بر زنجیره، نام میزبان (hostname) را نیز بررسی می‌کند. گواهی را سرو کنید و آن را دریافت کنید.

openssl s_server -accept 8443 -cert app.crt -key app.key -www &
curl --resolve app.internal.example:8443:127.0.0.1 https://app.internal.example:8443/

--resolve اتصال را به 127.0.0.1 می‌فرستد در حالی که همچنان درخواست app.internal.example را دارد، بنابراین SAN مطابقت دارد و تنها پرسش باقی‌مانده، اعتماد است. curl شکست می‌خورد و دلیلی که نتوانسته زنجیره را تأیید کند چاپ می‌کند. برای جزئیات بیشتر -v را اضافه کنید. سرور تست را در حال اجرا باقی بگذارید.

نصب ریشه (root) در مسیر /usr/local/share/ca-certificates

sudo cp ca.crt /usr/local/share/ca-certificates/example-internal-root.crt
sudo chmod 644 /usr/local/share/ca-certificates/example-internal-root.crt
sudo update-ca-certificates

جزئیاتی که تعیین می‌کنند آیا این کار به درستی انجام می‌شود یا خیر:

  • نام فایل حتماً باید به .crt ختم شود. صفحه راهنمای update-ca-certificates بیان می‌کند که گواهی‌های دارای پسوند .crt که در زیرشاخه /usr/local/share/ca-certificates یافت شوند، گنجانده شده و به‌طور ضمنی مورد اعتماد قرار می‌گیرند. فایلی با نام root.pem یا root.cer بدون هیچ هشداری نادیده گرفته می‌شود.
  • محتوا باید با فرمت PEM باشد؛ یعنی بلوک base64 که بین خطوط BEGIN CERTIFICATE و END CERTIFICATE قرار گرفته است. فایلی با فرمت DER که به .crt تغییر نام یافته، همچنان باینری باقی می‌ماند و خوانده نمی‌شود. آن را با استفاده از openssl x509 -inform DER -in ca.der -out ca.crt تبدیل کنید.
  • فقط گواهی ریشه (root) باید در اینجا قرار گیرد. کلید خصوصی CA و گواهی leaf هیچ جایگاهی در یک trust store ندارند.

دستور update-ca-certificates تعداد گواهی‌های اضافه یا حذف شده را چاپ می‌کند. اگر هیچ گواهی اضافه نشد، دلیل آن پسوند یا فرمت فایل است.

تغییرات را از سمت سیستم تأیید کنید، نه صرفاً بر اساس آن پیام.

ls -l /etc/ssl/certs/$(openssl x509 -noout -subject_hash -in /usr/local/share/ca-certificates/example-internal-root.crt).0
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt

دستور اول، نام فایلی را بر اساس hash موضوع گواهی شما می‌سازد و آن را لیست می‌کند. update-ca-certificates آن symlink را ایجاد کرده است و به فایلی که نصب کرده‌اید اشاره می‌کند. دستور دوم، تعداد گواهی‌ها را در bundle تک‌فایلی می‌شمارد. این دستور را پیش از نصب نیز اجرا کنید تا بتوانید تغییر تعداد را به اندازه یک واحد مشاهده کنید.

هنگامی که این ریشه را به ماشین‌های دیگر کپی می‌کنید، پیش از نصب بررسی کنید که فایل کپی‌شده سالم باشد. گواهی ریشه بدترین فایلی است که ممکن است در سیستم دچار نقص شود، بنابراین با آن مانند هر دانلود دیگری رفتار کنید که پیش از استفاده باید با checksum تأیید شود.

تأیید مجدد در برابر استور سیستم

openssl verify app.crt
echo $?
curl --resolve app.internal.example:8443:127.0.0.1 https://app.internal.example:8443/

همان دستورات، همان فایل‌های گواهی، اما پاسخ متفاوت است. هیچ تغییری در app.crt ایجاد نشده و سرور همان سروری است که قبلاً راه‌اندازی کردید. تنها تفاوت این است که اکنون root در استوری قرار دارد که کلاینت‌ها آن را می‌خوانند، بنابراین زنجیره کامل می‌شود. این مکانیزمی است که ارزش به خاطر سپردن دارد: تأیید اعتبار، جستجو برای صادرکننده‌ای است که کلاینت از قبل به آن اعتماد دارد و نصب یک CA روشی است که صادرکننده به محلی که کلاینت در آن جستجو می‌کند، وارد می‌شود.

سرور تست را با kill %1 متوقف کنید.

چرا نباید فایل‌های خود را در /etc/ssl/certs قرار دهید

/etc/ssl/certs یک خروجی تولیدشده است. update-ca-certificates آن را با پیوندهای نمادین (symlink) به فایل‌های اصلی گواهی پر می‌کند و بسته الحاقی /etc/ssl/certs/ca-certificates.crt را در کنار آن‌ها می‌نویسد.

گواهی‌ای که به‌صورت دستی در این دایرکتوری کپی می‌کنید، توسط هیچ برنامه‌ای شناسایی نمی‌شود. جستجوی دایرکتوری OpenSSL فقط فایل‌هایی را باز می‌کند که بر اساس هش موضوع (subject hash) گواهی نام‌گذاری شده‌اند؛ بنابراین فایلی با نام myca.crt برای آن نامرئی است. ابزار curl در Ubuntu فایل bundle را می‌خواند و این فایل از منابع ثبت‌شده بازسازی می‌شود، پس کپی شما در آن مسیر نیز وجود ندارد. با اجرای update-ca-certificates --fresh، پیوندهای نمادین موجود در دایرکتوری حذف و دوباره ساخته می‌شوند که در نتیجه، هر پیوند دستی که ایجاد کرده‌اید نیز از بین می‌رود.

بخش دیگر این ساختار، /usr/share/ca-certificates است که متعلق به بسته ca-certificates بوده و در /etc/ca-certificates.conf فهرست شده است. به‌روزرسانی‌های بسته، این فایل را بازنویسی می‌کنند. /usr/local/share/ca-certificates دایرکتوری رزرو شده برای مدیر سیستم (local administrator) است؛ بنابراین گواهی CA شما از هرگونه ارتقای بسته‌ای که مدیریت سایر بخش‌ها را بر عهده دارد، جان سالم به در می‌برد.

کدام برنامه‌ها از trust store سیستم صرف‌نظر می‌کنند

نصب root CA باعث اصلاح وضعیت برای تمام برنامه‌هایی می‌شود که از OpenSSL استفاده می‌کنند یا /etc/ssl/certs را می‌خوانند. این شامل curl، wget، git، ماژول استاندارد ssl در پایتون و برنامه‌های نوشته‌شده با Go می‌شود که فایل‌های سیستم را در لینوکس می‌خوانند. محیط‌های اجرایی (Runtimes) که لیست گواهی‌های اختصاصی خود را دارند، تحت تأثیر قرار نمی‌گیرند و بیشتر سردرگمی‌ها پس از نصب موفقیت‌آمیز، ناشی از همین موضوع است.

  • Node.js از لیستی استفاده می‌کند که در زمان کامپایل در آن گنجانده شده است. با استفاده از NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/example-internal-root.crt که پیش از شروع پردازش در محیط (environment) تنظیم می‌شود، آن را به سمت root خود هدایت کنید، زیرا Node این متغیر را فقط یک‌بار در زمان راه‌اندازی می‌خواند. نسخه‌های فعلی Node گزینه‌ای برای خواندن از trust store سیستم نیز دارند؛ برای بررسی اینکه آیا نسخه شما از آن پشتیبانی می‌کند، node --help | grep -i system-ca را اجرا کنید.
  • کتابخانه requests در پایتون از باندل certifi استفاده می‌کند. برای این پردازش، REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt را تنظیم کنید یا verify="/etc/ssl/certs/ca-certificates.crt" را به فراخوانی مربوطه پاس دهید. pip نیز به همین دلیل از --cert استفاده می‌کند.
  • Java یک keystore را می‌خواند. در اوبونتو، بسته ca-certificates-java یک hook در مسیر /etc/ca-certificates/update.d/ نصب می‌کند، بنابراین در صورت وجود این بسته، update-ca-certificates keystore جاوا را نیز به‌روزرسانی می‌کند. در غیر این صورت، root را با keytool -importcert وارد (import) کنید.
  • Firefox از store اختصاصی خود استفاده می‌کند و هرگز به /etc/ssl/certs نگاه نمی‌کند. گواهی را از طریق تنظیمات گواهی (certificate settings) در خود مرورگر وارد کنید. Chromium در لینوکس یک دیتابیس NSS برای هر کاربر می‌خواند که می‌توانید آن را با certutil از بسته libnss3-tools ویرایش کنید.
  • کانتینرها فایل‌سیستم اختصاصی خود را دارند، بنابراین store میزبان (host) در داخل آن‌ها بی‌معنی است. root را در image کپی کنید و در طول فرآیند build، دستور update-ca-certificates را اجرا کنید. اگر سرویس‌های شما تحت Docker Compose on a VPS اجرا می‌شوند، برای این مورد برنامه‌ریزی کنید.

اگر برنامه‌ای پس از نصب تمیز همچنان گواهی را نمی‌پذیرد، پیش از هر تغییری بررسی کنید که آن برنامه چه فایل‌هایی را باز می‌کند. strace -f -e trace=openat <command> 2>&1 | grep -i cert ابزاری مستقیم است که پاسخ این سؤال را در یک اجرا مشخص می‌کند.

حفظ قابلیت استفاده از CA در طول زمان

صدور مجدد یک گواهی leaf شامل تکرار مراحل CSR و امضا با استفاده از همان فایل app.ext است. کلاینت‌ها نیازی به انجام هیچ اقدامی ندارند، زیرا ریشه‌ای که به آن اعتماد دارند تغییر نکرده است. فایل ca.srl و تمامی فایل‌های .ext را در دایرکتوری CA نگه دارید تا صدور بعدی، تکرار دستوری باشد که قبلاً کار کرده است، نه بازسازی آن از روی حافظه.

از ca.key و ca.crt در جایی خارج از دستگاه نسخه پشتیبان تهیه کنید و همچنان آن‌ها را رمزنگاری‌شده نگه دارید. اگر کلید را گم کنید، دیگر قادر به صدور هیچ گواهی جدیدی نخواهید بود: در آن صورت مجبور می‌شوید یک CA دوم بسازید و ریشه آن را در تمام نقاطی که ریشه اول نصب شده بود، جایگزین کنید. فهرستی مکتوب از تمام ماشین‌ها و مخازن گواهی برنامه‌هایی که ریشه را دریافت کرده‌اند نگه دارید، زیرا همین فهرست است که چرخش (rotation) و حذف گواهی‌ها را ممکن می‌سازد.

هنگامی که تاریخ انقضای خودِ ریشه نزدیک می‌شود، جایگزین آن را زودتر تولید کرده و هر دو ریشه را در کنار هم نصب کنید. وجود دو ریشه در مخزن گواهی‌ها مشکلی ایجاد نمی‌کند و کلاینت هر کدام را بپذیرد، معتبر است. گواهی‌های leaf را با ریشه جدید مجدداً صادر کنید و سپس پس از آنکه دیگر هیچ سرویسی به ریشه قدیمی وابسته نبود، آن را حذف کنید.

حذف یک CA از trust store

sudo rm /usr/local/share/ca-certificates/example-internal-root.crt
sudo update-ca-certificates --fresh

دستور --fresh پیوندهای نمادین (symlinks) موجود در /etc/ssl/certs را حذف کرده و آن‌ها را بر اساس منابعی که همچنان باقی مانده‌اند بازسازی می‌کند؛ بنابراین با حذف root، این فایل‌ها از دایرکتوری و bundle خارج می‌شوند. حذف را به همان روشی که نصب را تأیید کردید، بررسی کنید.

openssl verify app.crt
echo $?
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt
ls -l /etc/ssl/certs/$(openssl x509 -noout -subject_hash -in ~/ca/ca.crt).0

تأییدیه دوباره با شکست مواجه می‌شود، تعداد گواهی‌ها به مقدار اولیه بازمی‌گردد و پیوند نمادین hash ناپدید می‌شود.

آن دستور فقط store سیستم را تغییر می‌دهد و به جای دیگری دسترسی ندارد. نصب را در هر یک از مکان‌های دیگر به‌صورت دستی لغو کنید: محتویات NODE_EXTRA_CA_CERTS را پاک کنید، alias را از هر Java keystore حذف نمایید، root را از پروفایل هر مرورگر بردارید و هر image کانتینری که آن را در خود جای داده است، بازسازی کنید. حذف root همچنین گواهی‌هایی را که امضا کرده است، باطل نمی‌کند. آن‌ها روی هر ماشینی که همچنان به آن اعتماد دارد معتبر باقی می‌مانند؛ این دلیل عملی است که یک CA خصوصی به لیستی مکتوب از مکان‌هایی که root در آن‌ها توزیع شده، نیاز دارد. CAای که نتوانید به‌طور کامل آن را پس بگیرید، یک حفره دائمی است؛ بنابراین حذف آن را در همان روزی که راه‌اندازی می‌کنید، روی یک ماشین تست کنید، تا زمانی که لیست هنوز کوتاه است.

FAQ

گواهی CA را در اوبونتو کجا قرار دهم؟

در /usr/local/share/ca-certificates/، با نام فایلی که به .crt ختم می‌شود و محتوای PEM دارد؛ سپس دستور sudo update-ca-certificates را اجرا کنید. آن دایرکتوری برای مدیر سیستم رزرو شده است، بنابراین ارتقای بسته‌ها تغییری در آن ایجاد نمی‌کند. مسیر /usr/share/ca-certificates متعلق به بسته ca-certificates است و /etc/ssl/certs از هر دو مسیر تولید می‌شود، بنابراین فایلی که در هر یک از آن‌ها قرار گیرد، بازنویسی یا نادیده گرفته می‌شود.

چرا curl پس از اجرای update-ca-certificates همچنان گواهی را رد می‌کند؟

علت‌ها را به ترتیب بررسی کنید. ممکن است فایل به .crt ختم نشود یا به جای PEM از فرمت DER باشد که در این صورت update-ca-certificates آن را نادیده گرفته و چیزی اضافه نکرده است. ممکن است گواهی فاقد subjectAltName منطبق با نام میزبان باشد که این یک خطای نام میزبان است، نه خطای اعتماد؛ با استفاده از openssl x509 -noout -ext subjectAltName -in app.crt بررسی کنید. ممکن است سرور فقط گواهی leaf را ارسال کند در حالی که به یک intermediate نیاز است. ممکن است curl توسط CURL_CA_BUNDLE یا --cacert به یک bundle متفاوت اشاره کند. همچنین سرویس‌هایی که طولانی‌مدت اجرا می‌شوند نیاز به restart دارند، زیرا اکثر برنامه‌ها هنگام شروع، trust store را فقط یک‌بار می‌خوانند.

آیا trust store سیستم شامل Firefox، Chrome، Node و Java می‌شود؟

خیر. curl، wget، git، ماژول استاندارد ssl در پایتون و برنامه‌های Go فایل‌های سیستم را می‌خوانند، بنابراین به محض اجرای update-ca-certificates کار می‌کنند. Firefox از store اختصاصی خود استفاده می‌کند. Chromium در لینوکس از یک دیتابیس NSS برای هر کاربر استفاده می‌کند که با certutil از بسته libnss3-tools ویرایش می‌شود. Node.js نیاز دارد که NODE_EXTRA_CA_CERTS به فایل root شما اشاره کند. Java یک keystore می‌خواند که update-ca-certificates تنها زمانی آن را به‌روز می‌کند که بسته ca-certificates-java نصب شده باشد. ماژول requests در پایتون از certifi استفاده می‌کند و نیاز به REQUESTS_CA_BUNDLE دارد.

چگونه یک CA را از trust store اوبونتو حذف کنم؟

فایل را از /usr/local/share/ca-certificates/ حذف کرده و sudo update-ca-certificates --fresh را اجرا کنید. گزینه --fresh پیوندهای نمادین (symlinks) در /etc/ssl/certs را پاک کرده و دوباره می‌سازد، بنابراین گواهی همزمان از پیوندهای هش و bundle مربوط به ca-certificates.crt خارج می‌شود. با اجرای openssl verify روی گواهی‌ای که توسط آن CA امضا شده و بررسی وضعیت خروج (exit status)، حذف را تأیید کنید. سپس حذف را در هر store دیگری که آن را اضافه کرده بودید تکرار کنید، زیرا آن دستور هیچ‌کدام از آن‌ها را تغییر نمی‌دهد.

آیا می‌توانم برای یک سایت عمومی به جای Let's Encrypt از یک CA خصوصی استفاده کنم؟

خیر. مرورگر بازدیدکننده هرگز root شما را ندیده است، بنابراین یک هشدار تمام‌صفحه نمایش می‌دهد و شما نمی‌توانید root خود را روی دستگاه‌هایی که کنترل نمی‌کنید نصب کنید. CA خصوصی برای نام‌هایی است که فقط دستگاه‌های خودتان آن‌ها را resolve می‌کنند و برای کلاینت‌هایی است که مدیریت می‌کنید. برای هر چیزی که یک غریبه بازدید می‌کند، گواهی را از یک CA عمومی دریافت کنید.

#tls#certificates#openssl#ubuntu#security#pki