Quand j’ai publié Le guide ultime pour déployer une application Shiny sur AWS il y a presque dix ans, AWS était le choix évident.
Aujourd’hui ? J’évite à tout prix de l’utiliser.
J’ai arrêté AWS pour notre infra interne depuis 2019. Et je ne le recommande plus à la plupart de nos clients.
Pourquoi ?
AWS coûte cher et AWS est compliqué.
Dans ce guide, je vais vous montrer une alternative.
Vous pouvez héberger une application Shiny sur une VM pour 5 €/mois.
Vraiment.
Il vous faudra trois choses :
1. Quelques heures devant vous.
Combien ? Ça dépend surtout de votre aisance technique.
Si vous n’avez jamais touché à un serveur, il faudra un peu plus de temps.
Mais c’est accessible à tous.
Ce guide s’adresse autant aux débutants complets qu’aux profils plus avancés (qui pourront sauter les sections les plus basiques).
2. Cinq euros par mois.
C’est le prix de la VM qu’on va utiliser. Avec 4 Go de RAM.
La plupart des applis Shiny tourneront très bien là-dessus.
Et si vraiment vous avez besoin de plus, le prix augmente de façon linéaire avec la RAM, le calcul est rapide.
3. Une application Shiny.
Oui, c’est quand même le sujet de base.
Si vous n’avez pas encore d’appli, allez en coder une et revenez ici quand vous voudrez la mettre en ligne.
La plupart des applis qu’on accompagne chez Data Champ’ sont parfaitement adaptées à cette méthode de déploiement. La vôtre l’est sans doute aussi.
Mais, il y a des exceptions.
Voici quelques rares cas où ce guide ne sera pas adapté :
- Vous avez besoin de haute disponibilité sur plusieurs continents.
- Vous devez scaler automatiquement à des milliers de nœuds.
- Vous avez besoin de certifications SOC 2 ou HDS.
- Vous ne voulez pas gérer de serveurs vous-même.
Toutes ces raisons sont valables.
Et il reste un petit cas particulier : Si vous avez besoin d’une base de données type PostgreSQL ou MariaDB. Ce guide va vous aider pour 90% du chemin, mais derrière il vous faudra installer, configurer, et maintenir la base de données vous-même. On ne couvrira pas cette partie dans ce guide.
Prêt ? GO !
Commander la VM
Ça fait depuis 2019 que je travaille avec Hetzner et j’en suis très satisfait. Toute notre infra ainsi que certains de nos clients sont hébergés chez eux.
C’est donc eux que je vais utiliser comme exemple dans ce guide.
Mais il existe pléthore d’alternatives :
Il m’arrive d’ailleurs de travailler aussi avec OVHCloud.
Dans la suite, et surtout pour la partie de mise en service du serveur, je vais utiliser Hetzner.
La première étape est de commander votre VM (machine virtuelle)
C'est quoi une machine virtuelle ?
Un serveur dédié, c'est un ordinateur physique dans un datacenter, loué rien que pour vous.
Vous contrôlez tout : système d'exploitation, partitions, voire même le matériel.
Inconvénient : si le matériel tombe en panne, c'est à vous de le remarquer et de demander au support de le remplacer.
Une machine virtuelle (VM), c'est juste un « morceau » de ce serveur, découpé par un logiciel hyperviseur.
Vous choisissez combien de CPU, de RAM, et de disque vous voulez, et vous pouvez augmenter ou diminuer ces ressources en un clic.
En pratique, une VM offre presque la même liberté, coûte beaucoup moins cher, et vous évite les galères matérielles.
Comprendre l’offre Hetzner Cloud
Rendez-vous sur Hetzner Cloud et descendez un peu sur la page.
Vous allez devoir choisir le type de VM, c’est-à-dire le nombre de cœurs CPU, la RAM, le stockage, etc. Les offres sont classées en quatre catégories :
- Shared vCPU Intel : Les options les moins chères, mais avec des CPU partagés et moins performants.
- Shared vCPU AMD : Meilleurs CPU, mais toujours partagés.
- Shared vCPU Ampere : À éviter ici, on est sur une architecture ARM64 qui ne sera pas compatible avec ce qu’on veut faire.
- Dedicated vCPU : Les meilleurs CPU, non partagés, mais forcément un peu plus chers.
Mes recommandations
- Choisissez un datacenter proche de vous. En Europe, toutes les offres sont disponibles. Mais aux US ou en Asie, les serveurs Intel ne le sont pas.
- La RAM est le critère le plus important, et la performance du CPU vient en second temps. Le nombre de vCPU importe peu, puisque Shiny ne tournera que sur un seul cœur.
Vous pourrez toujours changer plus tard. C’est l’avantage des VM : vous pouvez passer d’un CPU Intel à un AMD à tout moment, tant que le modèle est dispo dans votre région. Seule la localisation ne peut pas être modifiée après coup.
Pour ce guide, je vais choisir l’offre CX22 : 4 Go de RAM, CPU Intel.
Une fois votre choix fait, cliquez sur Get started en bas du tableau.
Vous devrez créer un compte, puis vous arriverez sur cette page :
Vous voyez ici mes cinq projets.
Un projet, c’est comme un dossier : ça permet d’organiser vos VM (par exemple : un dossier par client).
Créez un projet, donnez-lui un nom, puis cliquez sur Create Resource / Servers.
Lancer et configurer votre VM
C’est ici que vous allez vraiment personnaliser votre machine.
Localisation
Je vous conseille de choisir un datacenter proche de vous… ou, encore mieux, proche de vos utilisateurs.
Pour ma part, je prends Falkenstein, en Allemagne.
Image système
L’image, c’est le système d’exploitation qui sera installé sur la VM.
Pas de MacOS ni de Windows ici : le monde des serveurs, c’est du Linux.
La plupart des distributions Linux se divisent en deux grandes familles :
- Celles basées sur Debian : Ubuntu et Debian
- Celles basées sur RHEL : Fedora, CentOS, Rocky Linux, AlmaLinux
- Et OpenSUSE joue dans sa propre cour, on va l’ignorer ici
Les deux familles fonctionnent très bien. C’est surtout une question d’habitude.
Perso, j’utilise Ubuntu Desktop sur mon laptop depuis des années. Donc je suis toujours plus à l’aise sur les distributions basées sur Debian.
C’est pourquoi on partira sur Debian dans ce guide.
Ensuite : Comment choisir entre Ubuntu et Debian ?
Ubuntu Server a des packages plus récents, une très grosse communauté, et l’éditeur Canonical propose du support commercial. C’est assez classique comme choix pour un serveur de production.
Debian, à l’inverse, essaie d’être plus minimaliste et favorise la stabilité des packages plutôt que de proposer les toutes dernières versions.
Bien que j’utilise Ubuntu Desktop sur mon laptop, je préfère Debian pour mes serveurs, simplement pour la stabilité. Il y a déjà suffisamment de galères possibles sur un serveur sans se rajouter des complications.
C’est donc ce que je recommande ici : Debian 12.
Notez que Debian 13 va sortir très prochainement, à l’été 2025. Prenez la version la plus récente disponible au moment où vous lisez ce guide.
Type de VM
C’est ici que vous choisissez le modèle sélectionné plus haut.
Attention à nouveau : tous les types de VM ne sont pas disponibles dans tous les datacenters.
Par exemple, la CX22 est dispo à Falkenstein, mais pas à Nuremberg (pourtant aussi en Allemagne).
Réseau
Activez IPv4 et IPv6.
Clés SSH
Si vous avez déjà utilisé Git ou Github, vous connaissez sûrement les clés SSH.
Une clé SSH, c’est une sorte de passe d’accès pour se connecter à distance à un serveur, de façon sécurisée, en utilisant le protocole SSH.
Et c’est quoi ce protocole SSH ?
C’est un protocole, au même titre que HTTP est un protocole qui permet à un navigateur et un serveur de communiquer avec un langage commun. Chaque protocole va être spécialisé dans un type de communication.
Et SSH, c’est utilisé pour la communication entre des utilisateurs et des serveurs. Vous allez utiliser SSH pour ouvrir un terminal sur le serveur distant et taper des commandes.
Une paire de clés SSH, c’est :
- une clé publique (à donner au serveur, ou à Github)
- une clé privée (à garder précieusement sur votre machine)
Quand vous ouvrez une connexion SSH, le protocole fait tourner un algorithme qui permet de vérifier qui vous êtes, si vous avez bien accès, et ensuite toute la communication est chiffrée.
C’est bien mieux d’utiliser une clé SSH plutôt qu’un mot de passe : Beaucoup plus dur à deviner, et quasi-impossible à brute-forcer.
Pour générer une clé SSH, je vous recommande cet article de la documentation Github :
Génération d’une nouvelle clé SSH et ajout de celle-ci à ssh-agent
Ou bien suivez ces instructions :
- Sur Windows, commencez par installer Git Bash depuis https://git-scm.com/downloads. Pendant l’installation, gardez les options par défaut.
- Toujours sur Windows : Ouvrir Git Bash.
- Sur Linux ou MacOS, ouvrir un terminal.
- Tapez la commande suivante :
ssh-keygen -t ed25519 -C "name@host"
- Pas besoin de passphrase et gardez le nom de fichier par défaut.
Vous obtiendrez deux fichiers :
~/.ssh/id_ed25519
: C’est la clé privée~/.ssh/id_ed25519.pub
: C’est la clé publique (comme l’extension l’indique)
Le symbole ~
indique le dossier utilisateur. Sur Windows, ça va être C:\Users\charles
, et sur Unix ça sera plutôt /home/charles
.
Récupérez la valeur de la clé publique, et ajoutez-la dans l’interface Hetzner.
Volumes
Les volumes servent à stocker beaucoup de données (jusqu’à 10 To).
On n’en aura pas besoin ici.
Firewalls
Pas besoin de pare-feu côté Hetzner pour l’instant :
- On ne va pas exposer des service sensible tout de suite.
- On pourra configurer un pare-feu directement sur la VM.
Cependant :
- C’est toujours utile d’en avoir un au cas où on fait une erreur de configuration d’un service. C’est le principe d’avoir plusieurs couches de sécurité.
- Certes on peut configurer le pare-feu directement sur la VM… jusqu’au moment où on fait l’erreur bête de s’enfermer dehors.
Cette dernière possibilité arrive… parfois.
Dans ce cas c’est toujours utile d’avoir accès au pare-feu depuis l’extérieur de la VM.
On y reviendra plus tard.
Backups
Si vous créez un serveur de production : Activez les backups.
Le prix du serveur va être augmenté de 20%, et vous aurez des backups automatiques, journaliers, sur 7 jours glissants.
Si vous suivez ce guide juste pour l’exercice, vous pouvez laisser la case décochée.
Les trois sections suivantes : Placement groups, Labels et Cloud config ne sont pas utiles pour nous.
Passez directement à la dernière section.
Name
Donnez un nom descriptif à votre VM.
Si vous voulez créer une VM qui va héberger toutes vos applis, vous pouvez simplement l’appeler shiny
.
Si vous imaginez plus tard avoir plein de VMs avec plein d’applis Shiny, utilisez plutôt shiny1
.
Si vous êtes prêt, alors cliquez sur Create & Buy now.
Quelques secondes plus tard… votre serveur est prêt !
Notez bien votre adresse IP. Vous allez en avoir besoin très bientôt.
Comment se connecter à la VM
Vous vous souvenez de la section sur les clés SSH ?
Ouvrez un terminal (ou Git Bash si vous êtes sous Windows), et tapez :
ssh [email protected]
N’oubliez pas de remplacer l’adresse IP par celle de votre VM.
Lors de la première connexion, vous verrez un message du genre :
The authenticity of host '23.88.114.164 (23.88.114.164)' can't be established.
ED25519 key fingerprint is SHA256:krzeRLL+lBvGE7OEG/0nWCJy47L4K6ADQGDRy/zJ2WE.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
En pratique, vous n’avez pas de moyen de vérifier que vous parlez bien à la bonne machine (et pas à un pirate qui intercepte la connexion). Mais pour un serveur fraîchement créé, vous pouvez répondre « yes ».
Une fois connecté, vous voyez le message d’accueil :
Linux shiny 6.1.0-33-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.133-1 (2025-04-10) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@shiny:~#
Le message vous informe de la version du noyau Linux (6.1.0-33-amd64
), et que votre système contient uniquement des logiciels libres.
Bravo, vous avez fait le plus dur !
Sécuriser votre VM
Prêt à tout casser ?
Pas si vite, on se calme. On va déjà sécuriser le serveur.
Sur votre terminal, vous voyez ça :
root@shiny:~#
C’est le prompt. Il contient beaucoup d’informations :
root
: c’est l’utilisateur super-administrateur, qui a tous les droits (et donc tous les risques).@
est un séparateur entre le nom d’utilisateur et le nom de la machine. C’est comme dire « Je suis Michel de Nantes » sauf que là ça dit « Je suisroot
deshiny
».shiny
est la machine sur laquelle vous êtes. Le nom a été créé depuis le formulaire sur le site de Hetzner.:
est un séparateur~
est le répertoire courant, ça vous dit où vous êtes sur la machine.#
est un symbole. C’est#
quand vous êtesroot
, et$
quand vous êtes un simple utilisateur.
Vous pouvez désormais taper quelques commandes pour vous familiariser :
ls
liste les fichiers du dossier courant (ls
pour “list”)pwd
affiche le chemin du dossier courant (pwd
pour “print working directory”)touch test
crée un fichier vide nommétest
rm test
supprime ce fichier
À présent, tapez la commande suivante :
journalctl -u ssh
Voici ce que j’obtiens :
mai 26 15:52:38 shiny sshd[943]: Invalid user guest from 193.24.211.22 port 36651
mai 26 15:52:38 shiny sshd[943]: pam_unix(sshd:auth): check pass; user unknown
mai 26 15:52:38 shiny sshd[943]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=193.24.211.22
mai 26 15:52:40 shiny sshd[943]: Failed password for invalid user guest from 193.24.211.22 port 36651 ssh2
mai 26 15:52:40 shiny sshd[943]: Received disconnect from 193.24.211.22 port 36651:11: Client disconnecting normally [preauth]
mai 26 15:52:40 shiny sshd[943]: Disconnected from invalid user guest 193.24.211.22 port 36651 [preauth]
mai 26 15:53:03 shiny sshd[949]: Invalid user ubuntu from 92.118.39.66 port 59898
mai 26 15:53:03 shiny sshd[949]: pam_unix(sshd:auth): check pass; user unknown
mai 26 15:53:03 shiny sshd[949]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=92.118.39.66
mai 26 15:53:05 shiny sshd[949]: Failed password for invalid user ubuntu from 92.118.39.66 port 59898 ssh2
mai 26 15:53:06 shiny sshd[949]: Connection closed by invalid user ubuntu 92.118.39.66 port 59898 [preauth]
mai 26 15:56:07 shiny sshd[953]: error: kex_exchange_identification: Connection closed by remote host
mai 26 15:56:07 shiny sshd[953]: Connection closed by 2.57.122.57 port 39102
C’est quoi ça ?
Ce sont des tentatives de connexion. La première venait d’un utilisateur guest
, tandis que la seconde venait d’un utilisateur ubuntu
.
Vous allez sans doute voir quelque chose de différent. Peut-être même rien du tout, si vous avez été rapide. Mais attendez une ou deux minutes, ils vont arriver. Après la création de la VM, j’ai eu 60 tentatives juste sur la première heure.
D’où viennent ces tentatives ?
Il y a des bots dont le seul job est de taper à la porte de toutes les adresses IPv4 et d’essayer de rentrer dans le serveur. Avec seulement 4 milliards d’adresses, ça prend à peine une heure pour faire tout le tour.
Pas cool. Il faut verrouiller tout ça. On va mettre en place les étapes suivantes :
- Créer un simple utilisateur pour éviter d’utiliser
root
- Sécuriser la configuration SSH
- Installer
fail2ban
pour bloquer les attaques en brute-force - Configurer
unattended-upgrades
pour installer les mises à jour de sécurité automatiquement - Mettre en place un pare-feu
Créer un utilisateur non-root
L’objectif ici est de réduire le risque d’avoir le serveur compromis en utilisant l’utilisateur root
.
Oui c’est pratique de manipuler le super-administrateur. Mais c’est aussi risqué.
On applique le principe de moindre privilège : On n’utilise jamais root
pour les tâches courantes. On crée un utilisateur normal, qu’on autorise à devenir admin uniquement quand c’est nécessaire.
Créez cet utilisateur :
adduser charles
Et ajoutez-le au groupe sudo
:
usermod -aG sudo charles
Ce groupe va donner la possibilité à l’utilisateur charles
de lancer des commandes en tant qu’admin, mais seulement lorsqu’elles sont préfixées par sudo
.
Par exemple, si je lance (en tant que charles
) la commande sudo apt update
, ça marche. Mais si je lance apt update
, ça va échouer, parce que c’est une commande d’admin.
Ensuite, on veut pouvoir se connecter en SSH en tant que charles
. Pour ça, copiez la configuration SSH de root
directement dans les dossiers de charles
:
cp -r /root/.ssh /home/charles/.
chown -R charles:charles /home/charles/.ssh # This line ensures the files belong to charles
Étapes de vérifications :
- Fermez puis rouvrez votre terminal.
- Connectez-vous avec
ssh [email protected]
(utilisez votre utilisateur et votre adresse IP) - Testez une commande d’admin :
sudo journalctl -u ssh
À partir de maintenant, vous n’utiliserez plus root
directement. Toutes les commandes sensibles peuvent être lancées en préfixant sudo
.
Durcir la configuration SSH
Par défaut, SSH autorise la connexion directe en root
et l’authentification par mot de passe. Deux potentielles faiblesses.
1. Interdire la connexion SSH directe en root
On empêche toute connexion SSH en root :
echo "PermitRootLogin no" | sudo tee -a /etc/ssh/sshd_config.d/50-custom.conf
2. Désactiver l’authentification par mot de passe
On force l’utilisation des clés SSH plutôt que des mots de passe :
echo "PasswordAuthentication no" | sudo tee -a /etc/ssh/sshd_config.d/50-custom.conf
À présent…
Attendez.
Vous êtes bien SÛR que vous pouvez vous connecter en SSH avec votre utilisateur ? Vous avez testé ?
Parce que là avec les changements il ne sera plus possible de se connecter en tant que root
. Évitez de vous enfermer dehors.
3. Redémarrer le service SSH
Pour appliquer les changements :
sudo systemctl restart sshd
Bloquer les attaques par brute force
Bon, là on commence à être bien. Mais même avec l’accès root désactivé, des robots vont continuer à tenter de deviner vos identifiants.
Pour les calmer, on installe fail2ban
: il surveille les tentatives ratées et bannit automatiquement les IP suspectes.
Installer fail2ban :
sudo apt update
sudo apt install fail2ban
Vérifier le statut :
sudo systemctl status fail2ban
Euh… ça ne marche pas chez moi :
× fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Mon 2025-05-26 16:48:53 UTC; 30s ago
Duration: 150ms
Docs: man:fail2ban(1)
Process: 2751 ExecStart=/usr/bin/fail2ban-server -xf start (code=exited, status=255/EXCEPTION)
Main PID: 2751 (code=exited, status=255/EXCEPTION)
CPU: 137ms
mai 26 16:48:53 shiny systemd[1]: Started fail2ban.service - Fail2Ban Service.
mai 26 16:48:53 shiny fail2ban-server[2751]: 2025-05-26 16:48:53,323 fail2ban.configreader [2751]: WARNING 'allowipv6' not defined in 'Definition'. Using default one: 'auto'
mai 26 16:48:53 shiny fail2ban-server[2751]: 2025-05-26 16:48:53,335 fail2ban [2751]: ERROR Failed during configuration: Have not found any log file for sshd jail
mai 26 16:48:53 shiny fail2ban-server[2751]: 2025-05-26 16:48:53,341 fail2ban [2751]: ERROR Async configuration of server failed
mai 26 16:48:53 shiny systemd[1]: fail2ban.service: Main process exited, code=exited, status=255/EXCEPTION
mai 26 16:48:53 shiny systemd[1]: fail2ban.service: Failed with result 'exit-code'.
Alors, je vois un warning sur IPv6. On peut l’ignorer.
Il y a aussi un vrai message d’erreur : Have not found any log file for sshd jail.
Ah.
La jail, c’est un fichier de configuration utilisé par fail2ban
pour détecter les attaques par brute force et bannir les adresses IP. Et en général, il regarde des fichiers de logs.
Sauf qu’au fil du temps, les logs sont de moins en moins écrits dans des fichiers, et sont remplacés par la commande journalctl
. Et la configuration par défaut de fail2ban
ne s’est pas mise à jour.
Et… ne se mettra probablement jamais à jour d’après cette issue Github : Fail2Ban not starting when enabling sshd (systemd backend missed)
Bon, on va le faire nous-même. Ouvrez le fichier avec un éditeur de texte :
sudo nano /etc/fail2ban/jail.local
Là je vous recommande nano
qui est un éditeur de texte “simple” dans le terminal. Sinon vous avez aussi vim
si vous êtes motivé (bon courage).
Collez cette configuration :
[DEFAULT]
bantime = 10m
findtime = 10m
maxretry = 5
[sshd]
enabled = true
backend = systemd
port = ssh
Pour enregistrer :
- Ctrl + X pour quitter.
- Il va demander : “Save modified buffer?”. Répondez en tapant Y
- Il va demander : “File Name to Write: /etc/fail2ban/jail.local”. Répondez avec Entrée.
Vérifiez que tout s’est bien passé avec cat /etc/fail2ban/jail.local
.
Ensuite, installez la dépendance suivante dont fail2ban
aura besoin :
sudo apt install python3-systemd
À présent, redémarrez le service fail2ban
, et cette fois-ci ça devrait bien fonctionner :
sudo systemctl restart fail2ban
sudo systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; preset: enabled)
Active: active (running) since Mon 2025-05-26 17:04:36 UTC; 1min 0s ago
Docs: man:fail2ban(1)
Main PID: 3830 (fail2ban-server)
Tasks: 5 (limit: 4531)
Memory: 16.8M
CPU: 350ms
CGroup: /system.slice/fail2ban.service
└─3830 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
mai 26 17:04:36 shiny systemd[1]: Started fail2ban.service - Fail2Ban Service.
mai 26 17:04:37 shiny fail2ban-server[3830]: 2025-05-26 17:04:37,043 fail2ban.configreader [3830]: WARNING 'allowipv6' not defined in 'Definition'. Using default one: 'auto'
mai 26 17:04:37 shiny fail2ban-server[3830]: Server ready
Configurer les mises à jour de sécurité automatiques
Alors, j’ai longuement réfléchi avant d’inclure cette section.
Parce que quand je le fais pour des clients, je reviens quelques mois plus tard, et je vois ça :
130 updates can be installed immediately.
0 of these updates are security updates.
To see these additional updates run: apt list --upgradable
*** System restart required ***
Eux ils pensent que tout est bon puisqu’il y a un outil qui installe toutes les mises à jour automatiquement.
Non.
Vous DEVEZ vous-même installer certaines mises à jour manuellement, comme les mises à jour du noyau et les changements majeurs.
L’outil que je vous propose ici ne concerne que les mises à jour de sécurité. C’est pratique. Et facile à mettre en place :
sudo apt install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
Pour faire une mise à jour manuelle :
sudo apt update
sudo apt upgrade
Dans certains cas, notamment les mises à jour du noyau Linux, vous aurez besoin de redémarrer le serveur. Pour redémarrer, c’est très simple : sudo reboot
.
Configurer un pare-feu
Dans les années 2000, tout le monde avait un pare-feu sur son ordinateur. Aujourd’hui, on a presque oublié à quoi ça servait.
Votre ordinateur est connecté à Internet comme n’importe quelle autre machine. C’est ce qui vous permet d’envoyer des requêtes à des serveurs, et de recevoir en retour les pages web que vous consultez.
Mais, au fond, quelle est la différence entre votre ordinateur (le “client”) et un serveur ?
On les appelle client et serveur selon leur rôle, mais techniquement, ce sont juste deux machines reliées entre elles par le grand réseau qu’est Internet.
Donc, si vous pouvez envoyer des requêtes à une autre machine… n’importe qui peut aussi en envoyer à la vôtre.
C’est pour ça qu’on avait besoin d’un pare-feu : pour bloquer les connexions entrantes non sollicitées.
Puis, avec la pénurie d’adresses IPv4, on a mis des routeurs devant nos ordinateurs personnels : ce sont eux qui nous protègent des connexions extérieures.
Mais pour un serveur, ce n’est pas le cas.
Un serveur doit être accessible depuis Internet. Sinon, ça n’a aucun intérêt.
Mais du coup, les utilisateurs malveillants peuvent y accéder aussi.
Le pare-feu va servir à n’exposer que les services nécessaires, et rien de plus. C’est une façon de réduire la surface d’attaque.
On peut le configurer à deux niveaux :
- Bloquer toutes les connexions entrantes sauf certains ports. Ici on va autoriser seulement SSH, HTTP, et HTTPS.
- Bloquer toutes les connexions entrantes sauf certaines adresses IP. Par exemple, n’autoriser SSH que depuis votre propre IP.
Petit rappel : fail2ban
travaille en direct avec le pare-feu. À chaque fois qu’il bannit un utilisateur malveillant, il ajoute une règle au pare-feu pour bloquer l’IP concernée.
Passons à la pratique.
Uncomplicated Firewall (UFW)
Pour configurer facilement un pare-feu sous Linux, on utilise ufw
(“Uncomplicated Firewall”).
Installation :
sudo apt install ufw
On va autoriser seulement les ports essentiels : SSH, HTTP, et HTTPS :
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
Puis on active le pare-feu :
sudo ufw enable
Un message d’avertissement va s’afficher :
Command may disrupt existing ssh connections. Proceed with operation (y|n)?
Traduction : activer le pare-feu pourrait vous couper l’accès SSH si la règle n’est pas bien configurée. Ici, on a bien autorisé SSH, donc pas de risque, mais soyez toujours prudent quand vous touchez aux règles du pare-feu.
C’est facile de se retrouver bloqué dehors.
Si jamais ça arrive :
- Restaurez une sauvegarde.
- Redémarrez le serveur en “rescue mode”, montez le disque principal avec
chroot
, et corrigez les règles (bon courage).
Sinon… vous pouvez aussi gérer un autre pare-feu directement depuis l’interface Hetzner.
Pare-feu Hetzner
Retournez dans votre compte Hetzner et ouvrez le menu Firewalls dans la barre latérale.
Ajoutez les règles entrantes suivantes :
- Protocole ICMP : laissez activé (utile pour le ping).
- Protocole TCP, port 22 : pour SSH.
- Protocole TCP, port 80 : pour HTTP.
- Protocole TCP, port 443 : pour HTTPS.
Pour les règles sortantes (celles qui s’appliquent aux connexions initiées par le serveur), laissez vide pour tout autoriser.
Appliquez ce pare-feu à votre serveur, donnez-lui un nom, puis validez.
Utiliser le pare-feu Hetzner n’apporte pas de sécurité supplémentaire par rapport à ufw
. L’avantage, c’est que vous pouvez l’administrer à distance, même si vous vous êtes bloqué côté système : impossible de vous retrouver définitivement enfermé dehors.
Voilà, votre serveur est maintenant sécurisé ! On peut passer à l’installation de Shiny Server.
Installation de R et Shiny Server
Installation de R
Installer R sur Debian a longtemps été un vrai casse-tête.
Debian est réputée pour sa stabilité : elle privilégie les versions éprouvées des logiciels, quitte à ne proposer que des versions un peu anciennes. Par exemple :
- Debian 10 (2019) venait avec R 3.5.2
- Debian 11 (2021) venait avec R 4.0.4
- Debian 12 (2023) venait avec R 4.2.2
Debian 13 sortira à l’été 2025, probablement avec R 4.4.3. Au vu de l’historique, et de cette volonté de rester stable, il est très improbable qu’on y trouve R 4.5.0 , qui est encore trop récent pas assez testé.
Vous pouvez aller voir l’article Quelle version de R faut-il utiliser en production ? pour comprendre pourquoi utiliser R 4.5.0 en production aujourd’hui (été 2025) n’est pas une bonne idée.
Tout ça pour dire que vous ne pouvez pas vraiment choisir votre version de R.
Et en plus, avec ce système, vous ne pouvez pas avoir plusieurs versions de R sur le même système.
Heureusement, ça a changé.
Il y a une petite entreprise qui s’appelle Posit qui propose désormais des binaires pour chaque version de R et chaque distribution Linux.
Il suffit d’aller sur Install R (lien) et de suivre les instructions.
Passez le début sur RHEL 9 ou RHEL 8. Ça ne nous concerne pas avec Debian. Plus bas dans la page :
- Choisissez votre version de R.
- Téléchargez la version de R souhaitée. Utilisez bien les commandes de l’onglet Debian.
- Vérifiez que l’installation de R s’est bien passée.
- Créez un raccourci vers R.
Je remets les commandes ici, mais référez-vous au site de Posit pour avoir la version la plus récente :
# Specify R version
export R_VERSION=4.4.3
# Download and install R
curl -O https://cdn.posit.co/r/debian-12/pkgs/r-${R_VERSION}_1_$(dpkg --print-architecture).deb
sudo apt-get update
sudo apt-get install ./r-${R_VERSION}_1_$(dpkg --print-architecture).deb
# Verify R installation
/opt/R/${R_VERSION}/bin/R --version
# Create a symlink to R
sudo ln -s /opt/R/${R_VERSION}/bin/R /usr/local/bin/R
sudo ln -s /opt/R/${R_VERSION}/bin/Rscript /usr/local/bin/Rscript
Maintenant, quand vous tapez R
dans le terminal, ça ouvre la console R.
Pour fermer la console, utilisez la fonction q()
(ou utilisez le raccourci Ctrl + D).
charles@shiny:~$ R
R version 4.4.3 (2025-02-28) -- "Trophy Case"
Copyright (C) 2025 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
Natural language support but running in an English locale
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.
> 1+1
[1] 2
> q()
Save workspace image? [y/n/c]: n
Tiens, pendant que vous y êtes, installons le package shiny
. On va l’installer en tant qu’admin pour qu’il soit disponible sur le système entier :
sudo R
Et installez shiny
:
install.packages("shiny")
Ça va prendre quelques minutes, et vous serez bon.
Installer Shiny Server sur Debian
Shiny Server existe en deux versions :
- Shiny Server Open Source : logiciel libre et gratuit en licence AGPLv3.
- Shiny Server Pro, qui n’est plus commercialisé par Posit. Ça a été remplacé par Posit Connect.
Posit Connect est légèrement hors budget pour notre objectif de 5 €/mois, donc on va plutôt partir pour la version open source.
Pour l’installation, je vous invite à consulter la documentation officielle.
En haut de la page, choisissez Ubuntu puis la dernière version disponible (au moment d’écrire ces lignes : Ubuntu 18.04+).
Pourquoi Ubuntu ?
Il n’y a tout simplement pas d’option Debian, et Ubuntu et Debian partagent de toute manière la même base : ce qui fonctionne sur l’un fonctionne généralement sur l’autre.
Ignorez l’étape 2 (installation du package shiny
) : on l’a déjà fait.
Étape 3, téléchargez et installez le binaire :
wget https://download3.rstudio.org/ubuntu-20.04/x86_64/shiny-server-1.5.23.1030-amd64.deb
sudo apt install ./shiny-server-1.5.23.1030-amd64.deb
Pensez à supprimer le fichier après installation pour garder un système propre :
rm https://download3.rstudio.org/ubuntu-20.04/x86_64/shiny-server-1.5.23.1030-amd64.deb
À ce stade, Shiny Server est installé et lancé automatiquement. Il y a même une application de démo qui vient avec.
Comment y accéder ?
Shiny Server fonctionne comme un service qui écoute sur un port.
Un port, c’est comme une porte vers l’extérieur, à travers laquelle les requêtes vont passer. On ne peut avoir qu’un seul service derrière un port. Le service écoute, patiemment, les requêtes qui entrent. Lorsqu’une requête est reçue, il la traite et renvoie une réponse.
Shiny Server écoute sur le port 3838.
Vous pouvez donc tester l’application de démo en tapant {ip_address}:3838
sur votre navigateur. Par exemple : 23.88.114.164:3838
.
On patiente…
On patiente…
TIMEOUT.
Pourquoi ?
Parce qu’en configurant le pare-feu, on a bloqué tous les ports sauf 22 (SSH), 80 (HTTP), et 443 (HTTPS).
Le port 3838 est donc inaccessible. C’est pour ça qu’on ne reçoit pas de réponse.
C’est une bonne chose. Plus tard dans le guide, on va configurer un serveur web (nginx) qui servira d’intermédiaire, pour des raisons de sécurité. Aucune requête n’atteindra le Shiny Server sans passer par le serveur web.
Si on ne bloquait pas le port 3838, alors des utilisateurs pourraient contourner ce serveur web et atteindre directement le Shiny Server. Ce qui n’est pas souhaitable.
Si vous voulez tester, ouvrez temporairement le port 3838 dans le pare-feu, mais pensez à le refermer ensuite.
Voici à quoi ressemble l’application de démo :
Il y a quelques erreurs en bas à droite de la page. C’est normal, il manque encore des packages R. On s’en occupera plus tard.
Configurer Shiny Server
Lors de l’installation de Shiny Server, une configuration par défaut s’est installée dans /etc/shiny-server/shiny-server.conf
. Pour l’afficher :
cat /etc/shiny-server/shiny-server.conf
Vous devriez voir quelque chose comme ça :
# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;
# Define a server that listens on port 3838
server {
listen 3838;
# Define a location at the base URL
location / {
# Host the directory of Shiny Apps stored in this directory
site_dir /srv/shiny-server;
# Log all Shiny output to files in this directory
log_dir /var/log/shiny-server;
# When a user visits the base URL rather than a particular application,
# an index of the applications available in this directory will be shown.
directory_index on;
}
}
Quelques points à noter :
- Le service tourne sous l’utilisateur
shiny
, qui a été créé automatiquement à l’installation. C’est très bien : pas de droits admin inutiles. - Il écoute sur le port 3838.
- Le code doit être placé dans
/srv/shiny-server
. - Les logs sont stockés dans
/var/log/shiny-server
. - Un index des applications est affiché à la racine.
On va modifier trois choses :
1. Ajouter preserve_logs true;
pour conserver tous les logs. Par défaut, les logs sans erreur sont auto-supprimés. Sauf qu’en pratique, j’ai déjà eu des cas où mes fichiers de logs disparaissent alors qu’il y a des bugs. Donc maintenant je préfère tous les garder, et je nettoie de temps en temps.
2. Changer listen 3838;
en listen 3838 127.0.0.1;
pour que Shiny Server n’accepte que les connexions locales qui viennent de la même machine.
Pourquoi ? À nouveau parce que c’est le serveur web qui recevra les connexions provenant de l’extérieur. Ensuite, ce serveur web atteindra Shiny Server, donc les requêtes proviendront de la même machine.
Ce changement de configuration est redondant avec le pare-feu, puisque de toute façon on a interdit aux requêtes extérieures d’atteindre le port 3838. Mais c’est toujours bon d’avoir de la redondance en sécurité.
3. Désactiver l’index des applications avec directory_index off
. En général on ne souhaite pas forcément que toutes les applications déployées soient affichées sur une page.
Voici la configuration modifiée :
# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;
preserve_logs true;
# Define a server that listens on port 3838
server {
listen 3838 127.0.0.1;
# Define a location at the base URL
location / {
# Host the directory of Shiny Apps stored in this directory
site_dir /srv/shiny-server;
# Log all Shiny output to files in this directory
log_dir /var/log/shiny-server;
# When a user visits the base URL rather than a particular application,
# an index of the applications available in this directory will be shown.
directory_index off;
}
}
Ouvrez le fichier avec sudo nano /etc/shiny-server/shiny-server.conf
, et mettez-le à jour. Ensuite, redémarrez le service Shiny Server :
sudo systemctl restart shiny-server
Il nous reste quelques réglages à faire.
Déjà, supprimez l’application par défaut :
sudo rm -R /srv/shiny-server/*
Puis changez le propriétaire du dossier /srv/shiny-server
pour l’attribuer à l’utilisateur shiny
:
sudo chown shiny:shiny /srv/shiny-server/
Enfin, passez le shell de shiny
à bash
, ça sera plus pratique pour plus tard :
sudo usermod -s /bin/bash shiny
Préparer l’environnement de production
L’étape suivante consiste à transférer le code de votre application Shiny sur le serveur. On va :
- Installer les dépendances système nécessaires
- Travailler avec l’utilisateur
shiny
- Télécharger le code
- Installer les packages R
Installer les dépendances système nécessaires
Comme mon code est hébergé sur Gitlab, je vais avoir besoin de Git pour cloner le dépôt :
sudo apt install git
Ensuite, faisons un test : essayez d’installer le package R xml2
.
sudo R
install.packages("xml2")
Vous devriez obtenir l’erreur suivante :
* installing *source* package ‘xml2’ ...
** package ‘xml2’ successfully unpacked and MD5 sums checked
** using staged installation
Using PKG_CFLAGS=
Using PKG_LIBS=-lxml2
------------------------- ANTICONF ERROR ---------------------------
Configuration failed because libxml-2.0 was not found. Try installing:
* deb: libxml2-dev (Debian, Ubuntu, etc)
* rpm: libxml2-devel (Fedora, CentOS, RHEL)
* csw: libxml2_dev (Solaris)
If libxml-2.0 is already installed, check that 'pkg-config' is in your
PATH and PKG_CONFIG_PATH contains a libxml-2.0.pc file. If pkg-config
is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:
R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'
-------------------------- [ERROR MESSAGE] ---------------------------
<stdin>:1:10: fatal error: libxml/tree.h: No such file or directory
compilation terminated.
--------------------------------------------------------------------
ERROR: configuration failed for package ‘xml2’
* removing ‘/opt/R/4.4.3/lib/R/library/xml2’
Quand vous installez un package sur Windows, c’est rapide parce qu’ils sont déjà compilés sous forme de binaires.
Mais pour Linux, le CRAN ne fournit pas de binaires. On télécharge le code source des packages, puis la compilation est réalisée à la volée. Non seulement ça prend plus de temps, mais en plus il faut des bibliothèques système supplémentaires.
Dans ce cas, le message d’erreur nous indique qu’il manque la dépendance libxml2-dev
. Le nom exact de la librairie dépend selon qu’on soit sur Debian, RHEL, ou autre. C’est ce qu’on voit ici :
Configuration failed because libxml-2.0 was not found. Try installing:
* deb: libxml2-dev (Debian, Ubuntu, etc)
* rpm: libxml2-devel (Fedora, CentOS, RHEL)
* csw: libxml2_dev (Solaris)
Essayez d’installer celle pour Debian :
sudo apt install libxml2-dev
Puis retentez l’installation du package xml2
:
install.packages("xml2")
Après quelques étapes de compilation, ça devrait marcher.
Je vous montre cet exemple parce que vous aurez à installer certaines dépendances système selon les packages R utilisés par votre application.
Faut-il installer toutes les dépendances système à l’avance ?
Je vous le déconseille fortement.
Déjà, ça ne va pas vous faire gagner de temps.
Et d’autre part, plus vous installez de choses, plus vous augmentez la surface d’attaque de votre serveur.
La règle est simple : On installe seulement le strict nécessaire.
Utiliser l’utilisateur shiny
On l’a vu : Shiny Server crée automatiquement un utilisateur shiny
dédié. C’est bien :
- Un service ne doit jamais tourner sous
root
. Si le service est compromis, tout le serveur l’est. - L’utilisateur
shiny
n’a aucun privilège admin. Si un attaquant prend la main dessus, il ne pourra agir que sur Shiny Server, pas sur l’ensemble du système.
Jusqu’ici, vous avez travaillé avec votre propre utilisateur. On va maintenant basculer sur shiny
:
sudo su - shiny
Votre nouveau prompt doit ressembler à ça :
shiny@shiny:~$
On est l’utilisateur shiny
dans la machine shiny
dans notre répertoire utilisateur.
Si vous ne voyez que $
, c’est que le shell de shiny
n’a pas été correctement configuré à l’étape précédente.
N’oubliez pas que l’utilisateur shiny
n’a pas de droits admin. Impossible donc de lancer des commandes administrateur : ce qui n’est pas un problème, puisqu’on n’en aura pas besoin pour la suite.
Télécharger votre code
Je vous recommande fortement de stocker votre code de production sur un serveur Git : Github, Gitlab, ou autre.
Dans mon cas, le code est ici :
https://gitlab.datachamp.fr/charles/shiny-tiny-hackathon
Si vous n’avez pas encore d’application Shiny, vous pouvez utiliser la mienne pour tester.
Placez-vous dans le dossier /srv/shiny-server
et clonez le dépôt :
cd /srv/shiny-server
git clone https://gitlab.datachamp.fr/charles/shiny-tiny-hackathon.git
Installer les packages R
Entrez dans le dossier de votre application :
cd shiny-tiny-hackathon
Dans mon projet, j’utilise renv
, ce qui me permet de restaurer automatiquement les packages R dans les bonnes versions.
Si vous n’utilisez pas renv
, installez vos packages comme vous en avez l’habitude (script, installation manuelle, etc.).
Ce que j’apprécie particulièrement à propos de renv
, c’est qu’il va automatiquement m’indiquer les dépendances système manquantes (fonctionnalité récente de la version 1.1.2) :
> renv::restore()
The following required system packages are not installed:
- libfontconfig1-dev [required by systemfonts]
- libpng-dev [required by ggiraph]
- pandoc [required by knitr, rmarkdown]
The R packages depending on these system packages may fail to install.
An administrator can install these packages with:
- sudo apt install libfontconfig1-dev libpng-dev pandoc
Autre atout : il utilise le RSPM pour installer les packages, ce qui accélère de beaucoup le process d’installation.
RStudio Public Package Manager
Le RSPM>, qu'on appelle maintenant le P3M pour Posit Public Package Manager, est une plateforme similaire au CRAN à partir de laquelle on peut installer des packages R.
Je vous ai dit plus tôt que le CRAN ne fournissait pas de binaires pour Linux.
Eh bien, le P3M en fournit. Ça veut dire qu'il n'est pas nécessaire d'installer les packages en compilant le code source, et donc l'installation est beaucoup plus rapide.
Allez sur https://packagemanager.posit.co/ et cliquer sur SETUP pour indiquer votre environnement. Je vous laisse suivre les instructions.
R et Shiny Server sont installés. Vous avez installé le code et les packages R nécessaires. Il ne nous reste plus qu’une étape : Installer le serveur web.
Installer et configurer le serveur web nginx
Pourquoi est-ce qu’on s’embête avec ces histoires de serveur web ? Shiny Server ne se suffit pas à lui-même ?
Pas vraiment.
Le serveur web (ici : nginx) va remplir plusieurs rôles essentiels que Shiny Server ne gère pas :
- Servir les certificats TLS pour chiffrer les connexions en HTTPS.
- Optimiser les requêtes (compression, gestion des connexions, etc.)
- Protéger les services internes : seul nginx sera exposé à Internet, et Shiny Server sera accessible uniquement en local
- Journaliser toutes les requêtes entrantes
Pas d’inquiétude : Chaque étape sera détaillée. À la fin de cette section, votre application sera accessible en HTTPS, avec un nom de domaine personnalisé.
Mais avant de plonger dans la configuration, commençons par… un sujet fondamental.
Choisir votre nom de domaine
Le nom de domaine, c’est l’adresse que vos utilisateurs vont taper pour accéder à votre application.
Et non, vous ne pouvez pas leur demander de retenir une adresse IP.
Et surtout : le nom de domaine est indispensable pour configurer le HTTPS.
Dans ce guide, j’utilise datachamp.eu
, un domaine que je possède déjà.
Si vous n’avez pas encore de nom de domaine, voici quelques options :
- Cloudflare : C’est ma reco (j’explique pourquoi après)
- Hetzner : Je ne savais même pas qu’Hetzner proposait des noms de domaine avant d’écrire cet article. Mais pourquoi pas en effet.
- OVH : C’est là que j’achète la plupart de mes noms de domaine aujourd’hui.
- Hover : C’est là que j’achetais la plupart de mes noms de domaine avant.
J’ai longtemps utilisé Hover. J’aime particulièrement que la vente de nom de domaine est leur principal business, il n’y a pas de distractions par millions.
Seul problème : Ils ne vendent pas de .fr
. Du coup, j’ai migré chez OVH.
Si j’avais su que Hetzner en vendait, je serais peut-être allé chez eux, histoire de tout centraliser au même endroit. Mais… je n’y ai même pas pensé.
Alors… pourquoi je recommande Cloudflare ?
Il y a une différence entre l’endroit où vous achetez votre domaine et l’endroit où vous le gérez (configuration DNS).
Et pour le coup, j’utilise bien Cloudflare pour toute ma gestion DNS :
- L’interface est claire et simple (bien qu’elle tend à se complexifier au fil des années)
- Leurs services gratuits sont vraiment excellents, notamment une protection contre les attaques DDoS
- Ils font partie des résolveurs DNS les plus performants
- On peut directement avoir le certificat TLS chez eux (gratuitement)
Je n’utilise que la version gratuite, et c’est déjà très complet.
Au final, achetez le nom de domaine où vous voulez. Mais je vous conseille fortement de centraliser la gestion des DNS sur Cloudflare.
La suite du guide montrera la configuration DNS sur Cloudflare.
Configurer vos DNS
L’adresse de votre serveur, c’est son adresse IP : 23.88.114.164
.
C’est comme un numéro de téléphone pour vous joindre.
Et d’ailleurs, comme pour le numéro de téléphone, personne ne les retient. À la place, on passe par le carnet d’adresses.
Pour un site web, c’est pareil : personne ne mémorise l’adresse IP d’un site. On utilise un nom de domaine, et quelque part il existe un « annuaire » qui fait le lien entre nom de domaine et adresse IP.
C’est le registre DNS.
Votre fournisseur DNS (Cloudflare, OVH, Hetzner) va vous permettre de rajouter une entrée dans ce gigantesque registre DNS mondial.
Essayons avec Cloudflare.
Connectez-vous, sélectionnez votre domaine, puis allez dans DNS > Records.
Cliquez sur Add record et remplissez ainsi :
- Type : A
- Name :
app
(ou@
si vous voulez pointer sur le domaine principal) - IPv4 address : 23.88.114.164
- Proxy status : Désactivé
- TTL : Auto
À quoi sert le « proxy status » ?
C’est une fonctionnalité particulièrement intéressante de Cloudflare qui va faire en sorte que toutes les requêtes dirigées vers votre serveur vont d’abord passer par les serveurs de Cloudflare.
Ça fait un détour ? Oui, mais vous obtenez ces avantages en retour :
- Protection DDoS : Puisque les requêtes passent d’abord par les serveurs de Cloudflare, ils peuvent stopper une attaque DDoS avant même que les requêtes n’arrivent sur votre serveur.
- Caching : Tout le contenu statique (images, fichiers CSS, fichiers JS, etc.) est mis en cache automatiquement. Voir la page Cloudflare Cache. Là encore, si la ressource est dans le cache, les requêtes n’atteignent même pas votre serveur, ce qui économise de la puissance de calcul.
- Cacher votre adresse IP : À la place, c’est l’IP du serveur de Cloudflare qui va être affichée, ce qui rend le ciblage de votre serveur plus difficile.
- HTTPS : Plus besoin de configurer le HTTPS, puisque le serveur proxy de Cloudflare va se charger lui-même de chiffrer le trafic entre le client et le proxy. Sauf que… le trafic entre le proxy et votre serveur ne sera pas chiffré (grosse faille de sécurité ici).
C’est plein d’avantages… si on sait ce qu’on fait et qu’on le configure correctement. Dans le cas contraire, on risque de se mettre des bâtons dans les jambes. Même le système de cache peut entraîner des surprises désagréables quand on passe des heures à essayer de comprendre pourquoi notre nouveau fichier de CSS n’est pas pris en compte.
C’est pourquoi je vous recommande pour l’instant de laisser cette case décochée. On va d’abord le faire SANS le proxy Cloudflare, et une fois qu’on sera au bout, je vous laisserai faire ce que vous voulez.
Voici ma configuration :
Alors qu’est-ce qui se passe si je tape https://app.datachamp.eu
dans ma barre d’adresse ?
Rien.
Quand je tape https
, c’est l’équivalent de demander le port 443 sur le serveur (pour http
, c’est 80).
Est-ce que j’ai un service qui écoute sur le port 443 ou 80 ?
Bah non.
Donc rien.
C’est là qu’intervient… nginx !
Installer nginx
Notre objectif est le suivant :
- Rediriger HTTP vers HTTPS : Toutes les requêtes arrivant sur le port 80 (HTTP) doivent être redirigées vers le port 443 (HTTPS) pour garantir le chiffrement.
- Transférer les requêtes HTTPS vers Shiny Server : nginx chiffre le trafic avec les certificats TLS, puis transmet la requête à Shiny Server
En théorie, nginx peut faire bien plus que ça, mais c’est déjà suffisant pour notre cas.
Retournez dans le terminal de votre VM (avec un utilisateur ayant les droits admin) et installez nginx :
sudo apt install nginx
Là encore, la version fournie par Debian n’est pas la toute dernière. Par exemple, sudo nginx -v
affiche 1.22.1, alors que la dernière version stable est la 1.28.0.
Mais : C’est OK. Inutile d’aller chercher la version la plus récente. Debian applique bien les correctifs de sécurité sur ses propres versions, même si le numéro ne change pas. C’est pour ça que le vrai numéro de version est en fait 1.22.1-9+deb12u2
:
1.22.1
est la version nginx-9
indique que c’est la 9e révision de cette version 1.22.1 par les devs de Debian+deb12u2
indique que c’est une version pour Debian 12 et qu’il s’agit de la 2e update (u2
) de cette version.
Les derniers caractères vont changer au fil du temps, mais Debian 12 va toujours proposer la 1.22.1
pendant toute sa durée de vie.
Quand Debian 13 sortira, alors elle proposera une version plus récente de nginx.
Maintenant que nginx est installé, essayez d’accéder à votre domaine en HTTP (par exemple : http://app.datachamp.eu/).
Vous devriez voir la page d’accueil par défaut de nginx :
Parfait ! nginx fonctionne.
À nous maintenant de remplacer cette page par notre application Shiny.
Configurer nginx
Commençons déjà par retirer la configuration par défaut (c’est fou qu’il faille toujours faire le ménage dès qu’on installe un nouveau logiciel) :
sudo rm /etc/nginx/sites-enabled/default
sudo rm /etc/nginx/sites-available/default
sudo rm -R /var/www/html
La configuration par défaut est dans /etc/nginx/nginx.conf
. Je vais l’écraser par ma propre version :
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
######
## Basic Settings
######
tcp_nodelay on;
types_hash_max_size 2048;
server_tokens off;
server_names_hash_bucket_size 64;
include /etc/nginx/mime.types;
default_type application/octet-stream;
######
## Buffers & Timeouts
######
# Buffer size for POST submissions
client_body_buffer_size 10K;
client_max_body_size 8m;
# Buffer size for Headers
client_header_buffer_size 1k;
# Max time to receive client headers/body
client_body_timeout 12;
client_header_timeout 12;
# Max time to keep a connection open for
keepalive_timeout 15;
# Max time for the client to accept/receive a response
send_timeout 10;
# Skip buffering for static files
sendfile on;
# Optimise sendfile packets
tcp_nopush on;
######
## Add headers
######
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "frame-ancestors 'self'";
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy "no-referrer";
add_header Permissions-Policy "geolocation=(); midi=(); notifications=(); push=(); microphone=(); camera=(); magnetometer=(); gyroscope=(); speaker self; vibrate=(); fullscreen self; payment=();";
######
## SSL Settings
######
include snippets/ssl-params.conf;
######
## Logging Settings
######
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
######
## Gzip Settings
######
gzip on;
gzip_disable "msie6";
gzip_comp_level 4;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain;
gzip_types text/css;
gzip_types text/js;
gzip_types text/xml;
gzip_types text/javascript;
gzip_types application/javascript;
gzip_types application/x-javascript;
gzip_types application/json;
gzip_types application/xml;
gzip_types application/rss+xml;
gzip_types image/svg+xml;
######
## Virtual Host Configs
######
include /etc/nginx/sites-enabled/*;
}
C’est la configuration nginx par défaut que j’utilise pour tous mes projets. On pourrait utiliser celle de nginx, mais la mienne contient plusieurs améliorations significatives, dont voici un succinct résumé :
Elle utilise l’utilisateur www-data
. De manière similaire à Shiny Server qui utilise shiny
, on utilise un utilisateur qui n’a pas de droits d’admin.
Elle configure les en-têtes HTTP de sécurité. Ce sont des bouts d’information qui sont ajoutés dans les réponses envoyées par le serveur, et qui donnent des indications de sécurité au navigateur, permettant de réduire le risque d’attaques assez classiques.
Elle cache le numéro de version de nginx. Quand une faille de sécurité est découverte, c’est bien de ne pas crier sur les toits qu’on a exactement la version correspondant à la faille. Ceci dit, cacher l’information n’est pas une protection suffisante non plus : il faut appliquer le patch de sécurité.
Tout ce bazar doit être entré dans le fichier /etc/nginx/nginx.conf
. Vous pouvez supprimer le fichier précédent, puis le recréer avec sudo nano /etc/nginx/nginx.conf
pour copier/coller le contenu.
On n’a pas fini, puisque ce premier fichier fait appel à deux autres fichiers.
Le premier est /etc/nginx/dhparams.pem
, que vous pouvez créer avec la commande suivante :
sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096
Vous allez voir une succession de points .
, de plus +
et d’astérisques *
. Je ne sais pas exactement pourquoi il y a cet affichage, mais en arrière-plan il y a des algos qui tournent pour calculer des nombres premiers. Une sombre histoire.
Le deuxième fichier est /etc/nginx/snippets/ssl-params.conf
, dont voici le contenu :
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/nginx/dhparam.pem;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve X25519:prime256v1;
# HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Je vais être honnête, je ne comprends pas TOUT ce qui est fait ici. Je sais juste que :
- ça permet d’avoir un chiffrage fort
- on s’assure ne de pas utiliser des protocoles obsolètes
- on interdit les algorithmes de chiffrage faibles
- on force les navigateurs à utiliser HTTPS au lieu de HTTP
Quand on aura tout fini, vous pourrez tester votre configuration SSL/TLS sur https://www.ssllabs.com/ssltest/. Vous devriez obtenir une note A+ :
Mais pour l’instant on n’a pas fini. On n’a même pas appliqué notre nouvelle config.
Générer les certificats SSL/TLS
Les certificats SSL/TLS sont indispensables pour deux raisons :
- Chiffrer le trafic entre vos utilisateurs et le serveur
- Garantir à l’utilisateur que vous êtes bien qui vous prétendez être
Le premier point est simple : le certificat sert de clé cryptographique pour chiffrer toutes les communications.
Le second est plus subtil.
Quand vous envoyez une requête à un serveur, comment être sûr que c’est bien le bon serveur qui vous répond ? Votre requête a fait le tour du monde, a sauté de routeur en routeur, et à tout moment un routeur méchant pourrait décider de vous répondre en vous faisant croire que c’est lui le serveur.
C’est ce qu’on appelle une attaque Man-in-the-middle.
Pour l’éviter, des tiers de confiance (autorités de certification) délivrent des certificats : seuls les serveurs légitimes peuvent les présenter, et le navigateur détecte toute tentative d’usurpation.
Comment obtenir un certificat ?
Pas de laisser-passer A38 ni de formulaire bleu ici. À la place, il existe un outil gratuit et automatisé : certbot
Le site vous guide selon votre système et votre serveur web :
Voici les instructions à taper :
# Installer snapd
sudo apt update
sudo apt install snapd
sudo snap install snapd
# Installer certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Il ne reste plus qu’à demander le certificat, avec l’option certonly
pour ne pas que certbot ne vienne modifier votre belle configuration nginx :
sudo certbot certonly --nginx -d app.datachamp.eu
Si cette étape échoue, c’est probablement que vous avez fait une erreur quelque part dans la configuration DNS.
Si tout se passe bien, vous allez voir :
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/app.datachamp.eu/fullchain.pem
Key is saved at: /etc/letsencrypt/live/app.datachamp.eu/privkey.pem
This certificate expires on 2025-09-14.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Et là…
Attends.
Le certificat expire le 14 septembre ?
Pas d’inquiétude. certbot
s’occupe de tout et va justement renouveler automatiquement les certificats au bon moment. Vous n’aurez plus jamais à vous en soucier.
Notez bien le chemin du certificat et de la clé, on va en avoir besoin à l’étape suivante.
Rediriger les requêtes vers Shiny Server
Ça y est, on y est presque !
Il reste juste à créer la configuration nginx spécifique à Shiny.
Créez le fichier /etc/nginx/sites-available/shiny.conf
et collez-y le texte suivant :
server {
listen 80;
listen [::]:80;
server_name app.datachamp.eu;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.datachamp.eu;
location / {
proxy_pass http://localhost:3838;
proxy_redirect / $scheme://$http_host/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 20d;
proxy_buffering off;
}
ssl_certificate /etc/letsencrypt/live/app.datachamp.eu/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.datachamp.eu/privkey.pem;
}
Il faut que vous adaptiez le texte :
- Remplacez
app.datachamp.eu
par votre propre domaine partout. - Mettez à jour les chemins des certificats.
Alors à quoi sert ce fichier de configuration ?
Le premier bloc redirige tout le trafic HTTP vers HTTPS. Il écoute sur le port 80, et redirige (301) vers l’adresse en https
Le second bloc reçoit les requêtes HTTPS et les transmet à Shiny Server (qui écoute en local sur le port 3838).
Les deux dernières lignes servent les certificats SSL/TLS.
Pour tester, il faut d’abord activer la configuration :
sudo ln -s /etc/nginx/sites-available/shiny.conf /etc/nginx/sites-enabled/shiny.conf
Et vérifier qu’il n’y a pas d’erreur de syntaxe :
sudo nginx -t
Tout est bon de mon côté :
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Il ne reste plus qu’à appliquer la configuration nginx :
sudo systemctl restart nginx
C’est terminé !
Votre application est maintenant accessible en HTTPS sur https://app.datachamp.eu.
Ah. Oops.
Petit rappel : Si vous accédez à la racine du domaine, vous allez voir le contenu du dossier /srv/shiny-server
.
Sauf que nous, notre application est dans /srv/shiny-server/shiny-tiny-hackathon
. Donc l’application est disponible sur : https://app.datachamp.eu/shiny-tiny-hackathon
Enfin :
On y arrive.
Bravo.
Les 7 habitudes des serveurs efficaces
Avant de partir.
1. Mettez à jour votre serveur au moins une fois par mois. Si vous avez activé unattended-upgrades
, la plupart des failles critiques seront corrigées automatiquement, mais prenez l’habitude de vérifier de temps en temps. Et redémarrez après une mise à jour du noyau.
2. Sauvegardez votre serveur de production. On ne sait jamais : erreur de manipulation, piratage, blocage, … Si le serveur est critique, privilégiez des sauvegardes en dehors du fournisseur principal.
3. Apprenez à administrer un serveur. Maîtrisez les bases : bash
, vim
, configuration nginx
, etc. Tout ce qu’on a vu dans ce guide, mais en allant plus loin. Testez, expérimentez.
4. Mettez en place de la supervision et des alertes. Soyez prévenu si le serveur tombe ou si un service dysfonctionne. Par exemple, vous pouvez utiliser node-exporter
+ Prometheus + Grafana, ou des services tiers comme Netdata.cloud.
5. Changez régulièrement vos mots de passe et clés SSH. Surtout si vous travaillez en équipe. Je change mes mots de passe et clés SSH une fois par an, et surtout ne les réutilisez pas partout.
6. Automatisez le déploiement (CI/CD). Une fois la première mise en production terminée, vous pouvez mettre en place un pipeline pour déployer vos mises à jour automatiquement.
7. Apprenez à utiliser Ansible pour automatiser l’infra. Chez Data Champ’, on ne déploie presque plus rien à la main. L’automatisation avec Ansible permet de gagner du temps et d’éviter les erreurs : tout le guide peut être rejoué automatiquement.
Adoptez ces sept habitudes, et votre VM à 5 €/mois restera rapide, sécurisée, et fiable.
Annexe
AWS coûte cher
Très cher.
Prenons une machine avec 4 Go de RAM.
Vous pourriez prendre une instance EC2 de type t4g.medium
à $27 par mois (page des tarifs).
Ou un VPS Lightsail pour $24 par mois (page des tarifs).
Une machine similaire sur Hetzner coûte $4,59 par mois (page des tarifs).
Qu’est-ce qui justifie un prix CINQ FOIS plus élevé d’après vous ?
Un meilleur CPU ?
Même pas, d’après vpsbenchmarks.com.
Liens source : EC2, Lightsail, et Hetzner.
Ce que propose EC2 est à peine meilleur que Hetzner.
Et on peut trouver de meilleurs CPUs sur Hetzner pour un prix qui est toujours trois fois moins cher qu’AWS.
Même une adresse IPv4 coûte 3,6$ chez AWS contre 0,6$ chez Hetzner.
AWS coûte cher.
AWS est compliqué
J’entends souvent la remarque suivante : « Ah oui c’est sûr c’est plus cher, mais c’est entièrement managé, donc tu économises sur ton personnel. »
Ah bon ?
Pourquoi il faut recruter un expert AWS alors ?
Expert qui coûte deux fois plus cher qu’un classique IT guy.
AWS n’est pas complètement managé.
En fait non. En passant par AWS, on échange la complexité de gérer des serveurs Linux contre la complexité de gérer AWS.
L’interface est un vrai labyrinthe. La doc se lit comme un dictionnaire. Et maintenant on peut se créer sa propre carrière rien que sur la compétence AWS tellement c’est une usine à gaz.
Alors quoi ? Pourquoi ?
C’est plus compliqué ET plus cher.
Pourquoi vous vous infligez ça ?
Certaines entreprises en ont besoin. À l’origine, AWS a été créé pour Amazon :
- Des millions de visiteurs tous les jours
- Des milliers de micro services
- Des pics de visites x10 les jours de solde
Là, oui, OK, c’est intéressant d’aller voir un cloud public comme AWS.
Mais pour votre tableau de bord en Shiny ?
Le tableau que seule l’équipe interne utilise ?
Même si vous avez des milliers de visites tous les jours, vous n’avez pas besoin d’AWS.
Commentaires