SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-07

Certyfikat z podpisem własnym Ubuntu 24.04: konfiguracja

Instrukcja generowania certyfikatu TLS z SAN dla Ubuntu 24.04. Dowiedz się, jak poprawnie skonfigurować nginx lub Apache i dodać zaufanie w systemie bez użycia curl -k.

Co budujesz

Certyfikat TLS z podpisem własnym, który jest akceptowany przez nowoczesne przeglądarki i klientów, poprawne subjectAltName, bezpieczne uprawnienia do kluczy, zintegrowane z nginx lub Apache, oraz element, który pomija niemal każdy poradnik: poprawne skonfigurowanie zaufania u klientów, zamiast ciągłego ignorowania ostrzeżeń i wpisywania na sztywno curl -k w skryptach. Na końcu znajduje się pięciokomendowy prywatny CA na wypadek, gdy jedna usługa wewnętrzna zmieni się w sześć.

Najpierw decyzja, ponieważ certyfikat z podpisem własnym jest właściwym narzędziem znacznie rzadziej, niż jest używany. Jeśli usługa jest dostępna z publicznego Internetu pod prawdziwą nazwą DNS, przerwij czytanie i uzyskaj darmowy certyfikat Let's Encrypt za pomocą certbot na nginx lub odpowiednik dla Apache. Jest darmowy, odnawia się automatycznie, a każda przeglądarka na świecie już mu ufa. Certyfikat z podpisem własnym na publicznej stronie uczy użytkowników ignorowania ostrzeżeń bezpieczeństwa, co jest gorszym nawykiem niż zwykłe HTTP.

Certyfikat z podpisem własnym jest właściwym narzędziem, gdy publiczny Internet nie jest zaangażowany: panel administracyjny powiązany z adresem tunelu WireGuard na Twoim VPS, serwer testowy w sieci prywatnej, ruch między backendami, urządzenie w domowym laboratorium lub zastąpienie certyfikatu zastępczego, który Webmin generuje dla siebie na porcie 10000. Let's Encrypt i tak nie może wystawić certyfikatu dla 10.8.0.1 lub git.internal.lan, żaden publiczny CA nie umieści prywatnego adresu IP ani wymyślonej domeny najwyższego poziomu w certyfikacie. Dla tych nazw to Ty jesteś CA.

Wszystko poniżej działa na świeżej instalacji Ubuntu 24.04, która zawiera OpenSSL 3.0.x (openssl version w celu weryfikacji). Nic tutaj nie wymaga dostępu do Internetu; całość działa w środowisku odizolowanym (air-gapped).

Dlaczego stary jednowierszowy skrypt generuje certyfikaty odrzucane przez Chrome

Polecenie, które można znaleźć w każdym poradniku sprzed 2017 roku, wygląda następująco:

# 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

Wymusza ono udzielenie odpowiedzi na serię pytań interaktywnych, umieszcza nazwę hosta w polu Common Name i generuje certyfikat bez rozszerzenia subjectAltName. Taki certyfikat jest bezużyteczny w momencie utworzenia. Przeglądarka Chrome przestała odczytywać pole Common Name w wersji 58, w kwietniu 2017 roku, mimo że RFC 2818 uznało dopasowywanie CN za przestarzałe już w roku 2000. Przeglądarki Firefox, Safari, a także narzędzia curl i Python zachowują się w ten sam sposób. Certyfikat identyfikuje serwer wyłącznie poprzez rozszerzenie SAN. W przeciwnym razie przeglądarka wyświetli następujący komunikat:

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.

Żadne manipulacje w magazynie zaufanych certyfikatów nie naprawią tego błędu, ponieważ certyfikat w rzeczywistości nie wskazuje żadnej nazwy. Jeśli obecnie widzisz błąd NET::ERR_CERT_COMMON_NAME_INVALID, oznacza to, że certyfikat nie posiada rozszerzenia SAN (lub zawiera błędne dane) i konieczne jest wygenerowanie nowego. Na szczęście rozwiązanie sprowadza się do jednego polecenia.

Generowanie certyfikatu akceptowanego przez przeglądarki: jedno polecenie

Narzędzie OpenSSL zyskało flagę -addext w wersji 1.1.1, co eliminuje konieczność stosowania skomplikowanych plików konfiguracyjnych, wymaganych w starszych poradnikach do dodania rozszerzenia SAN. W systemie 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"

Działanie poszczególnych flag:

  • -x509 generuje certyfikat z podpisem własnym bezpośrednio, zamiast tworzyć żądanie podpisania.
  • -newkey rsa:4096 tworzy nowy klucz w tym samym kroku. RSA 4096 jest obsługiwane przez wszystkie starsze klienty; jeśli wszystkie łączące się urządzenia są nowoczesne, -newkey ec -pkeyopt ec_paramgen_curve:P-256 będzie mniejsze i szybsze.
  • -noenc to zapis używany w OpenSSL 3.x, zastępujący starsze -nodes: brak hasła dla klucza. Oba zapisy działają. Klucz zabezpieczony hasłem powoduje, że nginx oczekuje na wprowadzenie danych przy każdym starcie, dlatego w przypadku serwera należy użyć tej opcji.
  • -days 730, dwa lata; więcej informacji na temat tej wartości znajduje się w sekcji dotyczącej wygasania.
  • -subj automatycznie odpowiada na pytania interaktywne. Pole CN ma obecnie znaczenie czysto informacyjne, ale warto ustawić w nim główną nazwę; niektóre narzędzia wyświetlają ją w interfejsie.
  • -addext "subjectAltName=..." to kluczowa flaga. Należy wymienić każdą nazwę i każdy adres IP, których będą używać klienci: wpisy DNS: dla nazw hostów (symbole wieloznaczne, takie jak DNS:*.internal.lan, są dozwolone), wpisy IP: dla adresów. Jeśli ktokolwiek będzie łączył się z https://10.8.0.1, wpis IP:10.8.0.1 musi się znajdować w certyfikacie; w przeciwnym razie certyfikat zawierający tylko DNS SAN spowoduje wyświetlenie błędu NET::ERR_CERT_COMMON_NAME_INVALID.

Przed wdrożeniem certyfikatu należy zweryfikować, czy pole SAN zostało poprawnie dodane:

openssl x509 -in /etc/ssl/certs/git.internal.crt -noout -ext subjectAltName

Prawidłowy wynik:

X509v3 Subject Alternative Name:
    DNS:git.internal.lan, IP Address:10.8.0.1

Jeśli zamiast tego wyświetlony zostanie komunikat No extensions in certificate, certyfikat nie posiada pola SAN i zostanie odrzucony przez przeglądarki. W takim przypadku należy wygenerować certyfikat ponownie, zamiast kontynuować konfigurację.

Zabezpieczanie klucza prywatnego

Klucz prywatny dostępny do odczytu dla każdego użytkownika w systemie nie jest kluczem prywatnym. W systemie Ubuntu katalog /etc/ssl/private jest już zabezpieczony przez 710 root:ssl-cert, co chroni przed przypadkowym wglądem, jednak uprawnienia do samego pliku należy ustawić jawnie:

sudo chown root:root /etc/ssl/private/git.internal.key
sudo chmod 600 /etc/ssl/private/git.internal.key

Zarówno nginx, jak i Apache odczytują certyfikaty z uprawnieniami użytkownika root przed obniżeniem uprawnień, dlatego tryb root:root 600 jest dla nich odpowiedni. Jeśli klucz jest przeznaczony dla usługi działającej na własnym koncie użytkownika, która samodzielnie ładuje klucz, na przykład aplikacji Node, Gitea lub demona Python, należy chown go temu użytkownikowi, zachowując tryb 600. Nigdy nie należy stosować trybu 644, przechowywać kopii w repozytorium git ani tworzyć kopii w /tmp.

Konfiguracja w 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 nginx

nginx -t musi zwrócić syntax is ok oraz test is successful, zanim przeładowanie przyniesie jakikolwiek skutek. Jeśli zamiast tego pojawi się SSL_CTX_use_PrivateKey_file() failed ... key values mismatch, oznacza to, że certyfikat i klucz pochodzą z dwóch różnych cykli generowania; należy zapoznać się z sekcją dotyczącą trybów awarii.

Integracja z Apache

sudo a2enmod ssl proxy proxy_http

Samo ssl jest w tym przypadku niewystarczające: poniższy vhost korzysta z ProxyPass, a bez mod_proxy oraz mod_proxy_http test konfiguracji kończy się błędem Invalid command 'ProxyPass', perhaps misspelled or defined by a module not included in the server configuration. Zapisz vhost jako /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

configtest powinno zwrócić Syntax OK. Teraz wykonaj test z maszyny klienckiej:

curl -v https://git.internal.lan/

otrzymasz błąd:

curl: (60) SSL certificate problem: self-signed certificate

Nie jest to błąd oprogramowania. To poprawne działanie TLS: curl nie posiada informacji o certyfikacie i odmawia komunikacji z serwerem, którego nie może uwierzytelnić. Kolejna sekcja zawiera właściwe rozwiązanie, które różni się od powszechnie stosowanych w sieci praktyk.

Zapewnienie zaufania klientów i antywzorce, których należy unikać

Najpierw błędne rozwiązania, nazwane zgodnie z ich naturą. curl -k (lub --insecure) wkompilowane w skrypt, verify=False w Python requests, NODE_TLS_REJECT_UNAUTHORIZED=0 w Node – żadne z nich nie sprawia, że certyfikat staje się zaufany. Powodują one wyłączenie weryfikacji certyfikatu, co oznacza, że klient będzie bezkrytycznie komunikował się z każdym serwerem przedstawiającym jakikolwiek certyfikat, w tym z tym podstawionym przez atakującego. Utrzymuje się narzut TLS, tracąc jednocześnie uwierzytelnianie, które było jego głównym celem. Co gorsza, te flagi rozprzestrzeniają się: wklejone do jednego zadania cron, potem do skryptu wdrożeniowego, a następnie do kodu produkcyjnego, aż nikt nie pamięta, które połączenia miały być tymczasowe. Jeśli verify=False przetrwa sesję debugowania, w której powstało, oznacza to błąd w projekcie.

Właściwym rozwiązaniem jest nauczenie systemu operacyjnego klienta, że dany certyfikat jest zaufanym certyfikatem głównym (root). Na klientach Ubuntu i Debian:

sudo cp git.internal.crt /usr/local/share/ca-certificates/git.internal.crt
sudo update-ca-certificates

Istotna linia w wyniku (po niej następuje blok Running hooks in /etc/ca-certificates/update.d...):

Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.

W tych liniach kryją się dwie pułapki. Plik musi kończyć się rozszerzeniem .crt; rozszerzenie .pem jest ignorowane bez ostrzeżenia, co skutkuje błędem 0 added bez żadnego komunikatu. Zawartość musi być w formacie PEM, plik musi zaczynać się od -----BEGIN CERTIFICATE-----; pliki binarne DER należy najpierw przekonwertować za pomocą openssl x509 -inform der -in file.der -out file.crt. Dodanie certyfikatu z podpisem własnym jako głównego działa, ponieważ certyfikat z podpisem własnym jest swoim własnym certyfikatem głównym.

Po wykonaniu tych czynności curl, wget, git, apt oraz każde inne narzędzie używające OpenSSL w oparciu o systemowy zestaw certyfikatów, ufa serwerowi bez żadnych dodatkowych flag. Nieliczne aplikacje posiadają własne magazyny zaufania i wymagają indywidualnego podejścia:

  • Chrome/Chromium w systemie Linux odczytuje bazę danych NSS, a nie magazyn systemowy: sudo apt install libnss3-tools, a następnie certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "git.internal" -i git.internal.crt dla każdego użytkownika.
  • Firefox posiada własny magazyn: Ustawienia → Prywatność i bezpieczeństwo → Certyfikaty → Importuj, lub zmień wartość security.enterprise_roots.enabled na true w about:config, aby korzystał z magazynu systemowego.
  • Python requests dostarcza własny zestaw certyfikatów CA (certifi) i ignoruje magazyn systemowy: przekaż verify="/usr/local/share/ca-certificates/git.internal.crt" lub wyeksportuj REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt.
  • Node.js: wyeksportuj NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/git.internal.crt.

Na klientach z systemem Windows kliknij dwukrotnie .crt i zainstaluj go w Zaufane główne urzędy certyfikacji; na systemie macOS dodaj go do pęku kluczy System w aplikacji Dostęp do pęku kluczy i zaznacz opcję Zawsze ufaj.

Jeden główny certyfikat dla wielu usług: małe prywatne CA

Zaufanie do każdego certyfikatu z osobna przestaje być skalowalne: sześć usług pomnożone przez cztery maszyny klienckie daje dwadzieścia cztery instalacje zaufania, a każda nowa usługa zwiększa tę liczbę. Rozwiązaniem jest prywatne CA; klienci ufają jednemu głównemu certyfikatowi (root), a każda usługa jest podpisywana tym samym kluczem.

Przyjazną opcją jest mkcert, który znajduje się w repozytoriach Ubuntu 24.04 i obsługuje magazyny NSS (Chrome, Firefox), których update-ca-certificates nie obejmuje:

sudo apt install -y mkcert libnss3-tools
mkcert -install
mkcert git.internal.lan "*.internal.lan" 10.8.0.1

mkcert -install tworzy certyfikat główny i rejestruje go w każdym magazynie zaufania na danej maszynie; trzecie polecenie generuje git.internal.lan+2.pem oraz git.internal.lan+2-key.pem, gotowe do wklejenia w powyższe fragmenty konfiguracji nginx lub Apache. Narzędzie to zostało zaprojektowane z myślą o maszynach deweloperskich, klucz główny znajduje się na urządzeniu, na którym uruchomiono -install, więc jest idealne dla laptopa programisty, ale nieodpowiednie dla floty serwerów.

W przypadku serwerów, standardowe OpenSSL pozwala utworzyć całe CA za pomocą pięciu poleceń:

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

Pułapka kryje się w ostatnim poleceniu: openssl x509 -req domyślnie usuwa wszystkie rozszerzenia z CSR, w tym starannie dodane SAN. -copy_extensions copy (opcja dostępna w OpenSSL 3.x, więc działa na 24.04) przenosi je; pominięcie tej opcji sprawi, że podpisany certyfikat nie będzie posiadał SAN, a Chrome ponownie wyświetli błąd NET::ERR_CERT_COMMON_NAME_INVALID. Poprawność należy zweryfikować za pomocą tego samego polecenia openssl x509 -noout -ext subjectAltName co wcześniej.

Rozprowadź lab-ca.crt do klientów, wykonując powyższe kroki dotyczące magazynu zaufania – wystarczy zrobić to raz na maszynę. Chroń lab-ca.key jak największy skarb: ustaw tryb 600 i najlepiej przechowuj go na maszynie innej niż te, dla których wystawia certyfikaty, ponieważ każdy, kto posiada ten klucz, może wygenerować certyfikat dla dowolnej nazwy, której zaufają Twoi klienci.

Wygasanie i rotacja

Okresy ważności certyfikatów wydawanych przez publiczne urzędy certyfikacji (CA) ulegają skróceniu. W marcu 2026 roku forum CA/Browser ograniczyło czas ważności nowo wydawanych certyfikatów zaufanych publicznie do 200 dni (wcześniej 398 dni). W 2027 roku limit ten spadnie do 100 dni, a do marca 2029 roku do 47 dni. Zasady te dotyczą jednak wyłącznie urzędów zaufanych publicznie. Prywatny urząd CA nie podlega tym regulacjom, a przeglądarki nie wymuszają ich w przypadku ręcznie zainstalowanych certyfikatów głównych (root). Istnieje jednak jedno realne ograniczenie: platformy Apple odrzucają każdy certyfikat serwera TLS ważny dłużej niż 825 dni, niezależnie od wystawcy. Jeśli z usługą będą łączyć się urządzenia iPhone lub komputery Mac, należy ograniczyć ważność certyfikatów końcowych (leaf) do maksymalnie dwóch lat. -days 730 spełnia ten wymóg w każdym środowisku; certyfikat główny ważny dziesięć lat z certyfikatami końcowymi ważnymi dwa lata to optymalna konfiguracja wewnętrzna.

Certyfikaty długoterminowe zawodzą w jeden sposób: po cichu, wszystkie naraz, w dniu, o którym nikt nie pamięta. Należy sprawdzić posiadane zasoby:

openssl x509 -in /etc/ssl/certs/git.internal.crt -noout -enddate

Warto wpisać odnowienie do kalendarza lub skonfigurować zadanie cron, które wyśle powiadomienie na 30 dni przed wygaśnięciem. openssl x509 -checkend 2592000 -in cert.crt kończy działanie z kodem błędem, jeśli do wygaśnięcia pozostało mniej sekund niż podana wartość. Jeśli w infrastrukturze działa już Uptime Kuma do monitorowania stanu, jego monitory HTTPS automatycznie sygnalizują zbliżający się termin wygaśnięcia certyfikatu.

Rotacja w przypadku prywatnego CA jest procesem przewidywalnym: należy ponownie wykonać polecenia generowania CSR i podpisywania, podmienić pliki i przeładować serwer WWW. Certyfikat główny pozostaje bez zmian, więc klienci nie odnotują żadnych zakłóceń.

Tryby awarii i odpowiadające im komunikaty

NET::ERR_CERT_AUTHORITY_INVALID, stan oczekiwany przed zainstalowaniem zaufania; nie jest to wada certyfikatu. Jeśli błąd utrzymuje się po zainstalowaniu certyfikatu głównego: w systemie Linux przeglądarka Chrome korzysta z bazy NSS zamiast z systemowego magazynu certyfikatów (patrz krok certutil); skopiowany plik nie kończył się na .crt, a update-ca-certificates zwróciło 0 added; lub serwer prezentuje inny certyfikat niż ten, któremu nadano zaufanie – należy porównać odciski palców (fingerprints) za pomocą openssl s_client -connect git.internal.lan:443 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256.

NET::ERR_CERT_COMMON_NAME_INVALID, certyfikat nie posiada pola SAN lub pole SAN nie obejmuje nazwy wpisanej w pasku adresu. Typowy przypadek: SAN zawiera DNS:git.internal.lan, ale użytkownik przeszedł pod adres https://10.8.0.1. Zmiany w magazynie zaufanych certyfikatów nie rozwiążą tego problemu; należy wygenerować certyfikat ponownie, uwzględniając brakujący wpis.

curl: (60) SSL certificate problem: self-signed certificate, narzędzie curl nie ufa certyfikatowi. Wariant self-signed certificate in certificate chain oznacza to samo w przypadku certyfikatu podpisanego przez prywatny urząd certyfikacji (CA). Rozwiązanie doraźne: curl --cacert lab-ca.crt https://...; rozwiązanie trwałe: dodanie do magazynu zaufanych certyfikatów. Nie należy używać -k.

unable to load certificate ... Expecting: TRUSTED CERTIFICATE (lub Expecting: CERTIFICATE REQUEST, albo no start line), błąd formatu PEM. Do OpenSSL przekazano niewłaściwy typ pliku: klucz lub CSR zamiast certyfikatu, albo plik binarny DER zamiast PEM. Polecenie head -1 filename pozwala sprawdzić zawartość pliku; certyfikat zawsze zaczyna się od -----BEGIN CERTIFICATE-----. Pliki w formacie DER należy przekonwertować za pomocą openssl x509 -inform der -in file.der -out file.crt.

nginx: [emerg] SSL_CTX_use_PrivateKey_file(...) failed (SSL: error ... key values mismatch), certyfikat i klucz nie stanowią pary. Zazwyczaj wynika to z dwukrotnego uruchomienia polecenia generowania i pomylenia plików. Należy zweryfikować zgodność za pomocą openssl x509 -in git.internal.crt -noout -pubkey | sha256sum oraz openssl pkey -in git.internal.key -pubout | sha256sum; zgodne skróty oznaczają poprawną parę. Jeśli skróty są różne, należy wygenerować oba pliki ponownie.

FAQ

Dlaczego Chrome nadal wyświetla komunikat "Not secure" po utworzeniu certyfikatu z podpisem własnym?

Jeśli błąd to NET::ERR_CERT_AUTHORITY_INVALID, certyfikat jest poprawny, jednak Chrome nie ma podstaw, aby mu ufać. Należy zainstalować go (lub główny certyfikat prywatnego CA) w magazynie zaufanych certyfikatów klienta. Należy pamiętać, że w systemie Linux przeglądarka Chrome korzysta z bazy danych NSS za pośrednictwem certutil, a nie z magazynu systemowego. Jeśli błąd to NET::ERR_CERT_COMMON_NAME_INVALID, certyfikatowi brakuje pola Subject Alternative Name zgodnego z adresem URL; należy go wygenerować ponownie z użyciem -addext "subjectAltName=...".

Jak sprawić, by curl ufał certyfikatowi z podpisem własnym bez użycia flagi -k?

Skopiuj certyfikat (w formacie PEM, rozszerzenie .crt) do /usr/local/share/ca-certificates/ i uruchom sudo update-ca-certificates; wynik musi zawierać 1 added. Od tego momentu curl będzie weryfikował certyfikat tak samo jak każdy certyfikat publiczny. W przypadku pojedynczego żądania bez ingerencji w system, curl --cacert /path/to/cert.crt przeprowadzi weryfikację wyłącznie w oparciu o ten plik; -k całkowicie wyłącza weryfikację i nie powinno być stosowane w żadnych skryptach.

Jak długo może być ważny certyfikat z podpisem własnym?

Technicznie tak długo, jak to konieczne. Limity CA/Browser Forum (obecnie 200 dni, 47 dni od 2029 roku) wiążą publicznie zaufane urzędy certyfikacji, a nie prywatne zaufanie. W praktyce należy ograniczyć ważność certyfikatów serwerowych do 825 dni, ponieważ urządzenia Apple odrzucają certyfikaty o dłuższym okresie ważności niezależnie od wystawcy. Dziesięcioletni prywatny certyfikat główny (root) oraz dwuletnie (-days 730) certyfikaty końcowe to rozsądny standard. Należy jedynie zaplanować odnowienie w kalendarzu, ponieważ wygaśnięcie wewnętrznego certyfikatu powoduje cichą awarię usług w terminie, o którym nikt nie pamięta.

Czy powinienem użyć certyfikatu z podpisem własnym, czy Let's Encrypt?

Jeśli usługa posiada publiczną nazwę DNS i jest dostępna z Internetu, zawsze należy wybierać Let's Encrypt – jest darmowy, zautomatyzowany i zaufany przez każdego klienta. Certyfikaty z podpisem własnym (lub prywatne CA) stosuje się tam, gdzie Let's Encrypt nie może wystawić certyfikatu: dla prywatnych adresów IP, wewnętrznych nazw hostów takich jak .lan, sieci izolowanych (air-gapped) oraz usług celowo ukrytych za VPN. Decyzja dotyczy dostępności i nazewnictwa, a nie poziomu bezpieczeństwa; kryptografia jest identyczna.

Dlaczego mój certyfikat jest odrzucany nawet po dodaniu go do /usr/local/share/ca-certificates?

Należy sprawdzić trzy kwestie. Plik musi kończyć się rozszerzeniem .crt; rozszerzenie .pem jest pomijane bez ostrzeżenia, a update-ca-certificates zgłasza 0 added. Zawartość musi być tekstem PEM rozpoczynającym się od -----BEGIN CERTIFICATE-----, a nie plikiem binarnym DER. Ponadto aplikacja musi faktycznie korzystać z magazynu systemowego; Chrome w systemie Linux, Firefox, Python requests, Node.js oraz Java posiadają własne, prywatne magazyny zaufanych certyfikatów i wymagają osobnego dodania certyfikatu do każdego z nich.