Cara buat sijil self-signed Ubuntu 24.04
Pelajari cara jana sijil TLS dengan SAN menggunakan openssl di Ubuntu 24.04 agar diterima Chrome tanpa amaran keselamatan bagi kegunaan nginx atau Apache.
Apa yang anda bina
Sijil TLS tandatangan sendiri yang diterima oleh pelayar dan klien moden — subjectAltName yang betul, keizinan kunci yang munasabah, dan disambungkan ke nginx atau Apache — serta bahagian yang sering diabaikan oleh hampir semua panduan: cara memastikan klien mempercayai sijil tersebut dengan betul, bukannya klik pada amaran dan memasukkan curl -k secara manual ke dalam skrip selamanya. Pada akhirnya, anda akan memiliki CA peribadi dengan lima arahan untuk kegunaan apabila satu perkhidmatan dalaman bertambah menjadi enam.
Pertama, buat keputusan, kerana sijil tandatangan sendiri jarang sekali menjadi alat yang tepat berbanding kekerapan ia digunakan. Jika perkhidmatan boleh dicapai dari internet awam menggunakan nama DNS sebenar, berhenti membaca dan dapatkan sijil Let's Encrypt percuma dengan certbot pada nginx atau setara Apache sebagai ganti. Ia tidak memerlukan kos, pembaharuan adalah automatik, dan setiap pelayar di dunia sudah mempercayainya. Sijil tandatangan sendiri pada tapak awam melatih pengguna anda untuk mengabaikan amaran keselamatan, yang merupakan tabiat yang lebih buruk daripada HTTP biasa.
Tandatangan sendiri adalah alat yang tepat apabila tiada internet awam terlibat: panel admin yang terikat pada alamat terowong WireGuard pada VPS anda, pelayan staging pada rangkaian peribadi, trafik antara perkhidmatan (service-to-service) antara backend, peranti home-lab, atau menggantikan sijil placeholder yang dihasilkan oleh Webmin untuk dirinya sendiri pada port 10000. Let's Encrypt tidak boleh mengeluarkan sijil untuk 10.8.0.1 atau git.internal.lan — tiada CA awam akan memasukkan IP peribadi atau TLD rekaan ke dalam sijil. Untuk nama-nama tersebut, anda adalah CA.
Semua perkara di bawah dijalankan pada kotak Ubuntu 24.04 yang baharu, yang menyertakan OpenSSL 3.0.x (gunakan openssl version untuk pengesahan). Tiada apa yang memerlukan akses internet; semuanya berfungsi secara air-gapped.
Mengapa satu baris arahan lama menghasilkan sijil yang ditolak oleh Chrome
Arahan yang diberikan dalam setiap tutorial sebelum tahun 2017 adalah seperti berikut:
# 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.crtArahan ini meminta siri soalan interaktif, memasukkan hostname anda ke dalam medan Common Name, dan menghasilkan sijil tanpa sambungan subjectAltName. Sijil tersebut tidak boleh digunakan. Chrome berhenti membaca Common Name dalam versi 58 pada April 2017 — RFC 2818 telah lama melenyapkan padanan CN pada tahun 2000 — dan Firefox, Safari, curl, serta Python berkelakuan sama. Sijil mengenal pasti pelayannya melalui sambungan SAN atau tidak langsung; pelayar akan memberitahu anda dengan perkataan ini:
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.Sebarang pengubahsuaian pada trust-store tidak akan membaiki ralat tersebut kerana sijil itu memang tidak menamakan apa-apa. Jika anda melihat NET::ERR_CERT_COMMON_NAME_INVALID sekarang, sijil anda tidak mempunyai SAN (atau mempunyai SAN yang salah) dan anda perlu menjana sijil baharu. Nasib baik, penyelesaiannya hanya memerlukan satu arahan.
Jana sijil yang diterima pelayar: satu arahan
OpenSSL menambah flag -addext dalam versi 1.1.1, bermakna anda tidak lagi memerlukan konfigurasi fail yang rumit seperti dalam panduan lama untuk memasukkan SAN. Pada 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"Fungsi setiap flag:
-x509mengeluarkan sijil self-signed secara terus dan bukannya permintaan tandatangan (signing request).-newkey rsa:4096menjana kunci baharu dalam langkah yang sama. RSA 4096 sesuai untuk semua klien lama; jika semua klien adalah moden,-newkey ec -pkeyopt ec_paramgen_curve:P-256adalah lebih kecil dan lebih pantas.-noencadalah ejaan OpenSSL 3.x bagi-nodesyang lama: tiada kata laluan pada kunci. Kedua-dua ejaan berfungsi. Kunci dengan kata laluan akan menyebabkan nginx terhenti untuk menunggu input setiap kali but (boot), jadi anda memerlukan ini untuk kunci pelayan.-days 730— dua tahun; maklumat lanjut mengenai angka ini ada dalam bahagian tempoh tamat.-subjmenjawab soalan interaktif secara automatik. CN hanyalah kosmetik sekarang, tetapi tetap tetapkan ia kepada nama utama; sesetengah alatan memaparkannya.-addext "subjectAltName=..."adalah flag yang paling penting. Senaraikan setiap nama dan setiap IP yang akan ditaip oleh klien: entriDNS:untuk nama hos (wildcard sepertiDNS:*.internal.lanadalah dibenarkan), entriIP:untuk alamat. Jika sesiapa melayarihttps://10.8.0.1, entriIP:10.8.0.1mesti ada — SAN yang hanya mempunyai DNS akan menyebabkanNET::ERR_CERT_COMMON_NAME_INVALIDberlaku semula.
Sahkan SAN telah dimasukkan sebelum melakukan konfigurasi lain:
openssl x509 -in /etc/ssl/certs/git.internal.crt -noout -ext subjectAltNameOutput yang betul:
X509v3 Subject Alternative Name:
DNS:git.internal.lan, IP Address:10.8.0.1Jika ia memaparkan No extensions in certificate, sijil tersebut tidak mempunyai SAN dan pelayar akan menolaknya — jana semula sijil daripada meneruskan proses.
Lindungi kunci tersebut
Kunci peribadi yang boleh dibaca oleh semua pengguna pada sistem bukan lagi kunci peribadi. Pada Ubuntu, /etc/ssl/private sudah pun 710 root:ssl-cert, yang menghalang akses tidak sengaja, tetapi tetapkan fail tersebut secara eksplisit:
sudo chown root:root /etc/ssl/private/git.internal.key
sudo chmod 600 /etc/ssl/private/git.internal.keynginx dan Apache kedua-duanya membaca sijil sebagai root sebelum menurunkan hak akses, jadi mod root:root 600 berfungsi untuk mereka. Jika kunci tersebut adalah untuk perkhidmatan yang berjalan sebagai pengguna sendiri dan memuatkan kunci itu sendiri — aplikasi Node, Gitea, daemon Python — tukar chown kepada pengguna perkhidmatan tersebut, dengan mod tetap 600. Perkara yang tidak boleh dilakukan: mod 644, salinan dalam repositori git, atau salinan dalam /tmp.
Sambungkan ke 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 mesti memaparkan syntax is ok dan test is successful sebelum proses reload dilakukan. Jika ia memaparkan SSL_CTX_use_PrivateKey_file() failed ... key values mismatch, sijil dan kunci tersebut berasal daripada dua sesi penjanaan yang berbeza — rujuk bahagian failure-modes.
Sambungkan ke Apache
sudo a2enmod ssl proxy proxy_httpssl sahaja tidak mencukupi di sini: vhost di bawah menggunakan ProxyPass, dan tanpa mod_proxy serta mod_proxy_http, ujian konfigurasi akan gagal dengan Invalid command 'ProxyPass', perhaps misspelled or defined by a module not included in the server configuration. Simpan vhost sebagai /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 apache2configtest sepatutnya menjawab Syntax OK. Sekarang uji dari mesin klien:
curl -v https://git.internal.lan/dan anda akan menerima ralat:
curl: (60) SSL certificate problem: self-signed certificateItu bukan pepijat. Itu adalah fungsi TLS: curl tidak mengenali sijil anda dan enggan berkomunikasi dengan pelayan yang tidak dapat disahkan. Bahagian seterusnya adalah penyelesaian sebenar — dan ia bukan apa yang dilakukan oleh separuh daripada pengguna internet pada saat ini.
Jadikan ia dipercayai oleh klien — dan anti-corak yang perlu ditolak
Penyelesaian yang salah, dinamakan mengikut jenisnya. curl -k (atau --insecure) dalam skrip, verify=False dalam Python requests, NODE_TLS_REJECT_UNAUTHORIZED=0 dalam Node — semua ini tidak menjadikan sijil anda dipercayai. Ia mematikan pengesahan sijil, yang bermaksud klien akan berkomunikasi dengan mana-mana pelayan yang membentangkan mana-mana sijil, termasuk sijil daripada penyerang. Anda tetap menanggung beban overhead TLS tetapi kehilangan fungsi pengesahan yang menjadi tujuannya. Lebih buruk lagi, flag ini akan merebak: ditampal ke dalam satu cron job, kemudian skrip deployment, kemudian kod produksi, sehingga tiada sesiapa ingat sambungan mana yang sepatutnya bersifat sementara. Jika verify=False kekal selepas sesi debugging tamat, reka bentuk tersebut adalah salah.
Penyelesaian yang betul adalah dengan memberitahu setiap OS klien bahawa sijil ini adalah root yang dipercayai. Pada klien Ubuntu dan Debian:
sudo cp git.internal.crt /usr/local/share/ca-certificates/git.internal.crt
sudo update-ca-certificatesBaris penting dalam output (blok Running hooks in /etc/ca-certificates/update.d... menyusul selepasnya):
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.Dua perkara penting tersembunyi dalam baris tersebut. Fail mesti berakhir dengan .crt — sambungan .pem akan diabaikan secara senyap dan anda akan mendapat 0 added tanpa mesej ralat. Kandungannya mestilah format PEM — fail bermula dengan -----BEGIN CERTIFICATE-----; tukar binari DER terlebih dahulu dengan openssl x509 -inform der -in file.der -out file.crt. Menambah sijil self-signed itu sendiri sebagai root berfungsi kerana sijil self-signed adalah rootnya sendiri.
Selepas itu, curl, wget, git, apt, dan apa sahaja yang menggunakan OpenSSL terhadap bundle sistem akan mempercayai pelayan tanpa sebarang flag. Beberapa klien mempunyai stor kepercayaan sendiri dan memerlukan pengendalian individu:
- Chrome/Chromium pada Linux membaca pangkalan data NSS, bukan stor sistem:
sudo apt install libnss3-tools, kemudiancertutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "git.internal" -i git.internal.crtbagi setiap pengguna. - Firefox mempunyai stor sendiri: Settings → Privacy & Security → Certificates → Import, atau tukar
security.enterprise_roots.enabledkepadatruedalamabout:configsupaya ia membaca stor sistem. - Python requests membawa bundle CA sendiri (certifi) dan mengabaikan stor sistem: gunakan
verify="/usr/local/share/ca-certificates/git.internal.crt"atau eksportREQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt. - Node.js: eksport
NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/git.internal.crt.
Pada klien Windows, klik dua kali pada .crt dan pasang ke Trusted Root Certification Authorities; pada macOS, tambah ke System keychain dalam Keychain Access dan tandakan sebagai Always Trust.
Satu root untuk banyak servis: CA peribadi yang kecil
Kepercayaan berasaskan setiap sijil akan menyukarkan skalabiliti: enam servis dengan empat mesin klien bermaksud 24 pemasangan kepercayaan, dan setiap servis baharu akan menambah beban tersebut. Penyelesaiannya ialah CA peribadi — klien hanya mempercayai satu root, dan anda menandatangani setiap sijil servis menggunakan root tersebut.
Pilihan yang mudah ialah mkcert, yang terdapat dalam repositori Ubuntu 24.04 dan mengendalikan stor NSS (Chrome, Firefox) yang tidak dikendalikan oleh update-ca-certificates:
sudo apt install -y mkcert libnss3-tools
mkcert -install
mkcert git.internal.lan "*.internal.lan" 10.8.0.1mkcert -install mencipta root dan mendaftarkannya dalam setiap stor kepercayaan pada mesin tersebut; arahan ketiga akan menghasilkan git.internal.lan+2.pem dan git.internal.lan+2-key.pem, yang sedia untuk dimasukkan ke dalam petikan nginx atau Apache di atas. Andaian reka bentuknya adalah untuk mesin pembangunan — kunci root disimpan pada mana-mana kotak yang menjalankan -install — jadi ia sangat sesuai untuk komputer riba pembangunan tetapi tidak sesuai untuk rangkaian pelayan.
Untuk pelayan, OpenSSL biasa boleh melakukan keseluruhan proses CA dalam lima arahan:
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.crtKesilapan berlaku pada arahan terakhir: openssl x509 -req membuang semua sambungan (extensions) daripada CSR secara lalai, termasuk SAN yang telah anda tambah. -copy_extensions copy (pilihan OpenSSL 3.x, jadi ia berfungsi pada 24.04) akan mengekalkan sambungan tersebut; jika ia diabaikan, sijil yang ditandatangani tidak mempunyai SAN, dan Chrome akan memaparkan NET::ERR_CERT_COMMON_NAME_INVALID semula. Sahkan dengan semakan openssl x509 -noout -ext subjectAltName yang sama seperti sebelum ini.
Edarkan lab-ca.crt kepada klien melalui langkah stor-kepercayaan di atas — hanya sekali bagi setiap mesin. Lindungi lab-ca.key kerana ia kini sangat berharga: gunakan mod 600, dan sebaiknya simpan pada mesin yang bukan merupakan salah satu pelayan yang ditandatanganinya, kerana sesiapa yang memilikinya boleh mencipta sijil untuk sebarang nama yang akan dipercayai oleh klien anda.
Tarikh luput dan pusingan
Tempoh hayat sijil CA awam semakin singkat — CA/Browser Forum mengehadkan sijil yang dipercayai secara awam yang baru dikeluarkan kepada 200 hari pada Mac 2026 (berbanding 398 sebelum ini), kemudian kepada 100 hari pada 2027 dan 47 hari menjelang Mac 2029 — tetapi peraturan tersebut hanya terpakai untuk CA yang dipercayai secara awam. CA peribadi anda tidak tertakluk kepada peraturan tersebut, dan pelayar tidak menguatkuasakannya terhadap akar yang dipasang secara manual. Satu sekatan dunia nyata yang terpakai ialah: platform Apple menolak sebarang sijil pelayan TLS yang sah lebih daripada 825 hari tidak kira siapa yang mengeluarkannya, jadi jika iPhone atau Mac perlu disambungkan, pastikan sijil leaf adalah dua tahun atau kurang. -days 730 melepasi had tersebut di mana-mana sahaja; akar sepuluh tahun dengan leaf dua tahun adalah struktur dalaman yang stabil.
Sijil jangka panjang gagal dengan satu cara sahaja: secara senyap, secara serentak, pada tarikh yang tidak diingati oleh sesiapa. Semak apa yang anda miliki:
openssl x509 -in /etc/ssl/certs/git.internal.crt -noout -enddateMasukkan pembaharuan ke dalam kalendar sebenar, atau gunakan cron untuk memberi amaran 30 hari sebelum — openssl x509 -checkend 2592000 -in cert.crt akan keluar dengan nilai bukan sifar apabila tarikh luput berada dalam tempoh tersebut. Jika anda sudah menggunakan Uptime Kuma untuk pemantauan status, pemantau HTTPS miliknya akan menandakan tarikh luput sijil yang semakin hampir secara percuma.
Pusingan dengan CA peribadi adalah sangat mudah: jalankan semula arahan CSR-and-sign, tukar fail, muat semula pelayan web. Akar tidak berubah, jadi tiada klien akan menyedari sebarang perubahan.
Mod kegagalan, bersama rentetan yang akan anda lihat
NET::ERR_CERT_AUTHORITY_INVALID — keadaan biasa sebelum anda memasang kepercayaan, bukan kecacatan pada sijil tersebut. Jika ia berterusan selepas anda memasang root: pada Linux, Chrome membaca NSS dan bukannya stor sistem (rujuk langkah certutil); atau fail yang disalin tidak berakhir dengan .crt dan update-ca-certificates menyatakan 0 added; atau pelayan membentangkan sijil yang berbeza daripada sijil yang anda percayai — bandingkan cap jari dengan openssl s_client -connect git.internal.lan:443 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256.
NET::ERR_CERT_COMMON_NAME_INVALID — sijil tidak mempunyai SAN, atau SAN tidak merangkumi nama dalam bar alamat. Kes klasik: SAN menyenaraikan DNS:git.internal.lan tetapi pengguna melayari https://10.8.0.1. Perubahan stor kepercayaan tidak dapat membaiki masalah ini; keluarkan semula sijil dengan entri yang hilang.
curl: (60) SSL certificate problem: self-signed certificate — curl tidak mempercayai sijil tersebut. Varian self-signed certificate in certificate chain bermaksud perkara yang sama untuk sijil yang ditandatangani oleh CA peribadi anda. Penyelesaian sekali guna: curl --cacert lab-ca.crt https://...; penyelesaian kekal: stor kepercayaan. Bukan -k.
unable to load certificate ... Expecting: TRUSTED CERTIFICATE (atau Expecting: CERTIFICATE REQUEST, atau no start line) — kekeliruan PEM. Anda memberikan jenis fail yang salah kepada OpenSSL: kunci atau CSR di mana ia menjangkakan sijil, atau binari DER di mana ia menjangkakan PEM. head -1 filename memberitahu anda apa yang anda miliki — sijil bermula dengan -----BEGIN CERTIFICATE-----. Untuk DER, tukar dengan openssl x509 -inform der -in file.der -out file.crt.
nginx: [emerg] SSL_CTX_use_PrivateKey_file(...) failed (SSL: error ... key values mismatch) — sijil dan kunci tidak sepadan, biasanya kerana arahan penjanaan dijalankan dua kali dan fail telah bercampur. Sahkan dengan openssl x509 -in git.internal.crt -noout -pubkey | sha256sum berbanding openssl pkey -in git.internal.key -pubout | sha256sum; hash yang sepadan bermaksud pasangan yang sepadan. Jika ia berbeza, jana semula kedua-duanya bersama-sama.
FAQ
Mengapa Chrome masih memaparkan "Not secure" selepas saya mencipta sijil self-signed?
Jika ralatnya ialah NET::ERR_CERT_AUTHORITY_INVALID, sijil tersebut adalah sah — Chrome belum mempunyai sebab untuk mempercayainya. Pasang sijil tersebut (atau root CA peribadi anda) ke dalam stor kepercayaan klien. Ingat bahawa pada Linux, Chrome menggunakan pangkalan data NSS melalui certutil, bukan stor sistem. Jika ralatnya ialah NET::ERR_CERT_COMMON_NAME_INVALID, sijil tersebut kekurangan Subject Alternative Name yang sepadan dengan URL dan mesti dikeluarkan semula dengan -addext "subjectAltName=...".
Bagaimanakah cara untuk membuat curl mempercayai sijil self-signed tanpa -k?
Salin sijil (format PEM, sambungan .crt) ke dalam /usr/local/share/ca-certificates/ dan jalankan sudo update-ca-certificates — output mesti menyatakan 1 added. Selepas itu, curl akan mengesahkan sijil tersebut seperti sijil awam yang lain. Untuk permintaan sekali guna tanpa mengubah sistem, curl --cacert /path/to/cert.crt mengesahkan terhadap fail tersebut sahaja; -k melumpuhkan pengesahan sepenuhnya dan tidak sepatutnya digunakan dalam skrip sesiapa.
Berapa lamakah tempoh sah sijil self-signed?
Secara teknikal, selama mana yang anda mahu — had CA/Browser Forum (200 hari sekarang, 47 menjelang 2029) hanya terpakai untuk CA yang dipercayai secara awam, bukan kepercayaan peribadi. Secara praktikal, hadkan sijil pelayan kepada 825 hari, kerana peranti Apple akan menolak apa-apa yang lebih lama tanpa mengira penerbit. Root peribadi sepuluh tahun dengan sijil leaf dua tahun (-days 730) adalah tetapan lalai yang munasabah; pastikan anda menandakan tarikh pembaharuan, kerana sijil dalaman yang tamat tempoh akan melumpuhkan semua perkhidmatan secara senyap pada tarikh yang tidak diingati sesiapa.
Patutkah saya menggunakan sijil self-signed atau Let's Encrypt?
Jika perkhidmatan mempunyai nama DNS awam dan boleh dicapai dari internet, sentiasa gunakan Let's Encrypt — ia percuma, automatik, dan sudah dipercayai oleh setiap klien. Self-signed (atau CA peribadi) adalah untuk perkara yang tidak boleh dikeluarkan oleh Let's Encrypt: IP peribadi, nama hos dalaman sahaja seperti .lan, rangkaian air-gapped, dan perkhidmatan yang sengaja disembunyikan di sebalik VPN. Keputusan ini adalah mengenai kebolehcapaian dan penamaan, bukan kekuatan keselamatan — kriptografinya adalah serupa.
Mengapa sijil saya ditolak walaupun selepas menambahkannya ke /usr/local/share/ca-certificates?
Periksa tiga perkara. Fail mesti berakhir dengan .crt — sambungan .pem akan diabaikan secara senyap dan update-ca-certificates akan melaporkan 0 added. Kandungan mesti dalam teks PEM yang bermula dengan -----BEGIN CERTIFICATE-----, bukan binari DER. Dan aplikasi tersebut mesti menggunakan stor sistem — Chrome pada Linux, Firefox, Python requests, Node.js, dan Java masing-masing mempunyai stor kepercayaan peribadi dan memerlukan sijil ditambah secara berasingan.