🚀 Deploy d'une App Node.js avec NGINX et PM2 + Config Linux Server + Certificat SSL
Tags: nginx, node, deploy, devops
Published: 2023-10-17 07:05:51

« Mise en prod' » , « Deploy », « Push ».. Tant d'expressions qui permettent de signifier l'aboutissement d'un long chemin de développement : ça y est, votre code est fin prêt à être déployé dans un environnement de production et votre application/serveur exposé(e) au monde entier.

Si vous n'avez jamais mis d'applications en ligne auparavant, les appréhensions peuvent être nombreuses.

Comment configurer un **serveur Linux** pour exécuter des applications **Node.js** ? Pourquoi utiliser un gestionnaire de processus ? Comment mettre en place un **certificat SSL** et profiter du protocole **HTTPS** ? Comment configurer la **zone DNS** d'un nom de domaine ?

---

## ⚙️ Pré-configuration de votre serveur

L'utilisation d'un **VPS** _(= Virtual Private Server)_ est l'une des solutions les plus adaptées lorsque l'on doit déployer par exemple une ou plusieurs applications **Node.js**.

Vulgairement parlant, un **VPS** : c'est une machine comme la votre exposée via une adresse IPv4 dédiée, qui a pour vocation d'**être allumée 24h/24 - 7j/7**. Elle a généralement la particularité d'intégrer un système d'exploitation minimaliste, léger et nécessitant peu de ressources : **Linux**.

📶 La connexion à ce serveur distant est effectuée via le protocole SSH (Port 22), c'est par l'intermédiaire d'une invite de commande que notre serveur va être configurable.

Lorsque vous commandez un **VPS**, les informations suivantes vous sont communiquées :  

> - Adresse IP (Host)
- Username
- Password
- Port (22)

Ces informations permettent d'établir une connexion SSH. La plupart des systèmes d'exploitations intègrent aujourd'hui un client SSH mais certains nécessitent l'utilisation d'un client SSH tierce, c'est le cas avec Windows par exemple : l'installation du logiciel [PuTTY](https://www.putty.org/) est alors requise.

Si votre VPS est livré avec un système d'authentification : hôte / nom d'utilisateur / mot de passe, il n'a cependant pas pour vocation à le rester. Ce type d'authentification est exposé à de nombreux types d'attaques, le **bruteforce** notament.

**Pour pallier à cette problématique** : l'autorisation d'un client par le serveur depuis une pair de clé SSH préalablement générée

La première mise à niveau du serveur implique alors **les actions suivantes** :

- Autoriser une/des clé(s) pour s'authentifier au protocole SSH
- Supprimer l'authentification par mot de passe du protocole SSH

---

## 🔑 Générer une nouvelle pair de clé SSH, localement

### 🌕 Si vous utilisez Linux

Accédez localement à votre dossier `~/.ssh` puis générez une nouvelle clé si vous n'en n'avez pas déjà une : 

```
    cd ~/.ssh
    ssh-keygen -t rsa -b 4096 -C "your_label"
    
```

Vous n'êtes pas obligé(e) d'inclure une passphrase à votre nouvelle clé, appuyez sur ENTRER sans même saisir de phrase de sécurité, celle-ci est souvent négligée par les développeurs, notament lorsqu'une clé est sollicitée en guise d'authentification via un processus automatisé.

```
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/yourusername/.ssh/id_rsa):
    Created directory '/home/yourusername/.ssh'.
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /home/yourusername/.ssh/id_rsa.
    Your public key has been saved in /home/yourusername/.ssh/id_rsa.pub.
    The key fingerprint is:
    SHA256:2m0FfefEAFeJ31mFw87DAfef3FA113ge your_label
    The key's randomart image is:
    +---[RSA 4096]----+
    |      .          |
    |                 |
    | .               |
    |. . . .          |
    |. .=.o .S.       |
    | =o.o. ..   .    |
    |o +   .  . o ..  |
    |.. .      oEoo . |
    |o.        .o+oo  |
    +-----------------+
```

Votre pair de clé est à présent créée.

Récupérez votre clé publique grâce à la commande suivante : `cat ~/.ssh/id_rsa.pub`.

C'est cette clé que nous autoriserons côté serveur, nous pourrons ainsi nous authentifier à notre serveur distant sans même avoir à justifier d'un mot de passe*.
_(*) sous réserve qu'aucune phrase de sécurité n'ai été définie_

### 🌑 Si vous utilisez Windows

Le logiciel [PuTTYGen](https://www.puttygen.com/download-putty) va vous permettre de générer une nouvelle pair de clé.

Une fois le logiciel ouvert, cliquez sur "Generate", agitez la souris aux 4 coins de la fenêtre (stimulation cryptographique) et patientez un instant durant la génération de votre clé.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8htf4qwi19atcmbex5l6.png)

Cette clé publique générée a vocation d'être autorisée auprès de notre serveur. Notre clé privée permettra de nous authentifier avec [PuTTY](https://www.putty.org/) via le protocole SSH sans utiliser de mot de passe*.
_(*) sous réserve qu'aucune phrase de sécurité n'ai été définie_

Cliquez ensuite sur le bouton "**Save private key**" pour enregistrer votre clé au format `.ppk` (_Putty Private Key_ - format dédié à PuTTY). 

---

## 🚦 Connexion au serveur et autorisation de notre nouvelle clé

Il est temps de **se connecter pour la première fois à notre serveur**, nous utiliserons logiquement l'adresse distante de notre machine, l'identifiant et le mot de passe fournis par notre hébergeur.

```
    ssh ubuntu@102.49.82.203
    ...
    
    login as: ubuntu
    ubuntu@102.49.82.203's password: 
```

Une fois connecté(e),

Ouvrez le fichier `authorized_keys` via la commande : `sudo nano ~/.ssh/authorized_keys`

[...] Et ajoutez votre clé publique, précédemment générée, en la collant.

ℹ️ Voici un exemple de ce à quoi pourrait ressembler votre fichier `~/.ssh/authorized_keys` si vous aviez autorisé plusieurs clés; toutes les clés autorisées peuvent permettre de s'authentifier au serveur sans mot de passe :

```
    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQCo9+BpMRYQ/dL3DS2CyJxRF+j6ctbT3/Qp84+KeFhnii7NT7fELilKUSnxS30WAvQCCo2yU1orfgqr41mM70MB your_label
    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCb4oUsXZ51L9DmH3UqSnwOAUr9w6AOZa8bYH8qsJAhypAjH9YvQteVXXQEY0ybaVE1cmpIKEyC2jmC/jOJ4b7bivlo2hgnLOrj3FPkDWO2yNNio9RnPXREBx7 special_key
```

Sauvegardez à présent votre fichier, félicitations 🎉 

Vous pouvez dorénavant vous authentifier auprès de votre serveur en utilisant la clé précédemment générée.

Voyons à présent comment s'authentifier à notre serveur.

---

## 🔒 S'authentifier à notre serveur via SSH grâce à notre nouvelle clé

### 🌕 Si vous utilisez Linux

Connectez-vous à votre serveur via la commande traditionnelle : `ssh user@host`

✅ Aucun mot de passe n'est à présent requis, l'authentification est directement effectuée.

_Si une phrase de sécurité (= passphrase) a été configurée lors de la génération de votre clé, cette dernière sera requise lors de la tentative de connexion :_ 

```
    login as: ubuntu
    Authenticating with public key "id_rsa"
    Passphrase for key "id_rsa":
```

### 🌑 Si vous utilisez Windows

Ouvrez le logiciel [PuTTY](https://www.putty.org/) _(à ne pas confondre avec PuTTYGen permettant de générer des clés)_ et renseignez l'**adresse de votre serveur** distant, définissez le **port** SSH (22).

Rendez-vous dans la catégorie : **Connection > SSH > Auth > Credentials**.

Localisez votre clé privée (au format `.ppk`) préalablement enregistrée lors de l'étape de la génération de clé.

Retournez dans la catégorie "**Sessions**" puis cliquez sur "**Save**". Vos informations de connexion (y compris votre clé privée) seront alors gardés en mémoire. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yuhy3pfgipgal54aprpd.png)

Séléctionnez à présent la session que vous venez de sauvegarder et lancez-là.

Vous êtes à présent authentifié(e) à votre serveur*.
_(*) sous réserve qu'aucune phrase de sécurité n'ai été définie_

---

## ⛔ Désactiver l'authentification par identifiant / mot de passe

Notre authentification via clé SSH est à présent fonctionnelle ✅

L'authentification par l'intermédiaire d'une combinaison identifiant / mot de passe peut alors être supprimée, elle est la potentielle porte d'entrée à d'éventuelles intrustions malveillantes.

Commencons par modifier le fichier `/etc/ssh/sshd_config` en utilisant la commande :

`sudo nano /etc/ssh/sshd_config`

Notre mission : repérer les variables suivantes :

- `PasswordAuthentication`
- `ChallengeResponseAuthentication`
- `UsePAM`

[...] et leur attribuer la valeur suivante : `no`.

_ℹ️ Si l'une de ces variables est absente, rajoutez-là manuellement et définissez-là à `no`._

La variable `PubkeyAuthentication` quant à elle doit être définie à `yes`.

Dès **lorsque les modifications sont terminées**, utilisez généralement **CTRL + X** pour fermer et saisissez "**Y**" pour confirmer les changements.

_ℹ️  Vérifiez parallèlement qu'aucun fichier `.conf` ne soit inclus dans les répertoires suivants : `/etc/ssh/ssh_config.d` et `/etc/ssh/sshd_config.d`. Certains hébergeurs définissent parfois des variables dans des fichiers de configuration à ces endroits pour, entre autre, autoriser la connexion via identifiant / mot de passe._

Le serveur SSH aura besoin d'être redémarré pour que les changements soient pris en compte, saisissez la commande suivante : `sudo systemctl restart sshd ssh`.

**Notre serveur n'accepte dorénavant plus les connexions avec mot de passe**. Seules les clés autorisées via notre registre `~/.ssh/authorized_keys` pourront s'authentifier au protocole SSH de notre serveur.

---

## 📥 Télécharger et installer la dernière version de Node.js

**Node.js** est souvent téléchargé depuis les dépôts officiels / PPA _(= Personal Package Archives)_ Ubuntu.

Nous délaisserons cette méthode en téléchargeant directement **Node.js** depuis la [page de téléchargement du site officiel](https://nodejs.org/en/download), selon la version souhaitée. Ainsi, nous sommes certains de pouvoir profiter d'une version up-to-date.

> _ℹ️ Dans un environnement de production, il est recommandé d'utiliser l'une des dernières versions de Node.js_.

Dans ce tutoriel, nous utiliserons la **version 18.15.0**.

1️⃣ Localisez-vous au chemin d'accès par défaut `~` de votre serveur grâce à `cd ~/` puis téléchargez la version Node.js cible depuis le site officiel grâce à la commande :

`wget https://nodejs.org/dist/v18.15.0/node-v18.15.0-linux-x64.tar.xz`

```
    --2023-04-01 12:49:28
    --`https://nodejs.org/dist/v18.15.0/node-v18.15.0-linux-x64.tar.xz`
    Resolving nodejs.org (nodejs.org)... 2606:4700:10::6814:162e, 2606:4700:10::6814:172e, 104.20.23.46, ...
    Connecting to nodejs.org (nodejs.org)|2606:4700:10::6814:162e|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 23637576 (23M) [application/x-xz]
    Saving to: ‘node-v18.15.0-linux-x64.tar.xz’
    
    `node-v18.15.0-linux` 100%[===================>]  22.54M  28.9MB/s    in 0.8s    
    
    2023-04-01 12:49:28 (28.9 MB/s) - ‘node-v18.15.0-linux-x64.tar.xz’ saved [23637576/23637576]
```

2️⃣ Décompressez le fichier `.tar.xz` téléchargé en utilisant :

`sudo tar -xvf node-v18.15.0-linux-x64.tar.xz`

3️⃣ Puis transférez les fichiers source de **Node.js** dans le répertoire `/usr/` en utilisant la commande :

`sudo cp -r node-v18.15.0-linux-x64/{bin,include,lib,share} /usr/`

4️⃣ Supprimons sans tarder notre fichier `node-v18.15.0-linux-x64.tar.xz` ainsi que le répertoire décompressé `node-v18.15.0-linux-x64`, ils ne nous seront plus d'aucune utilité.

`sudo rm -rf node-v18.15.0-linux-x64 node-v18.15.0-linux-x64.tar.xz`

5️⃣ Ouvrez à présent le script de votre shell via `sudo nano ~/.bashrc` et exposez **Node.js** en ajoutant la ligne suivante, à la fin :

`export PATH=/usr/node-v18.15.0-linux-x64/bin:$PATH`

6️⃣ Rechargez à présent le script shell `~/.bashrc` en utilisant `source ~/.bashrc`.

**Node.js** est normalement installé ✅

Vérifiez que l'installation soit conforme en saisissant `node -v` dans votre terminal.

---

## 🔐 Authentification auprès de Github

**Git** est et restera sans doûte le meilleur ami de celles & ceux qui pratiquent la programmation. Il est un outil indispensable du quotidien et sert notament d'intermédiaire entre votre environnement de développement et votre environnement de production.

La configuration de l'**authentification avec Github** est particulièrement importante.

L'authentification via pair de clé SSH peut par exemple outrepasser la nécessité de justifier d'un mot de passe. Elle peut alors permettre la manipulation automatisée d'un 🌐 **repository** grâce à un processus de **CI/CD** _(= Continuous Integration/Continuous Delivery)_.

🔒 Intéressons-nous à l'**authentification via pair de clé SSH**.

Lors de la configuration de notre serveur Linux : nous avions généré, localement, une pair de clé SSH et nous avions autorisé notre clé publique depuis le serveur.

Cette fois, c'est **sur le serveur** que la pair de clé SSH va devoir être générée. La clé publique sera transmise à **Github** via les paramètres de notre compte et les échanges avec **Github** seront dès lors authentifiés.

Localisez-vous dans le dossier `~/.ssh` de votre machine distante puis générez votre nouvelle pair de clé SSH :

```
    cd ~/.ssh
    ssh-keygen -t rsa -b 4096 -C "your_email@domain.com"
```

Suivez la procédure de génération, ajoutez ou non une phrase de sécurité puis patientez un instant. 

```
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa): 
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /home/ubuntu/.ssh/id_rsa
    Your public key has been saved in /home/ubuntu/.ssh/id_rsa.pub
    The key fingerprint is:
    SHA256:c7bEyoefeR09TZ9vnEmddK2LwD4XRo4ZCLfmE3kKKTWoM your_email@domain.com
    The key's randomart image is:
    +---[RSA 4096]----+
    |    o . oo  .    |
    |  .  o     . o   |
    | o .  o   . + .  |
    |o      . + + .  .|
    |E s.    S X .   +|
    |.o     . B T..+.+|
    |.       = *.+o.* |
    |  ..  .  *.  .o o|
    |        ..o.     |
    +----[SHA256]-----+
```

Il ne vous reste plus qu'à copier votre clé publique en utilisant : `cat ~/.ssh/id_rsa.pub`.

Rendez-vous sur [https://github.com/settings/keys](https://github.com/settings/keys), cliquez sur "**Nouvelle clé SSH**" puis ajoutez votre clé publique en l'identifiant par un nom de référence.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9g1l4tl3c0lgzxyfoibg.png)

Vérifiez, dans le terminal de votre serveur, que vous êtes bien authentifié en utilisant la commande : `ssh -T git@github.com`.

Si vous êtes authentifié, vous reçevrez un message similaire à celui-ci 👇

`Hi {username}! You've successfully authenticated, but GitHub does not provide shell access.`

[...] Vous pouvez à présent cloner votre projet sur votre serveur.

---

## 🟰 Cloner une application depuis un repository Git

A la racine `~` de votre serveur distant, clônez votre application grâce à la commande :

`git clone git@github.com:{username}/{project}.git`

> _ℹ️  Prenez soin d'actualiser les variables `{ }` par vos données (= nom d'utilisateur Git et nom de projet)_.

Une fois votre application clônée et accessible en local, localisez-vous à la racine de votre projet à l'aide du nom de votre projet précédé par la commande `cd`.

> _ℹ️ Supposons que notre application soit une simple application React_.

Créez votre fichier d'environnement de production (dotenv) à la racine (= `.env` ou `.env.local`).

Installez vos dépendances et lancez le build de votre projet à l'aide de `npm i` et `npm run build`. 

⚙️ Vérifiez que votre application puisse se lancer via `npm run start` et tentez de joindre votre application via l'adresse de votre machine depuis le port utilisé par **Node.js**.

Mettez fin au processus **(CTRL + C)**.

⚠️ Soyez sûre que votre application soit fonctionnelle en installant les éventuels services dépendants _(= (No)SQL database, Redis, Docker, API..)_ 

---

## 📂 Utiliser le protocole SFTP dédié aux transferts de fichiers

Que vous utilisiez **Mac OS, Windows** ou **Linux**, le logiciel de transfert de fichiers [FileZilla](https://filezilla-project.org/download.php) vous permettra via une interface graphique de gérer les ressources de votre serveur (répertoires/fichiers).

> _ℹ️ Le transfert de fichiers se révèle particulièrement indispensable pour les sites web basiques incluant un système d'upload de fichiers. Ces derniers n'utilisant pas de solutions liées au cloud comme S3 AWS / Azure / Google Storage etc.. Les fichiers sont alors stockés sur le serveur_.

Le transfert de fichiers requiert l'utilisation d'un protocole **FTP** _(= File Transfert Protocol)_. Un serveur **FTP** _(Port 21)_ doit alors être installé sur le serveur.

Pour pallier à cette épreuve, une alternative s'offre à nous : le **SFTP** _(= SSH File Transfert Protocol)_.

Le **SFTP** accompli les missions du **FTP** mais en utilisant un autre protocole : le **SSH** _(Port 22)_, le même que nous utilisons quotidiennement pour intéragir via shell avec notre serveur distant. Nous bénéficions dès lors des atouts qu'offre le **SSH** : à savoir une connexion sécurisée via pair de clé **SSH**.

En principe et selon notre mode opératoire, **FileZilla** charge notre clé **SSH** _(préalablement générée)_ et la transmet à notre serveur qui nous autorise à établir la connexion.

Renseignez dès-lors l'adresse IPv4 de votre serveur, votre identifiant, le port : 22 puis procédez à la connexion.

✅ Votre clé SSH est détectée, transmise et approuvée par le serveur, vous êtes authentifié(e).

**La clé n'est pas détectée ?**

Si la clé n'est pas détectée, vous utilisez probablement **Windows**. Dans **FileZilla**, localisez la clé `.ppk` que vous aviez générée avec **PuTTYGen** depuis :

**Filezilla > Fichier > Gestionnaire de sites > Type d'authentification > Fichier de clef**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exh5snfpve0itavkgl08.png)

[...] Et chargez votre clé, la connexion devrait à présent aboutir.

---

## 📥 Installer et configurer le gestionnaire de processus PM2

Le gestionnaire de processus va permettre d'**initialiser chacune de vos applications Node.js** en les répertoriant dans des **conteneurs centralisés**. Il va occuper un rôle majeur en ce qui concerne la maintenance et le processus de déploiement de vos applications.

Chacun de ces conteneurs peut alors être (re)démarré/arrêté et exploité individuellement.

> _🔁 En cas de redémarrage impromptu du serveur, le gestionnaire de processus permet de redémarrage de l'intégralité de vos applications afin d'éviter une indisponibilité prolongée de vos services. Il donne également accès aux logs et à un système de monitoring efficace concernant vos processus._

Nous utiliserons [PM2](https://github.com/Unitech/pm2), une librairie disponible sur [yarn](https://yarnpkg.com/package/pm2) et [npm](https://www.npmjs.com/package/pm2), simple, complète et efficace d'utilisation.

### 💡 Et si nous n'utilisions pas de gestionnaire de processus ?

Si par exemple vous démarriez votre application via une session SSH sans utiliser de gestionnaire de processus (via `npm run start` par exemple) : un processus serait alors lancé.

Dans l'hypothèse où vous abandonneriez ce(tte) terminal/session, vous perderiez la main sur le processus mais celui-ci continuerai alors d'être exécuté en arrière plan _(vous pourriez évidement le reprendre dans une session ultérieure)_.

Si hasardeusement, vous oublieriez qu'une application Node.js était déjà en cours d'exécution, vous la lanceriez une nouvelle fois et il n'existerait alors pas moins de deux occurences d'une même application Node.js _(x2 processus en arrière plan)_.

Autrement, si vous aviez par exemple plusieurs applications Node.js sur le même serveur, vous ne pourriez même pas les distinguer dans la liste des processus en cours d'exécution.

C'est à ces problématiques que le gestionnaire de processus PM2 apporte des solutions 🔥

### 💹 Comment ça fonctionne ?

Installons maintenant notre gestionnaire de processus, sur notre serveur distant, via la commande :

`sudo npm i pm2 -g`

Vérifiez que **PM2** soit installé sur votre machine en exécutant : `pm2 -v`.

> ℹ️ Le drapeau `-g` permet d'installer la librairie globalement et d'enregistrer la nouvelle variable d'environnement `pm2`.

Le mode de fonctionnement de **PM2** permet d'initialiser un nouveau processus depuis la racine d'une application **Node.js**, au même niveau que notre traditionnel `package.json`.

Il intègre d'ailleurs la reconnaissance d'un fichier de configuration intitulé `ecosystem.config.js`. Dès lorsque ce fichier est présent à la racine de votre projet, il va permettre au gestionnaire de processus de démarrer votre application avec les différentes propriétés spécifiées.

> _ℹ️ La documentation des différentes propriétés disponibles pour la configuration du fichier `ecosystem.config.js` est [disponible en ligne](https://pm2.keymetrics.io/docs/usage/application-declaration/)._

Avant d'initialiser votre application dans **PM2**, prenez-soin de créer un fichier `ecosystem.config.js` à la racine de votre projet puis d'y apporter la configuration requise minimale : 

```
    module.exports = {
	    apps : [{
    		name: 'my-process-name',
    		script : "npm run start",
	    }]
	};
```

Exécutez à présent la commande `pm2 start` à la racine de votre projet.

```
    [PM2][WARN] Applications my-process-name not running, starting...
	[PM2] App [my-process-name] launched (1 instances)
	┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
	│ id │ name               │ mode     │  ↺   │ status    │ cpu      │ memory   │
	├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
	│ 0  │ my-process-name    │ fork     │ 0    │ online    │ 0%       │ 10.2mb   │
	└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
```

Le nouveau processus est créé et démarré 🎉 

D'autres applications peuvent ainsi être ajoutées à la liste des processus PM2.

> _ℹ️ La commande `pm2 startup ubuntu` permet de garantir le redémarrage de vos processus après un éventuel crash/reboot intempestif de votre machine._

L'exécution, l'arrêt et le redémarrage de votre application devra à présent passer par PM2.

```
    // Lister les processus référencés
    pm2 list
    
    // Démarrer une application
    pm2 start my-process-name

    // Arrêter une application
    pm2 stop my-process-name
    
    // Redémarrer une application
    pm2 restart my-process-name
    
    // Accéder aux logs d'une application
    pm2 logs my-process-name
    
    // Accéder au panel de monitoring instantanée
    pm2 monit
    
    // Sauvegarder le référencement des applications chargées
    pm2 save
    
    // Restaurer des applications depuis une sauvegarde
    pm2 resurrect
    
    // Démarrer les processus lors du démarrage du système
    pm2 startup ubuntu
```

---

## 📥 Installer et configurer le serveur web NGINX

L'utilisation d'un serveur web en guise de surcouche à **Node.js** est tout à fait recommandé. Le serveur web **Nginx** offre par exemple de nombreux avantages :

- Concentrer l'audience de vos applications sur un même port _(80 par défaut)_
- Intégrer d'indénombrables fichiers de configuration clairs et documentés. _([Voir la documentation officielle](http://nginx.org/en/docs/))_
- Optimiser le traitement des requêtes
- Gérer la mise en cache et la compression des données

[...] Et surtout : **permettre l'utilisation d'un Reverse Proxy** 🔥

Le **proxy inverse** permet de :

- Rediriger des requêtes entrantes vers différents serveurs internes utilisant différents protocoles.
- Agir en tant que répartiteur de charge entre plusieurs serveurs, si configuré comme tel.
- Décharger les serveurs d'une charge d'objets/pages statiques via la gestion d'un cache web local.
- Agir en tant que point de terminaison **TLS** et permettre le chiffrement via **certificat SSL**.
- Réduire la charge de traitement cryptographique (= **HTTPS**) si installé sur une machine différente des serveurs applicatifs.
- [...]

> _ℹ️ L'utilisation du reverse proxy a pour vocation de n'exposer que le port 80. Ainsi, les différents ports qu'occupent nos applications internes pourront être bloqués par notre pare-feu._

Procédons à l'installation de **Nginx** sur notre machine distante :

`sudo apt install nginx`

```
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    The following packages were automatically installed and are no longer required:
      javascript-common libc-ares2 libjs-highlight.js libnode72 nodejs-doc
    Use 'sudo apt autoremove' to remove them.
    The following additional packages will be installed:
      libnginx-mod-http-geoip2 libnginx-mod-http-image-filter
      libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream
      libnginx-mod-stream-geoip2 nginx-common nginx-core
    Suggested packages:
      fcgiwrap nginx-doc ssl-cert
    The following NEW packages will be installed:
      libnginx-mod-http-geoip2 libnginx-mod-http-image-filter
      libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream
      libnginx-mod-stream-geoip2 nginx nginx-common nginx-core
    0 upgraded, 9 newly installed, 0 to remove and 0 not upgraded.
    Need to get 696 kB of archives.
    After this operation, 2395 kB of additional disk space will be used.
    Do you want to continue? [Y/n]
```

Saisissez **_"Y"_** et appuyez sur **_ENTRER_**.

✅ Nginx est à présent installé sur votre machine.

Remplacez intégralement le contenu du fichier `/etc/nginx/sites-available/default` :

`sudo nano /etc/nginx/sites-available/default`

[...] par le code suivant : 

```
    server {
        listen 80 default_server;
        listen [::]:80 default_server;

		server_name yourdomain.com www.yourdomain.com;
		location / {
			proxy_pass 'http://127.0.0.1:{port}';
			proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Port $server_port;
		}
	}
```


> ℹ️ Soyez certain(e) d'avoir remplacer la variable `{port}` par le port utilisé par votre application Node.js et `(www.)yourdomain.com` par votre nom de domaine. Nous pourrons ensuite installer un certificat SSL.

Lorsque vous installez **Nginx**, la taille maximale d'une requête prise en charge par Nginx est restreinte à 1MB par défaut. Au delà, le requête sera rejetée ou ignorée. 

Pour modifier ce paramètre, ouvrez le fichier de configuration `/etc/nginx/nginx.conf` et ajoutez la ligne suivante dans la section `http` si elle n'existe pas déjà :

`client_max_body_size 50M;`

⚠️ Vérifiez que la configuration globale soit conforme en exécutant `sudo nginx -t` puis redémarrez **Nginx** pour intégrer les modifications.

Utilisez `sudo service nginx restart` ou `sudo systemctl restart nginx.`

Lancez à présent votre application via **PM2** et essayez de joindre votre application via **HTTP** _(port 80 de votre machine)_.

> ℹ️ L'installation d'un **certificat SSL** via la librairie **Certbot** viendra compléter le fichier de configuration `/etc/nginx/sites-available/default` pour permettre l'utilisation et la redirection **HTTPS**.

Si vous vouliez par exemple créer des sous-domaines et utiliser plusieurs méthodes de Proxy Reverse, vous pourriez configurer votre fichier `/etc/nginx/sites-available/default` selon le principe suivant :

```
    # Domain | www.domain.com
    
    server {
        server_name domain.com www.domain.com;
        location / {
            proxy_pass 'http://hostname:{port}';
        }
    }
    
    # Sub-domain n°1 | subdomain1.domain.com
    
    server {
        server_name subdomain1.domain.com www.subdomain1.domain.com;
        location / {
            proxy_pass 'http://hostname:{port}';
        }
    }
    
    # Sub-domain n°2 | subdomain2.domain.com
    
    server {
        server_name subdomain2.domain.com www.subdomain2.domain.com;
        location / {
            proxy_pass 'http://hostname:{port}';
        }
    }
```

Pour garantir de bonnes conditions de maintenance et renforcer l'organisation structurelle de notre Reverse Proxy, il serait préférable de supprimer notre fichier `/etc/nginx/sites-available/default` et de créer un fichier pour chaque (sous-)domaine.

L'arborescence des (sous-)domaines serait alors plus simple à distinguer.

```
    # Domain | www.domain.com
    
    server {
        server_name domain.com www.domain.com;
        location / {
            proxy_pass 'http://hostname:{port}';
        }
    }
```

```
    # Sub-domain n°1 | subdomain1.domain.com
    
    server {
        server_name subdomain1.domain.com www.subdomain1.domain.com;
        location / {
            proxy_pass 'http://hostname:{port}';
        }
    }
```

Une fois les fichiers de configurations référents à vos différents (sous-)domaines créés dans le répertoire `/etc/nginx/sites-available`, créez un lien symbolique pour chacun de vos fichiers depuis ce répertoire, au répertoire `/etc/nginx/sites-enabled/` en utilisant : 

```
    cd ~
    sudo ln -s /etc/nginx/sites-available/domain.com /etc/nginx/sites-enabled/domain.com
    sudo ln -s /etc/nginx/sites-available/subdomain1.domain.com /etc/nginx/sites-enabled/subdomain1.domain.com
    ...
```

⚠️ Vérifiez que la configuration globale soit conforme en exécutant `sudo nginx -t` puis redémarrez **Nginx** pour intégrer les modifications.

Utilisez `sudo service nginx restart` ou `sudo systemctl restart nginx`.

---

## 🛡️ Activation du pare-feu serveur

Dès lorsque nos applications **Node.js** sont accessible via le port 80 qu'utilise notre serveur web Nginx, il est judicieux d'activer le pare-feu et ainsi n'autoriser que les protocoles ayant pour vocation d'être exploités sur notre machine tels que :

- SSH / SFTP (22)
- HTTP(S) (80 / 443)

Exécutez les commandes suivantes : 

```
    sudo ufw enable
	sudo ufw allow ssh
	sudo ufw allow http
	sudo ufw allow https
```

Utilisez la commande `sudo ufw status` pour obtenir une synthèse des réglementations.

```
    Status: active

	To                         Action      From
	--                         ------      ----
	22/tcp                     ALLOW       Anywhere                  
	80                         ALLOW       Anywhere                  
	443                        ALLOW       Anywhere                  
	22/tcp (v6)                ALLOW       Anywhere (v6)             
	80 (v6)                    ALLOW       Anywhere (v6)             
	443 (v6)                   ALLOW       Anywhere (v6)      
```

---

## 🌐 Configuration de la zone DNS d'un nom de domaine

C'est une étape primordiale lorsqu'une activitée prend naissance sur le web : la configuration de la zone DNS du nom de domaine.

La **zone DNS** d'un nom de domaine est un espace administratif gérée par une organisation ou un administrateur spécifique qui permet le contrôle sur les différentes entrées du nom de domaine.

Lorsque vous reçevez votre nouveau nom de domaine, la zone DNS de votre nom de domaine est souvent pré-configuré par votre hébergeur de telle sorte à ce que vous puissiez dors et déjà par exemple accéder au protocole **FTP** du serveur cible via la pré-configuration d'un enregistrement `CNAME` _(= ftp.domain.com)_.

> ℹ️ Retrouvez la [liste des différents enregistrements DNS](https://fr.wikipedia.org/wiki/Liste_des_enregistrements_DNS) existants

Exemple d'espace d'administration de la **zone DNS d'un nom de domaine** _(OVH)_ 👇

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z6ostq2h00168v1jyx6v.png)

Ci-dessous, un aperçu textuel de la configuration de la zone DNS de notre nom de domaine :

```
    $TTL 3600
    @	IN SOA dns200.anycast.me. tech.ovh.net. (2023020018 86400 3600 3600000 60)
                              IN NS     ns200.anycast.me.
                              IN NS     dns200.anycast.me.
                              IN MX     1 mx4.mail.ovh.net.
                              IN MX     10 mx3.mail.ovh.net.
                              IN A      164.132.50.110
                              IN TXT    "1|www.domain.com"
    _autodiscover._tcp        IN SRV    0 0 443 mailconfig.ovh.net.
    _imaps._tcp               IN SRV    0 0 993 ssl0.ovh.net.
    _submission._tcp          IN SRV    0 0 465 ssl0.ovh.net.
    subdomain1                IN CNAME  domain.com.
    autoconfig                IN CNAME  mailconfig.ovh.net.
    autodiscover              IN CNAME  mailconfig.ovh.net.
    ftp                       IN CNAME  domain.com.
    subdomain2                IN CNAME  domain.com.
    www                       IN A      164.132.50.110
    www                       IN TXT    "3|welcome"
```

La première action à entreprendre : **modifier l'adresse IPv4** cible de **l'enregistrement de Type A** en renseignant **l'adresse IPv4 de votre serveur distant**, pour les deux entrées existantes : `domain.com` _(Type A)_ et `www.domain.com` _(Type A)_.

Le nom de domaine sera dès lors reconnu par n'importe quel naviguateur et dirigera le client à votre serveur distant.

Pour créer un sous-domaine, créez une nouvelle entrée de Type `CNAME`, ciblez votre nom de domaine et spécifiez l'intitulé du sous-domaine à créer, puis validez.

> _ℹ️ Un délai de propagation implique parfois d'attendre jusqu'à 24 heures avant de garantir l'accessibilité à votre nouveau sous-domaine_.

Lorsque vous requêterez votre sous-domaine, **Nginx** sera capable d'interprêter l'origine de la requête et vous redirigera auprès de l'application cible _(selon votre configuration via proxy)_.

---

## 🔒 Mise en place d'un certificat SSL avec Certbot

Maintenant que notre nom de domaine nous redirige vers notre serveur distant, que le serveur web Nginx interprête l'origine de notre requête et nous redirige vers l'application cible ;

Etablissons une connexion sécurisée entre client/serveur grâce au protocole **HTTPS**.

L'utilisation du protocole **HTTPS** nécessite l'utilisation d'un certificat numérique permettant aux navigateurs de vérifier l'identité des serveurs web. Des tiers de confiance, soit des **autorités de certification (AC)** sont chargées de déléguer les certificats aux serveurs web.

Une librairie nommée « **Certbot** » récupère des certificats depuis l'autorité de certification **Let's Encrypt**, _(autorité de certification lancée à l'initiative de **Mozilla** notament)_. Nous l'utiliserons pour implémenter rapidement notre **certificat SSL** avec **Nginx**.

Installons **Certbot**, depuis notre machine distante :

`sudo apt install certbot python3-certbot-nginx`

Procédons à l'**installation de notre certificat SSL** en utilisant **Certbot** :

`sudo certbot --nginx -d domain.com -d www.domain.com`

```
    Saving debug log to '/var/log/letsencrypt/letsencrypt.log'
    Enter email address (used for urgent renewal and security notices)
     (Enter 'c' to cancel): your_email@domain.com
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please read the Terms of Service at
    https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
    agree in order to register with the ACME server. Do you agree?
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: y
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Would you be willing, once your first certificate is successfully issued, to
    share your email address with the Electronic Frontier Foundation, a founding
    partner of the Lets Encrypt project and the non-profit organization that
    develops Certbot? We would like to send you email about our work encrypting the web,
    EFF news, campaigns, and ways to support digital freedom.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: y
    Account registered.
    Requesting a certificate for 'domain.com' and 'www.domain.com'
    
    Successfully received certificate.
    Certificate is saved at: '/etc/letsencrypt/live/domain.com/fullchain.pem'
    Key is saved at:         '/etc/letsencrypt/live/domain.com/privkey.pem'
    This certificate expires on 2023-07-01.
    These files will be updated when the certificate renews.
    Certbot has set up a scheduled task to automatically renew this certificate in the background.
    
    Deploying certificate
    Successfully deployed certificate for 'domain.com to' '/etc/nginx/sites-enabled/default'
    Successfully deployed certificate for 'www.domain.com' to '/etc/nginx/sites-enabled/default'
    Congratulations! You have successfully enabled HTTPS on 'https://domain.com' and 'https://www.domain.com'
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    If you like Certbot, please consider supporting our work by:
     * Donating to ISRG / Lets Encrypt:   https://letsencrypt.org/donate
     * Donating to EFF:                    https://eff.org/donate-le
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```

Une règle a été créée auprès de votre serveur web pour rediriger le client du protocole **HTTP** au **HTTPS**. Une tâche pour renouveller le certificat a quant à elle été initialisée et sera renouvellée chaque 90 jours.

Vérifiez que celle-ci soit créée via `sudo systemctl status certbot.timer`

```
    ● certbot.timer - Run certbot twice daily
	Loaded: loaded ('/lib/systemd/system/certbot.timer'; enabled; vendor preset:>
	Active: active (waiting) since Sun 2023-04-02 10:50:45 UTC; 4min 58s ago
	Trigger: Sun 2023-04-02 15:19:19 UTC; 4h 23min left
	Triggers: ● certbot.service

	Apr 02 10:50:45 vps systemd[1]: Started Run certbot twice daily.
```
Un email de **Let's Encrypt** devrait vous parvenir avant l'expiration de votre certificat.

Utilisez `sudo certbot renew --dry-run` à l'avenir pour renouveller votre certificat.

> ℹ️ Vous pourrez remarquer que le fichier de configuration **Nginx** lié au nom de domaine que vous avez certifié à été modifié par **Certbot**. Notre fichier `/etc/nginx/sites-available/default` ou `/etc/nginx/sites-available/domain.com` a hérité de nouvelles propriétés pour permettre l'utilisation du protocole **HTTPS**.

Félicitations 🎉

Vous pouvez dorénavant profiter de votre application depuis le protocole **HTTPS**.