diff options
| -rw-r--r-- | notes/create_self-signed_certificate_from_a_self-signed_CA.md | 434 | ||||
| -rw-r--r-- | notes/créer-un-certificat-auto-signé-depuis-un-CA-auto-signé.md | 435 | ||||
| -rw-r--r-- | notes/res/certificat-signed.svg | 1 | ||||
| -rw-r--r-- | notes/res/chain_of_trust.avif | bin | 0 -> 13552 bytes | |||
| -rw-r--r-- | notes/res/chain_of_trust.png | bin | 0 -> 79824 bytes | |||
| -rw-r--r-- | notes/res/chaîne_de_certification.avif | bin | 0 -> 14015 bytes | |||
| -rw-r--r-- | notes/res/chaîne_de_certification.png | bin | 0 -> 80455 bytes |
7 files changed, 870 insertions, 0 deletions
diff --git a/notes/create_self-signed_certificate_from_a_self-signed_CA.md b/notes/create_self-signed_certificate_from_a_self-signed_CA.md new file mode 100644 index 0000000..b3db87e --- /dev/null +++ b/notes/create_self-signed_certificate_from_a_self-signed_CA.md @@ -0,0 +1,434 @@ +--- +pubDate = 2025-01-25T16:09:04 +tags = ['https', 'web', 'nginx', 'linux', 'cryptography'] +lang = "en" +type = "note" + +[author] +name = "ache" +email = "ache@ache.one" + +[[alt_lang]] +lang = "fr" +url = "/notes/créer-un-certificat-auto-signé-depuis-un-CA-auto-signé" +--- + +# Obtaining a self-signed SSL certificate from our own Certificate Authority + + + +In this blog post, we will create a self-signed TLS certificate, install it on an nginx server and on client systems. + +## Objectives of the TLS Protocol + +:::attention +Generally, you do not want a self-signed certificate. +If you want to obtain a valid certificate on the internet, then you need it to be verified by a trusted Certificate Authority (CA). +In this case, [Let's Encrypt](https://letsencrypt.org/fr/) can certainly provide the certificate you need. +::: + +TLS authentication is used to **verify that** the server you want to connect to (by its **domain name**) **is indeed the server** that has authority over that domain name. +This may seem abstract, but there are several reasons why a domain name may not redirect to the correct server. + +- A lying DNS +- A compromised router redirects you to the wrong server +- A configuration error (on either the client or the server side) +- ... + +Subsequently, TLS will handle encryption that ensures confidentiality and integrity. + +TLS is a protocol used as an overlay on many other protocols (FTP, IMAP, SMTP, ..., and even DNS!) to add security to them. + +## Certificate Chain (or Chain of Trust) + +We do not have a list of certificates issued on our computers. It would be too complicated to manage, heavy, difficult to update and ultimately unreliable.[^ct-logs] + +[^ct-logs]: + The role of _Certificate Transparency (CT) logs_ is to ensure that any suspicious behavior (at the level of certificate issuance) can be detected. + For more information, see <https://certificate.transparency.dev/>. + +<img src="res/chain-of-trust.png" class="big" alt="Schema of the Certificate Chain"> + +On the contrary, we have only a relatively small list of certificates of trust on our computers (149 in my case). +These are the root certificates. +These certificates will delegate their role of identity verifier to others. +These intermediate authorities then have the role of ensuring that the request for certification comes from the correct server and issuing a signed certificate (and of short duration, i.e., several days at most, up to a few months). + +In our case, we will pass the intermediate certificate step as we will never delegate our authority. + +:::info +The certificates installed on your computer are selected by your operating system (for example, [that of Windows](https://ccadb.my.salesforce-sites.com/microsoft/IncludedCACertificateReportForMSFT)). +Often, this delegates your trust to a certificate selection organization such as [Mozilla](https://wiki.mozilla.org/CA/Included_Certificates). +::: + +### Under Arch Linux + +Arch Linux uses [p11-kit](https://github.com/p11-glue/p11-kit) to manage certificates. + +Just like on Ubuntu and Debian systems, the list of certificates is available in the PEM format in the `/etc/ssl/certs` directory. + +OpenSSL is the reference tool for TLS. You can use it to display a certificate as follows: + +```shell +$ openssl x509 -noout -text -in $certificat +``` + +In the case of a website, you can query the certificate of a website with the following command: +` + +```shell +$ openssl s_client -connect ache.one:443 -verify_return_error </dev/null >/dev/null +Connecting to 2001:41d0:601:1100::11dc +depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1 +verify return:1 +depth=1 C=US, O=Let's Encrypt, CN=R10 +verify return:1 +depth=0 CN=ache.one +verify return:1 +DONE +``` + +One can add : + +- `-showcerts` to print the server certificat +- `-servername` to set a SNI field +- `-starttls` with `smtp`, `ldap`, `pop3`, `xmpp`... to verify that a TLS setup is compatible with StartTLS. +- +- We can also extract the certificate into a file as follows: + + ```sh + $ openssl s_client -connect ache.one:443 -servername ache.one < /dev/null | \ + openssl x509 -outform PEM > ache_one.cert + ``` + +- To verify dates of a certificate : + + ```shell + $ openssl x509 -in ache_one.cert -noout -dates # Pour un certificat que l'on possède localement + notBefore=Wen 16 15:27:33 2025 GMT + notAfter=Wen 30 15:27:33 2025 GMT + $ openssl s_client -servername ache.one -connect ache.one:443 2>/dev/null </dev/null | \ + openssl x509 -noout -dates + ``` + +## Créer le certificat racine + +Firstly, let's create a private key. +We will use RSA with a 3072-bit key. + +```shell +$ openssl genrsa -out root_ca.key 3072 +``` + +To use elliptic curves, we will rather use `openssl ecparam -genkey` and select a curve from the available options `openssl ecparam -genkey -list_curves` (make sure to get informed about the support of the chosen curve). +Then, we create the certificate and sign it. + +```shell +$ openssl req -x509 -key local_ca.key -nodes -utf8 -days 3650 -out local_ca.cert -config local_ca.conf +``` + +With this configuration file : + +```toml +[ req ] +default_bits = 3072 +distinguished_name = x509_distinguished_name +x509_extensions = x509_ext +prompt = no + +[ x509_distinguished_name ] +countryName = "FX" +stateOrProvinceName = Pays des bisounours +organizationName = ache +organizationalUnitName = ache +commonName = ache.local +emailAddress = ache@ache.one + +[ x509_ext ] +basicConstraints=critical,CA:TRUE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +extendedKeyUsage=critical,serverAuth +subjectAltName=email:ache@ache.one,DNS:local,DNS:ache.local +# keyUsage = critical, digitalSignature, cRLSign, keyCertSign +keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign +``` + +Here, it's important to use `x509_extensions` instead of `req_extensions`, and the line `basicConstraints` specifies `CA:TRUE`. + +:::note +It is possible to perform these two steps in one single command: + +```shell +$ openssl req -x509 \ + -sha256 -days 3650 \ + -nodes \ + -newkey rsa:3072 \ + -subj "/CN=ache Root CA/C=FR/L=La grande ville" \ + -keyout root_ca.key -out root_ca.cert +``` + +::: + +### Add the root certificate to the list of trusted certificates + +On the client side, we can already install the self-signed CA for the clients. +For Arch Linux clients, this is done with the command `trust`. + +```shell +# trust anchor --store root_ca.cert +``` + +One can verify if it is indeed on the list by checking its name. + +```shell +$ trust list | rg -C 2 "ache" +pkcs11:id=%F4%30%84%A6%0F%AD%AF%BB%6C%3A%2D%C6%1A%66%84%48%73%4E%CB%E7;type=cert + type: certificate + label: ache Root CA + trust: anchor + category: authority +``` + +:::warn +We take this opportunity to make sure that the certificate is an authority certificate (category _authority_) and indeed that it is a certificate (type _certificate_). +::: + +It will then be installed in the directory `/etc/ca-certificates/trust-source/`. +To remove the certificate from the trusted certificates, we use the file generated in this directory. + +```shell +# trust anchor --remove /etc/ca-certificates/trust-source/ache.dns.p11-kit +``` + +## Creation of the DNS Certificate + +Same as before, we start by creating a private key. + +### The Private Key + +```shell +$ openssl genrsa -out website.key 3072 +``` + +Then, we create a **signature request**. +This allows us to configure the signature by the certification authority. + +### The Signature Request + +Normally, in the usual process, the Intermediate Certification Authority receives a signature request and initiates then the process of verifying the identity of the server that is requesting the signature. + +We can do this easily with OpenSSL: + +```shell +$ openssl req -new -key website.key -out website.csr -utf8 -nameopt multiline,utf +``` + +:::info +Here, I have added the parameters `-utf8 -nameopt multiline,utf8` so as to accept non-ASCII characters in the certification information (Company name, region ...). +::: + +OpenSSL will then ask for the certification information interactively. +This can be avoided either by filling in the data directly on the command line. + +```shell +$ openssl req -new -out website.csr -sha256 -days 3650 -nodes -subj "/C=FR/ST=Centre/L=La grande Ville/O=NomDeLEntreprise/OU=/CN=CommonNameOrHostname" +``` + +Or by using a configuration file. +This is what I chose to do in order to facilitate the re-creation of the certificate. + +```sh +$ openssl req -new -key website.key -out website.csr -utf8 -nameopt multiline,utf8 -config website.conf +``` + +The configuration file looks like this: + +```toml +[ req ] +prompt = no +default_bits = 3072 +default_md = sha256 +distinguished_name = server_distinguished_name +req_extensions = v3_ext + +[ server_distinguished_name ] +commonName = ache +countryName = FR +stateOrProvinceName = XXX +emailAddress = ache@ache.one +organizationName = ache local CA organization + +[ v3_ext ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[ alt_names ] +DNS.0 = website.ache.one +DNS.1 = website_alt.ache.one +``` + +## Creating the final certificate + +There is only one step left: creating the final certificate. +The following are required for this purpose: + +- Root certificate private key +- Root certificate +- The final certificate private key +- The Certificate signing request (CSR) + +```shell +$ openssl x509 -req -CA ../root_ca/root_ca.cert -CAkey ../root_ca/root_ca.key -in website.csr -out website.cert -days 10 -CAcreateserial -extensions v3_ext -extfile website.conf -sha256 +``` + +## Configuring the certificate in nginx + +There is nothing magical about it. +We add `ssl` to the `listen` and configure TLS. +To do this, we need to indicate where the encryption key is located as well as the certificate. + +```text + listen [::]:443 ssl; + listen 443 ssl; + http2 on; # Not related to TLS + + ssl_certificate /srv/www/website.cert; # managed by myself ! + ssl_certificate_key /srv/website/cert/website.key; + ssl_stapling off; # Since it's a self-signed certificate. No need for OCSP stapling. + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +``` + +It can be noticed that it is possible to pass the certificate chain also by concatenating the final certificate (first) with the root certificate. +I do not know if this has a real advantage, but it is a common practice. + +## Certificate Update + +It is recommended to set up an automatic method for updating the certificate. +I recommend a systemd timer that will check the validity of the certificate and renew it at regular intervals. + +`check-certificates.timer` : + +```toml +# This timer unit is for refreshing local certificates everyday + +[Unit] +Description=Refresh self-signed certificates regularly +Requires=check-certificates.service + + +[Timer] +Unit=check-certificates.service +OnUnitActiveSec=86400 + +[Install] +WantedBy=timers.target +``` + +And `check-certificates.service` + +```toml +# This service unit is for refreshing local certificates if needed + +[Unit] +Description=Refresh local certificates if needed +Wants=check-certificates.timer + +[Service] +Type=oneshot +ExecStart=/usr/bin/check-certificates.sh + +[Install] +WantedBy=multi-user.target +``` + +I thus created a Bash script to automate the verification of certificates and the updating of Nginx configuration files. + +:::details + +Content of the file `check-certificates.sh` + +::::summary + +```bash +#!/bin/env bash + +# This script checks the validity of the certificates in the directory +# ${CERTIFICATES_DIR} and renews them if necessary. +# +# It uses the local CA certificate and key to check the validity of the +# certificates in the directory ${CERTIFICATES_DIR}. +# +# The script is intended to be run from a systemd timer every day. + + +# TODO: Configure the following variables from cli arguments +# TODO: Rewrite this shit in Python +CERTIFICATES_DIR="/home/works/certs/" +LOCAL_CA_CERT="${CERTIFICATES_DIR}/local_ca.cert" +LOCAL_CA_KEY=$(echo "${LOCAL_CA_CERT}" | sed 's/.cert$/.key/') + +HAS_RENEW_CERT="" +if [ ! -r "$LOCAL_CA_CERT" ]; then + echo "☠️ Please re-check that local CA cert \"$LOCAL_CA_CERT\" exists" + exit +fi +if [ ! -r "$LOCAL_CA_KEY" ]; then + echo "☠️ Please re-check that local CA key \"$LOCAL_CA_KEY\" exists" + exit +fi + +date +echo -e "Checking certificate validity (NotAfter field)\n" + +pushd ${CERTIFICATES_DIR} >/dev/null 2>/dev/null +for cert in $(find -name "*.cert"); do + if openssl x509 -checkend 345600 -noout -in ${cert} > /dev/null; then + echo -e "✔️ $(basename ${cert}) will expire in more than 4 days" + else + echo -e "❌ $(basename ${cert}) will expire soon !" + if [ $(basename "$cert") = $(basename "$LOCAL_CA_CERT") ]; then + echo -e "\t⛔ I don't renew local CA certificate" + continue + fi + + NEW_CSR=$(echo ${cert} | sed 's/.cert/.csr/') + CERT_KEY=$(echo ${cert} | sed 's/.cert/.key/') + CERT_CONFIG=$(echo ${cert} | sed 's/.cert/.conf/') + + echo "Renewing ${cert}" + echo "Creating new CSR ($NEW_CSR)" + echo ${cert} + echo ${CERT_KEY} + echo ${CERT_CONFIG} + + # Sign the new signing request + if openssl req -new -key "${CERT_KEY}" -out "${NEW_CSR}" -config "${CERT_CONFIG}"; then + echo "👍 CSR created" + else + echo "❌ Failled to create signing request!" + continue + fi + + echo "Renewing certificate" + openssl x509 -req -CA "${LOCAL_CA_CERT}" -CAkey "${LOCAL_CA_KEY}" -in "${NEW_CSR}" -out "${cert}" -days 10 -CAcreateserial -extensions v3_ext -extfile "${CERT_CONFIG}" -sha256 + if [ $? -eq 0 ]; then + HAS_RENEW_CERT="yes" + echo "✔️ Certificate renew" + else + echo "❌ Failled to renew certificate!" + fi + fi +done + +echo -e "\nNo more certificate to check" +if [ -n "$HAS_RENEW_CERT" ]; then + echo "🔃 Reload nginx configuration (and update certificates)" + nginx -s reload +fi + +popd >/dev/null 2>/dev/null +``` + +:::: diff --git a/notes/créer-un-certificat-auto-signé-depuis-un-CA-auto-signé.md b/notes/créer-un-certificat-auto-signé-depuis-un-CA-auto-signé.md new file mode 100644 index 0000000..27f82c2 --- /dev/null +++ b/notes/créer-un-certificat-auto-signé-depuis-un-CA-auto-signé.md @@ -0,0 +1,435 @@ +--- +pubDate = 2025-01-25T16:09:04 +tags = ['https', 'web', 'nginx', 'linux', 'cryptographie'] +lang = "fr" +type = "note" + +[author] +name = "ache" +email = "ache@ache.one" + +[[alt_lang]] +lang = "en" +url = "/notes/create_self-signed_certificate_from_a_self-signed_CA" +--- + +# Obtenir un certificat auto-signé à partir de sa propre autorité de certification + + + +Dans ce poste de blog, nous allons créer un certificat TLS autosigné, l'installer sur un serveur nginx et sur les systèmes clients. + +## Objectifs de TLS + +:::attention +Généralement, vous ne souhaitez pas avoir un certificat autosigné. +Si vous souhaitez obtenir un certificat valide sur internet, alors vous avez besoin qu'il soit validé par une autorité de certification. +Dans ce cas, [Let's Encrypt](https://letsencrypt.org/fr/) peut très certainement vous délivrer le certificat dont vous avez besoin. +::: + +L'authentification TLS sert à **attester que le** serveur que vous souhaitez contacter (par son **nom de domaine**) **correspond bien au serveur** ayant autorité sur ce nom de domaine. +Cela peut paraître abstrait comme ça, mais il peut y avoir plusieurs raisons qui font que le nom de domaine ne redirige pas vers le bon serveur. + +- Un DNS menteur +- Un routeur compromis vous redirige vers le mauvais serveur +- Une erreur de configuration (coté client ou serveur) +- ... + +Ensuite, TLS se chargera du chiffrement qui assurera la confidentialité et l'intégrité. + +TLS est un protocole utilisé comme surcouche à pleins d'autres protocoles (FTP, IMAP, SMTP, ..., et même DNS !) afin de leur rajouter une sécurité. + +## Chaîne de certification (ou chaîne de confiance) + +Nous n'avons pas la liste des certificats émis sur nos ordinateurs. +Ça serait trop compliqué à gérer, trop lourd, difficile à mettre à jour et au final peu fiable.[^ct-logs] + +[^ct-logs]: + C'est le rôle cependant des _Certification Transparency (CT) logs_ qui assurent que tout comportement suspect (au niveau de l'émission de certificat) puisse être détecté. + Plus d'info à <https://certificate.transparency.dev/>. + +<img src="res/chaîne_de_certification.png" class="big" alt="Schéma de la chaîne de certification"> + +Au contraire, on a une seulement une liste relative faible de certificats de confiance sur nos ordinateurs (149 chez moi). +Ce sont les certificats d'autorité, ou racines. +Ces certificats vont déléguer leur rôle de vérificateur d'identité à d'autres. +Ces autorités intermédiaires ont alors pour rôle de s'assurer que la demande de certification émane bien du bon serveur et d'émettre un certificat signé (et de courte durée, c'est-à-dire quelques jours, au plus quelques mois). + +Dans notre cas, nous allons nous passez l'étape du certificat intermédiaire puisque nous n'allons jamais déléguer notre autorité. + +:::info +Les certificats installés sur votre ordinateur sont sélectionnés par votre système d'exploitation (par exemple [celle de Windows](https://ccadb.my.salesforce-sites.com/microsoft/IncludedCACertificateReportForMSFT)). +Souvent, celui-ci délègue votre confiance à un organisme de sélection de certificats comme [Mozilla](https://wiki.mozilla.org/CA/Included_Certificates). +::: + +### Sous Arch Linux + +Arch Linux utilise [p11-kit](https://github.com/p11-glue/p11-kit) pour gérer les certificats. + +Tout comme sur les systèmes Ubuntu et Debian, la liste des certificats est disponible au format PEM dans le dossier `/etc/ssl/certs`. + +OpenSSL est l'outil de référence en matière de TLS. +Vous pouvez l'utiliser pour afficher un certificat comme ceci. + +```shell +$ openssl x509 -noout -text -in $certificat +``` + +Dans le cas d'un site web, on peut interroger le certificat d'un site web avec la commande suivante. + +```shell +$ openssl s_client -connect ache.one:443 -verify_return_error </dev/null >/dev/null +Connecting to 2001:41d0:601:1100::11dc +depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1 +verify return:1 +depth=1 C=US, O=Let's Encrypt, CN=R10 +verify return:1 +depth=0 CN=ache.one +verify return:1 +DONE +``` + +On peut ajouter : + +- `-showcerts` pour afficher le certificat serveur +- `-servername` pour mettre en place le SNI +- `-starttls` avec `smtp`, `ldap`, `pop3`, `xmpp`... pour vérifier l'installation TLS sur un serveur avec un protocole compatible StartTLS. +- On peut également extraire le certificat dans un fichier : + + ```sh + $ openssl s_client -connect ache.one:443 -servername ache.one < /dev/null | \ + openssl x509 -outform PEM > ache_one.cert + ``` + +- Vérifier les dates de validité du certificat : + + ```shell + $ openssl x509 -in ache_one.cert -noout -dates # Pour un certificat que l'on possède localement + notBefore=Wen 16 15:27:33 2025 GMT + notAfter=Wen 30 15:27:33 2025 GMT + $ openssl s_client -servername ache.one -connect ache.one:443 2>/dev/null </dev/null | \ + openssl x509 -noout -dates + ``` + +## Créer le certificat racine + +Premièrement, il faut créer une clé privée. +Nous allons utilisez RSA avec une clé de 3072 bits. + +```shell +$ openssl genrsa -out root_ca.key 3072 +``` + +Pour utiliser les courbes elliptiques, on utilisera plutôt `openssl ecparam -genkey` en sélectionnant une courbe parmi celles disponibles `openssl ecparam -genkey -list_curves` (attention à bien se renseigner sur le support de la courbe choisie). + +Ensuite, on crée le certificat et on le signe : + +```shell +$ openssl req -x509 -key local_ca.key -nodes -utf8 -days 3650 -out local_ca.cert -config local_ca.conf +``` + +Avec le fichier de configuration : + +```toml +[ req ] +default_bits = 3072 +distinguished_name = x509_distinguished_name +x509_extensions = x509_ext +prompt = no + +[ x509_distinguished_name ] +countryName = "FX" +stateOrProvinceName = Pays des bisounours +organizationName = ache +organizationalUnitName = ache +commonName = ache.local +emailAddress = ache@ache.one + +[ x509_ext ] +basicConstraints=critical,CA:TRUE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +extendedKeyUsage=critical,serverAuth +subjectAltName=email:ache@ache.one,DNS:local,DNS:ache.local +# keyUsage = critical, digitalSignature, cRLSign, keyCertSign +keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign +``` + +Ici, ce qui est important dans l'utilisation de `x509_extensions` plutôt que `req_extensions` et la ligne `basicConstraints` spécifiant `CA:TRUE`. + +:::note +Il est possible de faire ces deux étapes en une seule commande : + +```shell +$ openssl req -x509 \ + -sha256 -days 3650 \ + -nodes \ + -newkey rsa:3072 \ + -subj "/CN=ache Root CA/C=FR/L=La grande ville" \ + -keyout root_ca.key -out root_ca.cert +``` + +::: + +### Ajouter le certificat racine à la liste des certificats de confiance + +Coté client, on peut d’ores et déjà installer le CA autosigné chez les clients. +Pour des clients Arch Linux ça se fait alors avec la commande `trust`. + +```shell +# trust anchor --store root_ca.cert +``` + +On peut vérifier qu'il est bien dans la liste à partir de son nom. + +```shell +$ trust list | rg -C 2 "ache" +pkcs11:id=%F4%30%84%A6%0F%AD%AF%BB%6C%3A%2D%C6%1A%66%84%48%73%4E%CB%E7;type=cert + type: certificate + label: ache Root CA + trust: anchor + category: authority +``` + +:::warn +On en profite pour bien vérifier que le certificat est un certificat d'autorité (catégorie _authority_) et biensûr que ce soit bien un certificat (type _certificate_). +::: + +Il est alors installé dans le dossier `/etc/ca-certificates/trust-source/`. +Pour supprimer le certificat des certificats de confiance, on utilise le fichier généré dans ce dossier. + +```shell +# trust anchor --remove /etc/ca-certificates/trust-source/ache.dns.p11-kit +``` + +## Création du certificat DNS + +Idem, on commence par créer une clé privée. + +### La clé privée + +```shell +$ openssl genrsa -out website.key 3072 +``` + +Puis, on crée une **demande de signature**. +Celle-ci permet de configurer la signature par l'autorité de certification. + +### La demande de signature + +Normalement, dans le processus normal, le CA intermédiaire reçoit une demande de signature et initie alors le processus de vérification de l'identité du serveur qui demande la signature. + +On peut le faire avec OpenSSL simplement : + +```shell +$ openssl req -new -key website.key -out website.csr -utf8 -nameopt multiline,utf +``` + +:::info +Ici, j'ai rajouté les paramètres `-utf8 -nameopt multiline,utf8` afin d’accepter les caractères non ASCII dans les informations de certification (Nom de l'entreprise, de la région ...). +::: + +OpenSSL va alors nous demander les informations de certification en mode interactif. +On peut éviter cela soit en remplissant les données directement en ligne de commande. + +```shell +$ openssl req -new -out website.csr -sha256 -days 3650 -nodes -subj "/C=FR/ST=Centre/L=La grande Ville/O=NomDeLEntreprise/OU=/CN=CommonNameOrHostname" +``` + +Soit utiliser un fichier de configuration. +C'est ce que j'ai choisi de faire afin de facilité la recréation du certificat. + +```sh +$ openssl req -new -key website.key -out website.csr -utf8 -nameopt multiline,utf8 -config website.conf +``` + +Le fichier de configuration ressemble à ceci : + +```toml +[ req ] +prompt = no +default_bits = 3072 +default_md = sha256 +distinguished_name = server_distinguished_name +req_extensions = v3_ext + +[ server_distinguished_name ] +commonName = ache +countryName = FR +stateOrProvinceName = XXX +emailAddress = ache@ache.one +organizationName = ache local CA organization + +[ v3_ext ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[ alt_names ] +DNS.0 = website.ache.one +DNS.1 = website_alt.ache.one +``` + +## Créer le certificat final + +Il ne reste plus qu'à créer le certificat. +On a besoin pour cela de : + +- La clé du certificat racine +- Le certificat racine +- La clé du certificat final +- La demande de signature + +```shell +$ openssl x509 -req -CA ../root_ca/root_ca.cert -CAkey ../root_ca/root_ca.key -in website.csr -out website.cert -days 10 -CAcreateserial -extensions v3_ext -extfile website.conf -sha256 +``` + +## Configuration du certificat dans nginx + +Il n'y a rien de bien sorcier. +On rajoute `ssl` aux `listen` et on configure TLS. +Pour cela, il faut indiquer où est la clé de chiffrement ainsi que le certificat. + +```text + listen [::]:443 ssl; + listen 443 ssl; + http2 on; # Not related to TLS + + ssl_certificate /srv/www/website.cert; # managed by myself ! + ssl_certificate_key /srv/website/cert/website.key; + ssl_stapling off; # Since it's a self-signed certificate. No need for OCSP stapling. + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +``` + +On peut remarquer qu'il est possible de passer la chaîne de certificat également en concaténant le certificat final (en premier) avec le certificat racine. +Je ne sais pas si cela a un réel avantage, mais c'est une pratique courante. + +## Mise à jour du certificat + +Il est conseillé de mettre en place une méthode automatique de mise à jour du certificat. +Je recommande un timer systemd qui va vérifier la validité du certificat et le renouveler à intervalles réguliers. + +`check-certificates.timer` : + +```toml +# This timer unit is for refreshing local certificates everyday + +[Unit] +Description=Refresh self-signed certificates regularly +Requires=check-certificates.service + + +[Timer] +Unit=check-certificates.service +OnUnitActiveSec=86400 + +[Install] +WantedBy=timers.target +``` + +Et `check-certificates.service` + +```toml +# This service unit is for refreshing local certificates if needed + +[Unit] +Description=Refresh local certificates if needed +Wants=check-certificates.timer + +[Service] +Type=oneshot +ExecStart=/usr/bin/check-certificates.sh + +[Install] +WantedBy=multi-user.target +``` + +J'ai ainsi créé un script bash pour automatiser la vérification des certificats et la mise à jour des fichiers de configuration de Nginx. + +:::details + +Contenue du fichier `check-certificates.sh` + +::::summary + +```bash +#!/bin/env bash + +# This script checks the validity of the certificates in the directory +# ${CERTIFICATES_DIR} and renews them if necessary. +# +# It uses the local CA certificate and key to check the validity of the +# certificates in the directory ${CERTIFICATES_DIR}. +# +# The script is intended to be run from a systemd timer every day. + + +# TODO: Configure the following variables from cli arguments +# TODO: Rewrite this shit in Python +CERTIFICATES_DIR="/home/works/certs/" +LOCAL_CA_CERT="${CERTIFICATES_DIR}/local_ca.cert" +LOCAL_CA_KEY=$(echo "${LOCAL_CA_CERT}" | sed 's/.cert$/.key/') + +HAS_RENEW_CERT="" +if [ ! -r "$LOCAL_CA_CERT" ]; then + echo "☠️ Please re-check that local CA cert \"$LOCAL_CA_CERT\" exists" + exit +fi +if [ ! -r "$LOCAL_CA_KEY" ]; then + echo "☠️ Please re-check that local CA key \"$LOCAL_CA_KEY\" exists" + exit +fi + +date +echo -e "Checking certificate validity (NotAfter field)\n" + +pushd ${CERTIFICATES_DIR} >/dev/null 2>/dev/null +for cert in $(find -name "*.cert"); do + if openssl x509 -checkend 345600 -noout -in ${cert} > /dev/null; then + echo -e "✔️ $(basename ${cert}) will expire in more than 4 days" + else + echo -e "❌ $(basename ${cert}) will expire soon !" + if [ $(basename "$cert") = $(basename "$LOCAL_CA_CERT") ]; then + echo -e "\t⛔ I don't renew local CA certificate" + continue + fi + + NEW_CSR=$(echo ${cert} | sed 's/.cert/.csr/') + CERT_KEY=$(echo ${cert} | sed 's/.cert/.key/') + CERT_CONFIG=$(echo ${cert} | sed 's/.cert/.conf/') + + echo "Renewing ${cert}" + echo "Creating new CSR ($NEW_CSR)" + echo ${cert} + echo ${CERT_KEY} + echo ${CERT_CONFIG} + + # Sign the new signing request + if openssl req -new -key "${CERT_KEY}" -out "${NEW_CSR}" -config "${CERT_CONFIG}"; then + echo "👍 CSR created" + else + echo "❌ Failled to create signing request!" + continue + fi + + echo "Renewing certificate" + openssl x509 -req -CA "${LOCAL_CA_CERT}" -CAkey "${LOCAL_CA_KEY}" -in "${NEW_CSR}" -out "${cert}" -days 10 -CAcreateserial -extensions v3_ext -extfile "${CERT_CONFIG}" -sha256 + if [ $? -eq 0 ]; then + HAS_RENEW_CERT="yes" + echo "✔️ Certificate renew" + else + echo "❌ Failled to renew certificate!" + fi + fi +done + +echo -e "\nNo more certificate to check" +if [ -n "$HAS_RENEW_CERT" ]; then + echo "🔃 Reload nginx configuration (and update certificates)" + nginx -s reload +fi + +popd >/dev/null 2>/dev/null +``` + +:::: diff --git a/notes/res/certificat-signed.svg b/notes/res/certificat-signed.svg new file mode 100644 index 0000000..63278d7 --- /dev/null +++ b/notes/res/certificat-signed.svg @@ -0,0 +1 @@ +<svg:svg xmlns:svg="http://www.w3.org/2000/svg" width="215" height="215" fill="none" version="1.1" viewBox="0 0 215.493 215.493"><svg:defs/><svg:path stroke="#1a1b1f" stroke-dasharray="6, 3" stroke-width="1.849" d="m 107.20009,8.8400154 21.80278,16.5838846 27.17395,-3.460579 10.58959,25.263491 25.26402,10.58993 -3.46058,27.173948 16.58369,21.80278 -16.58369,21.80277 3.46058,27.17395 -25.26402,10.5896 -10.58959,25.26402 -27.17395,-3.46057 -21.80278,16.58368 L 85.397306,188.16324 58.223357,191.62381 47.633766,166.35979 22.369741,155.77019 25.830318,128.59624 9.2466353,106.79347 25.830318,84.99069 22.369741,57.816742 47.633766,47.226812 58.223357,21.963321 85.397306,25.4239 Z" class="anim rev"><svg:animate attributeName="stroke-dashoffset" calcMode="linear" dur="2s" repeatCount="indefinite" values="18;0"/></svg:path><svg:path fill="#d6fff8" stroke="#1a1b1f" stroke-width="1.927" d="m 64.255208,45.81924 h 70.884212 l 18.08225,24.386957 V 165.80548 H 64.255208 Z" style="fill:#fff880;fill-opacity:1"/><svg:path stroke="#1a1b1f" stroke-width="1.927" d="m 134.84017,45.81924 v 24.79054 h 18.3815"/><svg:line x1="73.078" x2="128.958" y1="81.518" y2="81.518" stroke="#1a1b1f" stroke-width="1.927"/><svg:line x1="73.078" x2="110.577" y1="94.409" y2="94.409" stroke="#1a1b1f" stroke-width="1.927"/><script/><svg:path d="m 9.836065,2.0341911 c 0.112518,0.038276 0.222436,0.083805 0.329064,0.1363026 l 1.282734,0.6315458 c 0.348138,0.1714034 0.756137,0.1714034 1.104274,0 l 1.282734,-0.6315458 c 1.362589,-0.6708615 3.011025,-0.1101062 3.681887,1.2524821 l 0.07348,0.1623228 0.06282,0.1667409 0.460459,1.3536002 c 0.12497,0.3673712 0.413469,0.65587 0.78084,0.7808401 l 1.353601,0.4604596 c 1.437866,0.4891247 2.206973,2.0512594 1.717848,3.4891256 -0.03828,0.112518 -0.08381,0.222436 -0.136303,0.329064 l -0.631546,1.282734 c -0.171403,0.348138 -0.171403,0.756137 0,1.104274 l 0.631546,1.282734 c 0.670862,1.362589 0.110106,3.011025 -1.252482,3.681887 -0.106627,0.0525 -0.216545,0.09803 -0.329063,0.136303 L 18.89436,18.11352 c -0.367371,0.12497 -0.65587,0.413469 -0.78084,0.78084 l -0.460459,1.353601 c -0.489125,1.437866 -2.05126,2.206973 -3.489126,1.717848 -0.112518,-0.03828 -0.222436,-0.08381 -0.329064,-0.136303 L 12.552137,21.19796 c -0.348137,-0.171403 -0.756136,-0.171403 -1.104274,0 l -1.282734,0.631546 C 8.8025404,22.500368 7.1541036,21.939612 6.4832421,20.577024 6.4307448,20.470397 6.3852151,20.360479 6.3469394,20.247961 L 5.8864798,18.89436 C 5.7615097,18.526989 5.4730109,18.23849 5.1056397,18.11352 L 3.7520395,17.653061 c -1.4378662,-0.489125 -2.2069731,-2.05126 -1.7178484,-3.489126 0.038276,-0.112518 0.083805,-0.222436 0.1363026,-0.329064 l 0.6315458,-1.282734 c 0.1714034,-0.348137 0.1714034,-0.756136 0,-1.104274 L 2.1704937,10.165129 C 1.4996322,8.8025404 2.0603875,7.1541036 3.4229758,6.4832421 3.5296031,6.4307448 3.6395214,6.3852151 3.7520395,6.3469394 L 5.1056397,5.8864798 C 5.4730109,5.7615097 5.7615097,5.4730109 5.8864798,5.1056397 L 6.3469394,3.7520395 C 6.8360641,2.3141733 8.3981988,1.5450664 9.836065,2.0341911 Z M 15.46967,8.9696699 10.050399,14.388941 8.076166,12.019862 C 7.8109929,11.701654 7.3380694,11.658661 7.0198617,11.923834 6.701654,12.189007 6.658661,12.661931 6.923834,12.980138 l 2.5,3 c 0.2823364,0.338804 0.794645,0.362043 1.106496,0.05019 l 6,-6 c 0.292893,-0.2928931 0.292893,-0.7677669 0,-1.0606601 -0.292893,-0.2928932 -0.767767,-0.2928932 -1.06066,0 z" style="fill:#80b7ff;fill-opacity:1;stroke:none;stroke-width:.429579;stroke-dasharray:none;stroke-opacity:.46557" transform="matrix(2.1092 0 0 2.1092 102.13 114.153)"/></svg:svg>
\ No newline at end of file diff --git a/notes/res/chain_of_trust.avif b/notes/res/chain_of_trust.avif Binary files differnew file mode 100644 index 0000000..a1b114a --- /dev/null +++ b/notes/res/chain_of_trust.avif diff --git a/notes/res/chain_of_trust.png b/notes/res/chain_of_trust.png Binary files differnew file mode 100644 index 0000000..e27c0b2 --- /dev/null +++ b/notes/res/chain_of_trust.png diff --git a/notes/res/chaîne_de_certification.avif b/notes/res/chaîne_de_certification.avif Binary files differnew file mode 100644 index 0000000..7152fe6 --- /dev/null +++ b/notes/res/chaîne_de_certification.avif diff --git a/notes/res/chaîne_de_certification.png b/notes/res/chaîne_de_certification.png Binary files differnew file mode 100644 index 0000000..b0f5f5d --- /dev/null +++ b/notes/res/chaîne_de_certification.png |