Le guide ultime pour déployer une application Shiny sur AWS

Une des requêtes les plus fréquentes que j’ai de la part de mes clients est de créer une application en R Shiny qui permet aux utilisateurs finaux de visualiser tout un tas d’indicateurs et aussi d’interagir avec les données.

Plutôt la base pour une appli Shiny.

Étant statisticien à l’origine, j’ai appris au fil des années à créer des dashboards plutôt cools et mes clients sont contents !

Sauf que vient toujours le moment où il faut déployer l’application sur les systèmes du client.

Et là… ça se complique.

De Shiny à AWS

Je maîtrise plutôt bien le langage R et il m’arrive régulièrement de créer des dashboards en Shiny pour faire des reportings automatiques.

Sauf que… créer un serveur où je peux héberger l’application, la configurer, et ensuite maintenir le serveur à jour…

C’est pas tout à fait mon boulot à la base.

Intro

La méthode « galère »

Je vous avoue que la toute première fois où j’ai été confronté au problème, mon équipe et moi avons opté pour une solution un peu… disons… artisanale !

J’ai presque honte d’admettre ce qu’on a fait…

On a…

On a installé R et le code de l’appli Shiny sur chaque poste de chaque utilisateur.

Voilà.

Oui oui.

Il a fallu aller dans chaque bureau, installer R, les packages, aller chercher les données, ajouter un raccourci sur le bureau, … Bref.

Avec le recul, on avait été assez créatif sur le coup.

Sad Face

Mais en vrai c’était horrible !

  • Il faut quand même un moyen de centraliser les données.
  • Ça prend un temps fou d’installer l’appli sur le poste de chaque utilisateur.
  • Et puis, niveau maintenance, c’est juste pas possible.

Mais bon.

On n’avait ni les compétences, ni les moyens d’accéder à un serveur.

On s’est débrouillé comme on pouvait.

Mais il faut savoir qu’il existe une bien meilleure méthode.

La méthode « qui marche »

Il s’agit tout simplement de :

  • Créer un serveur dans le cloud.
  • Envoyer l’application sur le cloud.

Et c’est tout.

Bon, bon, ok, je dis tout simplement, mais c’est pas SI simple (sinon ce guide ne ferait pas 13.000 mots).

Au début en tout cas.

Aujourd’hui, dans ce guide sur le déploiement d’application Shiny sur un serveur AWS, on va voir exactement comment procéder, étape par étape.

Comment ?

D’abord, on va utiliser AWS, c’est le petit nom de Amazon Web Services, c’est-à-dire tous les services Cloud proposés par Amazon.

Vous pourriez utiliser des services concurrents, comme Azure chez Microsoft, Digital Ocean, Rackspace, etc.

Peu importe en fait.

Au début, j’utilisais AWS, maintenant j’utilise mon propre serveur dédié. Le fonctionnement est exactement le même et ce guide vous servira quelque soit le fournisseur que vous utilisez.

Mais on va utiliser AWS parce que c’est ce qui est le plus utilisé.

En plus, quand on crée un nouveau compte, on a accès à certains services gratuitement pendant un an.

Tout ce qu’on va faire dans ce tutorial est faisable sans débourser un sou.

Happy Face

Les avantages de cette méthode :

  • Vous pouvez déployer l’application sur le serveur depuis votre canapé.
  • C’est facile à maintenir et à mettre à jour.
  • Vous pouvez facilement moduler la puissance du serveur.
  • Vous avez la possibilité de sécuriser votre application comme vous le souhaitez.

À quoi s'attendre dans ce guide ?

Mon but est de créer un guide de référence auquel vous pourrez toujours revenir dès que vous serez confronté à la problématique Comment déployer mon appli sur un serveur ?

Jusqu’à ce que vous reteniez comment faire.

Mais je sais d’expérience que c’est le genre de chose qu’on oublie vite.

Je vais donc expliquer chaque étape avec moults détails, plein de screenshots, et des dessins rigolos.

Quelles étapes ? Les voici :

Dans la Partie 1, on va aller se trouver une application Shiny basique. Bon, c’est pas le but du guide d’apprendre à créer des applis, donc on va en prendre une qui existe déjà.

Dans la Partie 2, je vous montrerai comment créer un serveur sur AWS. C’est la seule partie qui est vraiment spécifique à AWS, et en vrai, elle est nécessaire seulement parce que AWS est assez difficile à prendre en main tout au début.

Dans la Partie 3, on verra comment installer R et R Shiny sur votre tout nouveau serveur. Comment ça marche ? Comment on fait ? À l’issue de cette partie, vous aurez déjà un serveur fonctionnel et même une appli Shiny disponible sur les internets !

Dans la Partie 4, on va finalement déployer l’application sur le serveur, pour qu’elle soit disponible à tous !

J’aurais pu m’arrêter là. Sauf que.. vous n’aurez pas un résultat parfait, et probablement encore d’autres questions. C’est quoi j’ai rajouté trois autres parties.

Dans la Partie 5, on va aller chercher un nom de domaine pour avoir une belle URL. Oui parce que jusqu’ici, il fallait taper une adresse IP pour accéder à l’application.

Dans la Partie 6, ça va se compliquer. On va se prendre un serveur web qui s’appelle nginx, on va générer des certificats TLS, tout ça afin de sécuriser votre application en HTTPS. Ça paraît compliqué, mais chaque étape sera détaillée !

Dans la Partie 7, on voit finalement comment protéger l’application avec un mot de passe. Oui parce que tout le monde ne veut pas que son appli soit ouverte au public. Je vous montre notamment quelles sont les différentes possibilités qui s’offrent à vous pour protéger votre appli.

Et là, on sera paré !

D’ailleurs, je vous propose de commencer directement avec l’étape 1 : Créer une application Shiny.

1. Créer une application Shiny

C’est pas exactement le but de ce guide d’apprendre à utiliser Shiny, du coup on va prendre une appli disponible dans la galerie Shiny.

J’ai pris celle de l’explorateur de films :

Movie Explorer

Et j’ai créé un repo sur Gitlab pour que vous puissiez la cloner facilement : https://gitlab.datachamp.fr/charles/051-movie-explorer

Notez que vous aurez besoin de git et d’une console pour suivre ce tutoriel. Vous pouvez télécharger git à l’adresse suivante : https://git-scm.com/downloads

Euh.. c’est déjà fini ?

Ben oui, c’est partie là est vraiment courte :D Profitez-en !

2. Créer un serveur sur AWS

Nous allons commencer par créer un serveur AWS afin d’héberger notre application sur les internets.

Créer votre compte gratuitement sur AWS

Pour ça, la première étape est de vous créer un compte sur AWS.

Rendez-vous d’abord sur https://aws.amazon.com et créez un compte. Les étapes sont simples à suivre donc je vous laisse faire.

Notez qu’Amazon va vous demander un moyen de paiement, puisqu’à chaque fois que vous utiliserez un service payant, il faudra bien le régler.

Mais rassurez-vous, lorsque vous ouvrez un nouveau compte, certains services (limités, certes) sont gratuits afin que vous puissiez vous faire la main sans soucis !

À noter aussi qu’il est possible de réserver un serveur pour très peu d’argent. À titre informatif, mon site aujourd’hui est hébergé sur AWS et il ne m’en coûte que 10$ par mois. J’y héberge aussi mes applications Shiny, un RStudio Server, un Jupyter Notebook, et encore d’autres services.

Créer votre premier serveur sur EC2

Une fois votre compte créé, identifiez-vous, et vous arriverez sur une page avec tous les services proposés par AWS :

Services AWS

Dans le champ de recherche en haut, tapez EC2.

Kesako

EC2, ça veut dire Elastic Compute Cloud. En français, un nuage élastique qui fait des calculs.

En fait, il s’agit d’un service qui permet de louer des ressources d’un serveur afin de :

  • créer des services web (comme héberger un site, ou … une appli Shiny !)
  • faire des calculs (comme faire tourner un algorithme de machine learning)
  • stocker des données

AWS dispose d’un service plus adapté pour stocker des données, comme S3, mais quand on veut héberger un site ou faire tourner un modèle, il faut bien stocker les données et les fichiers du site quelque part !

Le gros atout de ce service, c’est qu’on peut très facilement changer la configuration du serveur.

Peut-être qu’aujourd’hui vous avez besoin d’une petite machine pas trop gourmande. Mais demain, quand le monde entier ira visiter votre appli Shiny, vous devrez augmenter la RAM ou la puissance du processeur. C’est possible en seulement quelques clics.

Ou bien imaginez que vous souhaitiez faire tourner un algorithme de Deep Learning mais votre petit laptop n’est pas assez puissant.

Louez une machine bien balèze, importez vos données, faites tourner l’algorithme, exportez vos résultats, éteignez la machine, et hop! le problème est réglé.

Vraiment pratique.

Bref, je parle, je parle, en attendant on a toujours pas de serveur.

Créons le serveur !

Dans le champ de recherche, tapez EC2 et entrez dans le service.

À gauche, dans le menu, cliquez sur Instances. Une instance, c’est un serveur.

Puis ensuite, cliquez sur Launch Instance

Étape 1 : Choisir votre AMI

Choisir votre AMI

Une AMI, c’est une Amazon Machine Image, c’est-à-dire une sorte de serveur pré-packagé avec déjà plein de choses prêtes à l’intérieur. Il y a des AMIs préparées par Amazon, on peut en créer nous-même, et on peut même en louer à des entreprises tierces (par exemple avec des logiciels propriétaires à l’intérieur).

Nous, on va prendre une AMI de base, la Ubuntu Server 18.04 LTS.

Sélectionnez-là en cliquant sur Select.

Étape 2 : Choisir votre type d'instance

Choisir votre type d'instance

Le type d’instance correspond à la puissance de votre machine.

Est-ce que vous avez besoin d’une bête de course ou d’une petite machine peu puissante ?

Dans ce guide, inutile d’avoir une grosse machine. En fait, on va prendre le type t2.micro qui est le seul type d’instance éligible à l’offre gratuite d’AWS. Elle contient un processeur avec 1 seul cœur, seulement 1 GB de RAM, et une connexion internet pas ouf mais suffisante.

Pour en savoir plus sur les différents types d’instance qui existent, vous pouvez aller voir la page Amazon EC2 Instance Types qui répertorie toute la liste.

Ensuite, cliquez sur Next: Configure Instance Details.

Dans l’écran suivant, on ne va rien modifier, donc cliquez directement sur Next: Add Storage

Étape 3 : Choisir votre taille de disque dur

Choisir votre taille de disque dur

Par défaut, AWS choisit de vous allouer 8 GB d’espace sur votre serveur.

À vous de décider si vous avez besoin de plus ou non.

Notez que pour rester dans l’offre gratuite d’AWS, vous devez allouer un maximum de 30 GB.

À présent, la configuration est terminée !

Vous pouvez cliquer sur Review and Launch.

Un écran va vous résumer les différentes options que vous avez choisi. Vous pouvez y jeter un œil puis valider le lancement de votre instance en cliquant sur Launch !

Étape 4 : Créez une clé de sécurité

Créez une clé de sécurité

Après avoir cliqué sur Launch, AWS va vous demander de créer une paire de clés. Qu’est-ce que c’est que cette histoire de clés ?

Eh bien il s’agit tout simplement de protéger votre serveur à présent ! Comme il est connecté aux internets, s’il n’y avait pas de clé, tout le monde pourrait y accéder et mettre le bazar !

Pas cool.

Du coup, vous allez créer une clé d’accès sous la forme d’un fichier que vous allez garder sur votre ordinateur. À chaque fois que vous voudrez vous connectez sur votre instance, il faudra spécifier ce fichier et hop vous aurez accès !

Important : Ne perdez pas ce fichier. Sinon, vous risquez de vous voir refuser l’accès à votre propre serveur et vous devrez tout recommencer !

Choisissez un nom pour votre clé, et téléchargez-la.

Félicitations, vous avez créé votre premier serveur !

Après quelques minutes, votre instance sera prête et apparaîtra dans la liste de vos instances.

Nouvelle instance créée

Prochaine étape : On va voir comment s’y connecter en SSH puis on installer R et R Shiny dessus !

3. Installer R et R Shiny sur votre nouveau serveur

Nous nous sommes donc arrêtés à la création du serveur sur AWS.

Dans cette nouvelle partie, nous allons apprendre à :

  • accéder à ce serveur via SSH
  • installer R et R Shiny Server

Comment accéder à votre nouveau serveur ?

Une fois votre instance créée, AWS vous propose un bouton Connect. Bouton qui fait apparaître la fenêtre suivante :

Bouton Connect

Ce sont les instructions pour se connecter à votre serveur. À présent, on va séparer les instructions selon votre système d’exploitation.

Pour les utilisateurs de Windows

Si vous êtes sous Windows, vous aurez besoin de télécharger un client SSH, puisque l’OS n’en propose pas par défaut. Le plus couramment utilisé est PuTTY que vous pouvez télécharger directement à partir de https://www.putty.org/.

Ensuite, il va vous falloir transformer la clé que vous avez téléchargée dans le chapitre précédent (vous savez, le fichier avec l’extension pem) :

PuTTYgen

  1. Lancez PuTTYgen (tapez “PuTTYgen” dans votre menu démarrer)
  2. Choisissez RSA.
  3. Allez chercher le fichier pem que vous avez téléchargé (vous aurez besoin d’activer l’option permettant d’afficher tous les types de fichiers)
  4. Enregistrez la clé privée sous le même nom que le fichier pem (l’extension sera différente, en ppk).

Ensuite, lancez PuTTY puis configurez-le avec les informations données par AWS :

PuTTY config

  1. Dans Host Name (or IP address), rentrez la valeur qui se trouve au point 4 du screenshot ci-dessus (c’est ec2-52-59-238-223.eu-central-1.compute.amazonaws.com pour moi)
  2. Port : 22
  3. Connection type : SSH
  4. Changez de menu et allez dans Connection / SSH / Auth, et chargez la clé ppk que vous avez enregistrée il y a quelques instants.

Cliquez sur “Open”, entrez “ubuntu” quand on vous demande login as, puis dites “Oui”, et une fenêtre console va s’ouvrir !

Bravo, vous êtes à présent connecté à votre serveur !

PuTTY console

Pour les utilisateurs de Ubuntu ou Mac

Si vous n’êtes pas sous Windows, c’est beaucoup plus simple !

Il vous suffit d’ouvrir une console, de vous déplacer jusqu’au répertoire qui contient la clé pem puis de taper l’instruction donnée par AWS.

Par exemple pour moi, c’est :

$ cd Downloads/
$ ssh -i "tutorial.pem" ubuntu@ec2-52-59-238-223.eu-central-1.compute.amazonaws.com

Dites “yes” et vous serez connecté.

Si vous obtenez un message d’erreur “WARNING: UNPROTECTED PRIVATE KEY FILE!”, alors il vous faudra changer les droits du fichier avec la commande suivante :

$ chmod 400 tutorial.pem

Comment installer Shiny Server ?

La prochaine étape, c’est d’installer Shiny Server.

Et un premier pré-requis, c’est évidemment d’installer R !

Généralement, les repos d’Ubuntu ne contiennent pas la dernière version de R. Du coup, on va directement ajouter le repo du CRAN de cette manière :

$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
$ sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/'

Une fois ces commandes lancées, le repo du CRAN est ajouté. On peut alors installer R :

$ sudo apt update
$ sudo apt install r-base r-base-dev

Une fois R installé, et en préambule à l’installation de Shiny Server, il va falloir installer le package shiny.

Pour ça, lancez R en mode administrateur et installez le package :

$ sudo R
> install.packages("shiny")

Vous pouvez quitter R en tapant q() puis choisissez n.

Ok, on progresse. Il ne reste plus qu’à installer Shiny Server !

Il va falloir aller télécharger la dernière version d’abord. Rendez-vous sur le site de RStudio à l’adresse suivante : https://www.rstudio.com/products/shiny/download-server/ et on va vous donner les instructions tout en bas de la page.

Pour moi, il s’agit de :

$ sudo apt-get install gdebi-core
$ wget https://download3.rstudio.org/ubuntu-14.04/x86_64/shiny-server-1.5.13.944-amd64.deb
$ sudo gdebi shiny-server-1.5.13.944-amd64.deb

A priori les deux dernières lignes seront différentes pour vous si une nouvelle version de Shiny Server sort entre-temps.

Une fois les commandes lancées, dites “oui”, et c’est parti !

Ça y est !

Vous ne le savez pas encore, mais vous avez une application Shiny qui tourne sur votre serveur.

Comment y accéder ?

C’est ce qu’on voit dans la partie suivante…

4. Déployer l’application sur le serveur

Rappelons les étapes déjà réalisées.

D’abord, on a créé une application Shiny et on l’a envoyée sur Gitlab. Ensuite, on a créé un compte sur AWS pour ouvrir un serveur, sur lequel on a préparé le terrain en installant R et R Shiny.

À la fin du précédent chapitre, je vous disais que vous aviez déjà une appli Shiny qui tourne sur votre serveur !

En effet, elle est apparue en installant Shiny Server.

1. Comment accéder à l'application Shiny installée par défaut ?

Pour y accéder, deux étapes :

  1. Trouver l’adresse IP de votre serveur
  2. Ouvrir le port 3838 sur le pare-feu

Trouver l’adresse IP de votre serveur

Dès que vous activez votre serveur sur AWS, une adresse IP y est affectée automatiquement.

Attention: L’adresse IP change dès que vous éteignez/rallumez le serveur ! Si vous voulez avoir une IP fixe, il faudra aller voir dans Elastic IP et allouer une adresse IP directement à votre serveur.

Dans votre dashboard sur EC2, il y a une colonne IPv4 Public IP.

Repérez-la et notez l’adresse IP.

Adresse IP AWS

Chez moi, c’est 3.121.42.9

Pour accéder à l’application Shiny, il suffit alors simplement de taper l’adresse IP dans votre navigateur préférée en spécifiant le port réservé à Shiny : 3838

3.121.42.9:3838

Sauf que… si je fais ça, la page ne se charge pas.

Firewall AWS

Ouvrir le port 3838 sur le pare-feu

En effet : Par défaut, AWS n’autorise que le port 22, c’est-à-dire le port réservé à SSH, le protocole qu’on a utilisé dans le chapitre précédent pour se connecter au serveur via la console.

C’est une mesure de sécurité.

Pour changer ça, à partir du dashboard EC2 :

  • Sélectionnez votre instance.
  • Dans le cadre du bas, repérez la ligne Security groups et cliquez sur le premier lien.
  • Dans la nouvelle fenêtre, dans le cadre du bas, cliquez sur “Inbound”, le deuxième onglet. Vous voyez alors que seulement le port 22 apparaît.
  • Cliquez sur “Edit”, puis dans le pop-up, “Add Rule”. Les paramètres à entrer sont les suivants :
Type Protocol Port Range Source Description
SSH TCP 22 Custom: 0.0.0.0/0  
Custom TCP Rule TCP 3838 Custom: 0.0.0.0/0  

Voici quelques screenshots si jamais vous êtes perdu :

Ouvrir ports sur AWS

.

Ouvrir ports sur AWS

Une fois que vous avez ouvert le port 3838, l’application devient accessible !

Ré-essayons d’y accéder :

3.121.42.9:3838

Cette fois-ci…

Default app

Boom !

Ça marche ! Top cool !

Note : L’adresse est un peu moche pour l’instant. D’abord il faut taper une adresse IP, et ensuite un numéro de port. Dans le chapitre suivant, on verra comment avoir une belle adresse internet et se débarrasser du numéro de port.

C’est une page html avec des widgets shiny sur le côté droit de la page.

D’ailleurs, vous noterez peut-être un message d’erreur dans un des widgets : “An error has occurred”. C’est tout simplement un problème de package qui n’est pas installé.

Ça c’est chouette mais.. le but c’est de mettre notre application maintenant !

2. Comment ajouter votre application sur votre serveur ?

Pour ça, on va devoir retourner dans la console sur notre serveur.

Si vous avez déjà oublié comment on faisait, retournez voir le chapitre précédent Installer R et R Shiny sur le serveur.

Dans un premier temps, on va explorer un peu notre serveur.

Explorer le serveur

On vient tout juste de voir que quand on installe Shiny Server pour la première fois, une application par défaut s’installe et nous souhaite la bienvenue !

Où se trouve cette application ?

Elle se trouve tout simplement dans le dossier /srv/shiny-server/, et c’est là que seront stockées toutes vos applications.

Tapez les commandes suivantes dans votre console :

$ cd /srv/shiny-server/
$ ls
index.html  sample-apps

Vous allez voir juste un seul dossier : sample-apps, et un fichier : index.html.

Le fichier index.html correspond à ce que nous avons vu lorsque nous sommes allés nous connecter sur la page web de notre serveur, et il fait appel à l’application qui se trouve dans sample-apps. Si on va voir dedans :

$ cd sample-apps/hello
$ ls
server.R  ui.R

On retrouve nos fidèles server.R et ui.R qui sont la base de toute application Shiny !

Par défaut, Shiny Server va aller chercher les applications qui se trouvent dans ce dossier. Il est possible de le changer à partir du fichier de config qui se trouve dans /etc/shiny-server/shiny-server.conf mais je vous le déconseille.

Ce qu’on va plutôt faire, c’est :

  1. Télécharger notre application dans un dossier de travail.
  2. Créer un raccourci dans /srv/shiny-server/.

C’est généralement la pratique qui est recommandée.

Alors au travail !

2. Télécharger l'application sur le serveur à partir de Git

D’abord, on retourne dans le dossier de base :

$ cd

Ensuite, on clone notre application qui se trouve sur Gitlab :

$ git clone https://gitlab.datachamp.fr/charles/051-movie-explorer.git
Cloning into 'movie-explorer'...
remote: Enumerating objects: 9, done.
remote: Total 9 (delta 0), reused 0 (delta 0), pack-reused 9
Unpacking objects: 100% (9/9), done.
Checking connectivity... done.

Parfait ! Cette simple commande nous a permis de télécharger l’appli sur le serveur.

Sauf que Shiny s’attend à ce que l’appli se trouve dans le dossier bien spécial /srv/shiny-server/. Donc on va créer un raccourci là-bas :

3. Créer un raccourci dans /srv/shiny-server/

$ cd /srv/shiny-server
$ sudo ln -s ~/movie-explorer .

À présent, si on affiche la liste des fichiers, on va voir que notre raccourci a bien été créé :

$ ls
index.html  movie-explorer  sample-apps

D’ailleurs, au passage, je vous propose de supprimer l’appli installée par défaut :

$ sudo rm index.html
$ sudo rm -R sample-apps

Eh bien ça y est !

On a ajouté notre application dans le bon dossier, à présent elle devrait être accessible sur les internets.

Testons !

Si je tape 3.121.42.9:3838 dans la barre de mon navigateur, je tombe sur…

Root folder

Oops, pas très joli ça. On va voir après comment le changer.

Cliquons sur le lien, et…

ERROR: An error has occurred. Check your logs or contact the app author for clarification.

Eh :(

Bon.

Il semblerait que le serveur reconnaisse bien qu’on a ajouté notre application, mais elle ne marche pas !

Pourquoi ?

3. Comment configurer Shiny Server ?

Il va falloir changer un petit peu la config.

Le fichier de configuration de Shiny Server se trouve dans /etc/shiny-server/shiny-server.conf.

On va l’ouvrir et essayer de comprendre :

sudo nano /etc/shiny-server/shiny-server.conf

Le fichier ressemble à ç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;
  }
}

Je vais vous expliquer ligne par ligne comment le comprendre.

  • La première ligne run_as shiny indique l’utilisateur qui est derrière le Shiny Server. Quand vous vous connectez au serveur via SSH, vous dirigez l’utilisateur ubuntu, celui par défaut. Quand on a installé Shiny Server, il a créé un nouvel utilisateur, qui s’appelle shiny, qui est celui qui va faire tourner le serveur. C’est important pour la suite.
  • Ensuite, on a listen 3838, ça veut dire qu’il faut spécifier le port 3838 pour accéder à l’appli. C’est justement ce qu’on a fait quand on a tapé l’adresse dans notre navigateur.
  • La ligne site_dir /srv/shiny-server indique l’endroit où se trouvent les applis Shiny. Et effectivement, c’est là qu’on a placé la notre. En tout cas, on y a mis un raccourci.
  • Finalement, log_dir /var/log/shiny-server spécifie l’endroit où se trouve les logs. Ça c’est super utile, puisque ça va nous permettre de comprendre pourquoi notre appli a planté. On va aller les voir juste après.
  • La dernière ligne, directory_index on, comme le commentaire l’indique, permettre de montrer l’arborescence des dossiers à la racine. C’est ce qu’on voit quand je vais directement sur 3.121.42.9:3838. Moi je trouve ça un peu moche, du coup j’ai tendance à le désactiver : directory_index off. En plus, c’est mieux si on préfère ne pas montrer toutes les applis dont on dispose.

Outre cette modification optionnelle pour la dernière ligne, je vous invite à rajouter une ligne tout en haut du fichier de config :

preserve_logs true;

Cette ligne permet de garder les logs en toute circonstance.

En effet, Shiny a la mauvaise habitude de supprimer les logs quand il considère qu’il ne s’est rien passé d’intéressant. Et parfois, notre appli bug, on veut voir les logs… et ils ne sont pas là ! Cette ligne permet d’éviter ce problème.

Important : Lorsque vous changez la config, il faut recharger le Shiny Server avec la commande suivante afin que les changements soient pris en compte :

$ sudo systemctl reload shiny-server

D’ailleurs, en parlant de logs… allons voir pourquoi notre appli plante !

4. Comment débugger une appli Shiny à partir des logs

Pour aller voir les logs :

$ cd /var/log/shiny-server
$ ls
movie-explorer-shiny-20181210-080534-46879.log rmd-shiny-20181023-144836-40079.log

Je vois deux fichiers dans la liste.

Celui nommé movie-explorer correspond à notre application. L’autre est l’application de base par défaut.

Les chiffres dans les noms des fichiers correspondent à la date. Donc le premier fichier a été créé le 2018-12-10 à 08:05:34. Quand les fichiers de logs commencent à s’accumuler, c’est pratique pour s’y retrouver.

Si jamais vous ne voyez pas de fichier, essayez de relancer l’application. C’est le problème des logs qui disparaissent que je mentionnais juste avant. Mais il sera résolu avec la nouvelle config.

J’affiche les dernières lignes du fichier de log et je vois :

$ sudo tail movie-explorer-shiny-20181210-080534-46879.log
Warning message:
replacing previous import by ‘Rcpp::evalCpp’ when loading ‘later’ 

Listening on http://127.0.0.1:44533
Warning: Error in library: there is no package called ‘ggvis’
  48: stop
  47: library


Execution halted

Mais oui !

On n’a pas installé les libraries nécessaires pour le fonctionnement de l’appli.

C’est évident maintenant qu’on le voit.

Mais dites-vous que c’est une source d’erreur très fréquente. On rajoute une librairie dans une appli, tout roule bien en local, on met à jour, et là… ça plante, on comprend plus pourquoi. Juste parce qu’on n’a pas pensé à installer le package sur le serveur.

Attention : Comme le Shiny Server est géré par l’utilisateur shiny, il faut installer les packages avec cet utilisateur. On peut changer d’utilisateur en tapant les commandes suivantes :

$ sudo su - shiny
$ R
> install.packages("ggvis")

Il va vous demander si vous voulez utiliser une bibliothèque locale, répondez que oui.

Une fois le package installé, ré-essayez de charger l’application.

Cette fois-ci, vous allez voir l’appli apparaître, et très rapidement elle crash (elle devient grisée).

App Shiny crash

Encore raté !

Si on retourne rapidement dans les logs, on s’aperçoit qu’un nouveau fichier s’est créé, et il nous indique :

$ sudo tail movie-explorer-shiny-20181211-083747-41773.log
The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Warning: Error in : The dbplyr package is required to communicate with database backends.
  56: <Anonymous>
Error : The dbplyr package is required to communicate with database backends.


Execution halted

Il manque un autre package ! dbplyr.

Note: Après avoir installé le package pour l’utilisateur shiny, n’oubliez pas de revenir à l’utilisateur principal ubuntu en tapant exit dans la console.

Je vous laisse installer le package, vous savez comment faire, à présent. Bon, et puis autant vous le dire tout de suite, il faut aussi installer RSQLite.

Une fois installés…

Shiny AWS Movie Explorer Final

Finalement !

On y arrive.

Note : J'en profite pour vous dire que si vous essayez d'installer dplyr, il faudra un serveur avec au moins 2 Go de ram.

C'est seulement pour l'installation, donc avec AWS vous pouvez très bien upgrader votre serveur puis le downgrader une fois l'installation terminée.

Après avoir :

  • Créé votre serveur sur AWS
  • Installé R et R Shiny dessus
  • Envoyé votre application sur le serveur
  • Débuggé l’appli à partir des logs
  • Et configuré le serveur

Enfin on peut accéder à notre appli depuis n’importe où dans le monde ! Et surtout, on peut la partager à d’autres personnes.

Par contre, vous avez peut-être encore quelques questions, comme…

  • Comment se débarrasser de cette adresse IP moche ?
  • Comment restreindre l’accès à l’application avec un mot de passe ?
  • Comment sécuriser l’application en HTTPS ?

C’est ce que nous voyons dans les prochains chapitres.

5. Extra : Créer un nom de domaine cool

Dans la partie 4 de ce guide, nous avons déployé l’appli Shiny sur le serveur AWS.

Rappelez-vous, on pouvait taper 3.121.42.9:3838 dans le navigateur et boum notre appli apparaissait !

Shiny AWS Movie Explorer Final

Plutôt cool !

Je vous mets un petit rappel des choses importantes qu’on a vues jusqu’ici pour manipuler le serveur :

  • On déploie l’appli dans le dossier /srv/shiny-server.
  • On peut lire les logs de Shiny dans /var/log/shiny-server.
  • On peut changer la config de Shiny dans /etc/shiny-server/shiny-server.conf.

Ce sont les trois importants dossier à se rappeler.

Au bout d’un moment ça rentre, mais d’ici là, n’hésitez pas à repasser ici quand vous en avez besoin.

Alors..

Comme je disais juste au-dessus, on peut maintenant visiter notre appli juste en tapant 3.121.42.9:3838.

Oui alors ok, sauf que qui va s’amuser à se rappeler tous ces numéros ?

Votre client ?

Sans doute pas.

Non en fait, dans le monde réel, personne ne visite jamais un site web en tapant l’adresse IP.

Par exemple, moi je mets toutes mes applis Shiny sur shiny.datachamp.fr.

C’est facile à se rappeler et ça fait du sens.

D’ailleurs j’ai hébergé notre appli à l’adresse suivante : https://shiny.datachamp.fr/051-movie-explorer/

Alors comment on fait ?

Trois étapes :

  1. Trouver un nom de domaine
  2. Paramétrer les DNS
  3. Mettre en place un reverse proxy

Si le vocabulaire utilisé vous fait un peu peur.. pas d’inquiétude !

Comme d’habitude, je vais vous guider étape par étape, avec des screenshots et tout et tout!

1. Trouver un nom de domaine

La toute première étape, pour utiliser un nom de domaine, c’est.. d’en avoir un !

Ça veut aussi dire en acheter un.

Alors oui.

Ça coûte des sous en général.

Par exemple, le mien (charlesbordet.com) me coûte $14.99 par an.

Les .fr coûtent moins chers que les .com en général. J’en ai un qui me coûte 6.99€ par an par exemple.

Vous pouvez aussi trouver des noms de domaine un peu exotiques vraiment pas chers à moins de 2€ par an.

J’achète les miens sur Hover.com. Ils sont spécialisés exclusivement sur les noms de domaine.

Ça fait des années que j’en ai chez eux et ça marche toujours bien.

Vous pouvez aussi utiliser OVH qui vend les .fr.

Du coup au niveau des screenshots je vais utiliser Hover, mais que ce soit OVH ou les autres, vous aurez à peu près la même interface.

Bon.

Alors supposons que vous soyez allé acheter votre nom de domaine.

Maintenant il faut faire le lien entre:

  1. Votre nouveau nom de domaine
  2. L’adresse IP de votre serveur

Comment on fait ça ?

Avec les DNS !

2. Paramétrer les DNS

DNS, ça veut dire Domain Name System.

C’est un des services fondamentaux des internets.

Et probablement aussi un des plus ignorés.

C’est ce qui vous permet de taper https://duckduckgo.com dans votre navigateur au lieu de l’adresse IP 23.21.193.169.

En gros, c’est ce qui rend l’internet un peu plus humain.

Et c’est exactement ce qu’on veut faire avec notre appli !

On veut que les gens tapent http://monapplishinysupercool.fr plutôt que 3.121.42.9:3838.

Votre vendeur de nom de domaine va vous donner accès à une interface où vous pouvez justement paramétrer ces DNS.

Sur Hover, il suffit de cliquer sur DNS :

DNS sur Hover

Il y a déjà des valeurs par défaut qui sont entrées (peut-être différentes des vôtres, peu importe).

Si vous voulez comprendre comment ça marche, je vous invite à lire la boîte bleue ci-dessous. Sinon, passez directement à la suite.

Boîte bleue : Comprendre vite fait les DNS

Chaque entrée DNS indique comment rediriger les utilisateurs quand ils tapent votre nom de domaine.

Alors pourquoi avoir plusieurs entrées ?

Parce que vous pourriez avoir datachamp.fr sur un premier serveur, puis shiny.datachamp.fr sur un autre serveur, et ainsi de suite. Pour chaque cas d'usage, il faut ajouter une nouvelle ligne.

La colonne TYPE peut prendre les valeurs suivantes : A pour rediriger le nom de domaine vers une adresse IP. Ou CNAME pour rediriger le nom de domaine vers un autre nom de domaine. Ou MX pour gérer les emails.

La colonne HOST correspond au sous-domaine. Un sous-domaine, c'est la partie avant le domaine. Par exemple, quand j'écris shiny.datachamp.fr, c'est un sous-domaine de datachamp.fr. On peut mettre une étoile * pour représenter tous les sous-domaines. Et on peut mettre un @ pour le domaine principal (donc datachamp.fr dans mon cas).

La colonne VALUE dépend du TYPE qu'on a choisit. Si on a choisit le TYPE A, alors il faut entrer une adresse IP.

Le reste n'a pas vraiment d'importance pour nous aujourd'hui.

Les valeurs par défaut de Hover indiquent ici que le domaine principal datachamp.fr et tous les sous-domaines *.datachamp.fr vont être redirigés vers l’adresse IP 64.98.145.30.

C’est pas tout à fait ce qu’on veut.

Moi j’aimerais avoir seulement le sous-domaine shiny.datachamp.fr redirigé vers l’adresse IP de mon serveur AWS.

Pour faire ça je retire les deux lignes de TYPE A, et j’en ajoute une nouvelle :

Créer une entrée DNS sur Hover

Si vous ne voulez pas utiliser un sous-domaine mais directement votre domaine principal, alors écrivez @ au lieu de shiny dans la case hostname.

Du coup à la fin j’ai un tableau qui ressemble à ça :

Mes nouveaux DNS sur Hover

À présent, à chaque fois que quelqu’un tape shiny.datachamp.fr dans son navigateur, il sera redirigé automatiquement vers mon serveur AWS !

Top !

Mais c’est pas fini.

Si vous avez essayé, vous vous êtes aperçu que ça ne marchait pas.

Pourquoi ?

Parce qu’on n’a pas spécifié le port 3838 !

En fait, on a toujours besoin d’écrire shiny.datachamp.fr:3838 pour accéder à l’appli.

Uh…

C’est pas terrible ça.

On n’a jamais vu une URL avec des chiffres au bout comme ça. Ça fait pas sérieux.

Bon.

On s’approche quand même.

Il y a deux méthodes pour résoudre ce problème :

  1. La méthode rapide et sale.
  2. La méthode que c’est la bonne sur le long terme.

La méthode rapide et sale

Si vous voulez juste expérimenter et vous avez besoin d’une solution rapide, c’est facile :

  1. Connectez-vous en SSH sur votre serveur AWS (voir partie 3 si vous avez déjà oublié comment faire)
  2. Ouvrez votre fichier de config : sudo nano /etc/shiny-server/shiny-server.conf.
  3. Changez la ligne listen 3838 en listen 80.
  4. Redémarrez le serveur Shiny pour prendre en compte les changements : sudo systemctl restart shiny-server.

Et voilà !

Note: N’oubliez pas d’ouvrir le port 80 au niveau du pare-feu de AWS. Voir le chapitre Ouvrir le port sur le pare-feu.

Le changement, c’est que le serveur Shiny écoute sur le port 80 au lieu de 3838.

Pourquoi 80 ?

80 c’est le port par défaut pour le protocole HTTP (celui que tous les sites web utilisent).

En fait, quand on tape shiny.datachamp.fr dans le navigateur, on utilise implicitement le port 80 sans avoir besoin de le spécifier.

Et si on veut spécifier un autre port, comme 3838, il faut l’écrire explicitement.

Du coup, ça marche bien..

Alors pourquoi “rapide et sale” ?

En fait, si demain vous avez besoin d’installer un autre service sur votre serveur, comme un serveur RStudio, un Jupyter notebook, ou même un site web, vous aurez le même problème.

Et le problème, c’est que seulement un seul service à la fois peut utiliser le port 80.

Du coup, si vous avez à la fois un Shiny Server et un RStudio Server, ils doivent forcément utiliser deux ports différents.

On ne peut pas tous les deux les mettre sur le port 80.

Alors que vous aimeriez bien avoir deux URL bien clean : shiny.datachamp.fr et rstudio.datachamp.fr.

La solution, c’est d’utiliser un reverse proxy qui s’appelle Nginx.

3. Mettre en place un reverse proxy

Bon.

On entre dans des considérations un peu plus compliquées.

Je pourrais juste vous donner les commandes à écrire, et puis youplaboum article terminé.

Sauf que c’est pas tout à fait ma méthode d’enseignement.

Donc je vais tout vous expliquer.

En plus comme ça si demain vous avez besoin de changer quelque chose, vous comprendrez ce que vous faites.

Commençons par..

C’est quoi le rivet sporxi ?

Reverse proxy.

Voyez votre serveur comme une grande maison qui a plein de portes.

65535 portes exactement.

Et au lieu de dire portes, on va dire ports, ok ?

Si vous voulez parler à quelqu’un dans cette maison, il faut passer par la bonne porte.

Par exemple, M. Shiny Server est dans la maison, et il habite à la porte 3838.

Et puis M. RStudio Server est à la porte 8080.

Mais si vous allez à la porte 3322, vous ne trouverez personne, parce que personne n’y habite.

Sans le Reverse Proxy

Ça ça marche.

Mais c’est pas pratique parce qu’il faut à chaque fois indiquer la bonne porte.

Comme on disait avant, il faut spécifier le numéro du port dans l’URL, comme dans shiny.datachamp.fr:3838.

Ce qui serait pratique, c’est si on avait Nestor à l’entrée principale qui pourrait nous guider directement à la bonne personne.

Du coup, pas besoin de spécifier une porte particulière, on va juste à l’entrée principale, on demande M. Shiny Server, et il vient à nous !

Nestor

Bien mieux.

Et ce Nestor, en fait il s’appelle Nginx, et c’est ce qu’on appelle un reverse proxy.

Avec le Reverse Proxy

Au lieu de taper le numéro de port, on tape seulement shiny.datachamp.fr, et le premier interlocuteur au niveau du serveur sera Nginx.

Nginx va comprendre qu’on veut accéder au Shiny Server, et il va nous guider au bon endroit.

Techniquement, il nous reste donc seulement à :

  • Installer Nginx.
  • Configurer Nginx.

Installer Nginx

Ça c’est l’étape facile.

Connectez-vous en SSH sur votre instance AWS (de nouveau, voyez la partie 3 si vous avez oublié), puis tapez :

$ sudo apt install nginx

Et Nginx est installé.

Facile !

Configurer Nginx

Pour la partie un peu plus délicate..

D’abord, changez de répertoire pour aller dans les dossiers de Nginx :

$ cd /etc/nginx
$ ls
auth          fastcgi_params  mime.types         proxy_params     snippets
conf.d        includes        modules-available  scgi_params      uwsgi_params
dhparam.pem   koi-utf         modules-enabled    sites-available  win-utf
fastcgi.conf  koi-win         nginx.conf         sites-enabled

La commande ls permet de faire la liste de tous les fichiers et dossiers dans ce répertoire.

Ce qui nous intéresse, c’est le fichier nginx.conf, mais on ne va pas y toucher.

À la place, on va rajouter un nouveau fichier séparé dans sites-available :

$ cd sites-available
$ sudo nano shiny.conf

Et c’est là qu’on va écrire la configuration :

server {
    # listen 80 signifie que le serveur Nginx écoute sur le port 80.
    listen 80;
    listen [::]:80;
    # À remplacer avec votre nom de domaine.
    server_name shiny.datachamp.fr;
    # La partie reverse proxy :
    location / {
        proxy_pass http://localhost:3838;
        proxy_redirect http://localhost:3838/ $scheme://$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;
    }
}

C’est pas très long, mais ça peut faire un peu peur.

listen

Au début, on dit que le serveur Nginx écoute sur le port 80.

Comme il n’y a qu’un seul service qui peut être sur ce port, on va mettre Nginx, et c’est lui qui va nous rediriger vers tous les autres services.

En gros, c’est notre Nestor à l’entrée principale de la maison.

server_name

La partie server_name indique ce que l’utilisateur tape dans son navigateur.

Ici je veux que l’utilisateur accède à l’appli Shiny sur le sous-domaine shiny.datachamp.fr.

Évidemment, remplacez la valeur avec votre nom de domaine.

location

Et enfin, tout ce qui est dans location correspond au reverse proxy.

On donne les instructions à Nestor :

Si l’utilisateur arrive et demande shiny.datachamp.fr, alors guide-le dans la maison à la porte 3838.

Et c’est tout !

Vous pouvez même réutiliser ce fichier de config pour un RStudio Server.

Ajoutez un deuxième fichier nommé rstudio.conf et copiez/collez tout. Il faut remplacer deux choses :

  1. Le server_name
  2. Le numéro de port

..

C’est presque fini mais il reste un petit détail.

Enregistrez le fichier et retournez dans le terminal.

Il faut qu’on crée un raccourci dans le dossier sites-enabled :

$ cd ../sites-enabled
$ sudo ln -s ../sites-available/shiny.conf .

Pourquoi on fait ça ?

En fait, Nginx ne regarde pas dans le dossier sites-available.

Il regarde seulement le dossier sites-enabled.

Donc on crée le fichier de config dans sites-available, et ensuite on active la config en créant un raccourci dans sites-enabled.

L’idée c’est que si demain vous souhaitez temporairement désactiver l’accès à Shiny, il suffit seulement de supprimer le raccourci.

Alors que sinon, il faudrait supprimer tout le fichier de config, et après c’est pas vraiment pratique pour le remettre.

Ok.

Il ne reste plus qu’à redémarrer Nginx pour que les changements soient pris en compte.

D’abord :

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Ça permet de vérifier si la syntaxe du fichier de config est correcte.

Si jamais vous avez une erreur du type nginx: [emerg] unknown "connection_upgrade" variable, c’est que les variables $http_upgrade et $connection_upgrade n’ont pas été déclarées.

Dans ce cas, ouvrez le fichier /etc/nginx/nginx.conf puis ajoutez les lignes suivantes à l’intérieur du bloc http :

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
} 

Ensuite réessayez de valider la syntaxe avec sudo nginx -t.

Puis :

$ sudo systemctl restart nginx

Bravo !

Votre appli Shiny est à présent accessible à un vrai nom de domaine du type https://shiny.datachamp.fr/051-movie-explorer/ !

C’était pas très facile à la fin, mais c’est vraiment la meilleure manière de procéder.

En plus, il y a d’autres avantages..

Comme par exemple la possibilité de sécuriser l’appli en HTTPS !

6. Extra : Sécuriser votre application en HTTPS

On a maintenant changé l’adresse IP un peu moche de notre appli Shiny en bon nom de domaine et on est capable d’accéder à l’application en tapant une URL du type http://shiny.datachamp.fr/051-movie-explorer.

Très cool.

Sauf qu’il reste un vrai problème si vous voulez mettre votre application en production pour un client.

L’appli n’est pas sécurisée.

Vous avez peut-être remarqué le message sympa que vous laisse votre navigateur :

Shiny AWS HTTP non sécurisé

Votre application utilise en effet le HTTP, un protocole de communication non chiffré, alors qu’aujourd’hui, tout le monde utilise HTTPS, l’équivalent chiffré et sécurisé.

Dans ce chapitre, on va voir :

  • Qu’est-ce que le HTTPS et pourquoi on a besoin d’y passer
  • Comment créer des certificats SSL/TLS automatiquement et gratuitement avec Let’s Encrypt
  • Comment installer ces certificats sur le serveur
  • Comment forcer l’utilisation de HTTPS

Le HTTPS, c’est quoi ?

Vous en avez sans doute entendu parler sans savoir exactement comment le https fonctionnait ni pourquoi il était autant nécessaire.

Imaginez qu’à chaque fois que vous vous connectez à un site web, vous envoyez une requête de chez vous jusqu’au data center où le serveur est hébergé, puis une réponse revient chez vous.

Ces messages font parfois le tour du monde et passent de main en main, de routeur en routeur, avant d’arriver à destination.

Quand on utilise le HTTP, ces messages ne sont pas chiffrés.

Donc n’importe qui peut à tout moment intercepter le message, l’ouvrir, le lire, et même le modifier avant de le renvoyer sans que personne ne le sache.

Shiny AWS Man in the Middle Attack

On appelle ça une Man-in-the-Middle attack .

Pas très cool, d’autant plus quand le message contient des mots de passe, des data confidentielles ou votre code de carte bleue.

C’est pour éviter ça que le HTTPS est apparu.

Le HTTPS garantit que les messages seront chiffrés. Si une personne intercepte le message, elle ne pourra pas le lire.

Au départ, le HTTPS était surtout utilisé par les commerces en ligne pour sécuriser vos informations bancaires lors de transactions, puis il a été généralisé à l’Internet entier.

C’est pour ça que votre navigateur vous met un gros panneau “ATTENTION DANGER !!!”.

Bref, si vous voulez :

  • que vos données soient sécurisées et chiffrées,
  • que votre client ou chef ne flippe pas en voyant un avertissement de la part du navigateur,

On va devoir utiliser le HTTPS.

Comment ça se passe ?

Il faut d’abord obtenir des certificats (SSL/TLS) pour le serveur et des clés.

Je vous passe les détails techniques de cryptographie, mais en gros le certificat permet de dire « Oui c’est bien moi le vrai serveur ! » et il contient une clé de chiffrement.

Créer un certificat SSL/TLS avec Let's Encrypt

Les certificats s’obtiennent auprès d’institutions de confiance.

La plupart du temps, c’est payant.

Si on utilise Let’s Encrypt, c’est gratuit et automatique.

Donc je vous propose d’utiliser Let’s Encrypt.

Il existe même un programme qui permet de renouveler les certificats automatiquement (parce qu’ils ont une date de péremption, oui).

Donc on le fait une fois, et après on est tranquille !

Pour commencer, allons sur le site de Cerbot:.

Dans les cases en haut, on choisit :

  1. Software = Nginx
  2. System = Ubuntu 18.04 (prenez celui que vous utilisez évidemment)

Shiny AWS Certbot

Ensuite descendez dans la page et copiez/collez les instructions dans la console de votre serveur. Vous pouvez jeter un œil sur le chapitre Installer R et R Shiny sur le serveur) si vous avez déjà oublié comment vous connecter à votre serveur.

Donc :

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository universe
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update

$ sudo apt-get install certbot python-certbot-nginx 

L’obtention du certificat est ensuite automatique en tapant la commande suivante :

$ sudo certbot certonly --nginx -d shiny.datachamp.fr

Évidemment, modifiez le nom de domaine pour renseigner le vôtre.

Quelques lignes vont s’afficher en parlant de challenges :

Renewing an existing certificate
Performing the following challenges:
http-01 challenge for shiny.datachamp.fr
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/shiny.datachamp.fr/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/shiny.datachamp.fr/privkey.pem
   Your cert will expire on 2020-02-04. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

Les lignes les plus importantes sont celles qui vous informent que les certificats ont été enregistrés dans le dossier /etc/letsencrypt/live/shiny.datachamp.fr.

On va pouvoir s’en servir pour passer notre appli en HTTPS !

Comment installer un certificat SSL/TLS avec nginx

Pour mettre en place ce qui suit, il faut que vous ayez suivi les recommandations du précédent chapitre de ce guide sur la création d’un nom de domaine, notamment la dernière section : Mettre en place un reverse proxy.

Si vous ne l’avez pas encore mis en place, je vous invite à le faire et revenir ensuite.

Dans cette section, on avait installé le logiciel nginx.

Ce logiciel, c’est tout simplement ce qu’on appelle un serveur web. L’une de ses fonctionnalités, c’est le reverse proxy (ce qu’on avait donc vu la dernière fois).

Rappelez-vous, on avait dit que nginx agissait comme le gardien du serveur : il écoute sur le port 80, et il redirige les visiteurs vers le bon port (3838 pour Shiny par exemple).

Le port 80, c’était pour le protocole HTTP.

Comme on va passer en HTTPS, ce port va changer : à la place, on va utiliser le port 443.

Chaque protocole a son port dédié, ce n’est pas nous qui pouvons décider. Par exemple, SSH est sur le port 22, FTP sur le port 21, etc.

Vous pouvez trouver une liste de ces ports logiciels sur Wikipédia.

Ils sont pris jusqu’au port 1024, et tout ceux qui sont au-delà sont libres. C’est pour cette raison que Shiny s’octroie le port 3838 par défaut (mais on pourrait le changer).

Bref.

Reprenons la configuration de nginx. On va changer le port 80 en 443.

Pour rappel, le fichier de configuration se trouve au chemin suivant :

sudo nano /etc/nginx/sites-available/shiny.conf

Vous devriez retrouver le fichier qu’on avait écrit la dernière fois.

On va rajouter et modifier quelques points.

1. Changement de port

D’abord, on change le port.

Les deux lignes qui commencent par listen vont être remplacées par celles-ci :

# listen 443 signifie que le serveur Nginx écoute sur le port 443 (HTTPS).
listen 443 ssl http2;
listen [::]:443 ssl http2;

2. Configuration du SSL

On va ensuite ajouter les paramètres de SSL.

SSL, c’est le protocole qui permet de chiffrer vos données. Dites-vous que HTTP + SSL = HTTPS.

Normalement, depuis 20 ans, on dit TLS, mais tout le monde continue de dire SSL.

Je vous le dis tout de suite : je ne suis pas un expert de cryptographie ni de SSL.

Donc quand il s’agit de configurer de manière optimale, je vais chercher l’information d’une source sûre.

Ici, j’utilise les recommandations de Mozilla. Ils ont créé un SSL Configuration Generator.

On va donc rajouter les lignes suivantes dans notre fichier de config :

# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
ssl_session_tickets off;

# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem
ssl_dhparam /path/to/dhparam.pem;

# intermediate configuration
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;

# verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;

Dans cette config, il va falloir modifier certaines choses.

On commence avec les deux premières lignes ssl_certificate et ssl_certificate_key. Il faut leur donner les chemins des certificats générés plus tôt.

Je les modifie ainsi :

ssl_certificate /etc/letsencrypt/live/shiny.datachamp.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shiny.datachamp.fr/privkey.pem;

(Bien entendu, remplacez avec votre nom de domaine)

3. Télécharger les paramètres DH

On va ensuite s’attaquer aux paramètres DH. Le commentaire nous dit de les télécharger à partir d’une URL. C’est donc ce qu’on va faire avec l’instruction suivante dans la console :

$ curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/nginx/snippets/dhparam.pem;

Et il faut ensuite modifier le chemin spécifié dans ssl_dhparam :

ssl_dhparam /etc/nginx/snippets/dhparam.pem;

Je vous conseille de désactiver la partie HSTS pour l’instant.

Pourquoi ? Car le HSTS va permettre de dire au navigateur : « À partir d’aujourd’hui, et pendant deux ans, tu utilises toujours le HTTPS et jamais le HTTP. ».

C’est une bonne pratique de sécurité.

Sauf que si vous avez des problèmes pour mettre en place le HTTPS et que vous souhaitez revenir au HTTP, vous allez être coincé.

Donc désactivons-le pour le moment, et lorsqu’on sera sûr que la config fonctionne bien, on le remettra. Je rajoute simplement un # devant :

# add_header Strict-Transport-Security "max-age=63072000" always;

4. Utiliser le certificat Let’s Encrypt

Il ne nous reste plus qu’à modifier la dernière ligne avec un certificat de Let’s Encrypt :

ssl_trusted_certificate /etc/letsencrypt/live/shiny.datachamp.fr/chain.pem;

Et voili voilou !

À présent, enregistrez la configuration, on va tester si tout fonctionne.

Pour vérifier que vous n’avez pas fait d’erreur, tapez dans la console :

sudo nginx -t

Vous devriez recevoir le message suivant :

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Si ce n’est pas le cas, c’est que vous avez fait une erreur quelque part.

Une fois la syntaxe validée, on redémarre le serveur nginx :

sudo systemctl restart nginx

À présent, il ne reste plus qu’à tester.

Rendez-vous sur votre nom de domaine en tapant bien https au début, par exemple : https://shiny.datachamp.fr/051-movie-explorer.

Note: N’oubliez pas d’ouvrir le port 443 au niveau du pare-feu de AWS. Voir le chapitre Ouvrir le port sur le pare-feu.

Votre navigateur va vous afficher un beau cadenas avec un message rassurant :

Shiny AWS HTTPS sécurisé

Bravo !

Mais ce n’est pas terminé. Il nous reste encore une dernière étape : interdire le HTTP et forcer l’utilisation du HTTPS.

Comment forcer l’utilisation de HTTPS

Un peu plus haut, je vous ai recommandé de désactiver le HSTS (l’utilisation automatique du HTTPS).

On va à présent réactiver cette option.

Dans la config nginx, on retire le # ajouté plus tôt :

add_header Strict-Transport-Security "max-age=63072000" always;

Et on va faire une deuxième modification.

Au niveau du générateur de configuration SSL de Mozilla, vous avez peut-être remarqué un autre bloc server ajouté en haut de leur fichier.

Ce bloc serveur va écouter sur le port 80, c’est-à-dire HTTP, et automatiquement rediriger vers le port 443.

Il permet aux utilisateurs qui viennent sur votre appli en HTTP de ne pas tomber sur une page 404 (ben oui, parce que plus personne n’écoute sur le port HTTP à présent).

À la place, on leur dit gentiment d’aller voir en 443 si on y est.

Donc tout en haut du fichier de config nginx, on va rajouter les lignes suivantes :

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

    # redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;
}

Et là, c’est bon. On a activé le HTTPS, notre appli est sécurisée, et les données qu’on envoie sont chiffrées.

N’oubliez pas de redémarrer nginx à chaque fois que vous faites un changement :

sudo systemctl restart nginx

Vous aurez remarqué que plus on avance dans ce guide, plus on s’éloigne de R et plus on rentre dans le domaine de l’administration système.

Perso, je trouve que c’est un domaine super intéressant.

Grâce à Shiny, j’ai appris énormément sur les serveurs et même sur le fonctionnement des internets en général. Et ce n’est pas fini !

7. Extra : Protéger votre application avec un mot de passe.

Cette fois-ci, on va apprendre à protéger notre application derrière un portail d’authentification.

La plupart des applications Shiny que j’ai eu l’occasion de créer pour mes clients étaient des applications internes.

Ça veut dire qu’on utilise des données de l’entreprise, confidentielles, et le dashboard est utile à une équipe en particulier. Mais ce n’est pas fait pour être ouvert au grand public.

Dans certaines entreprises, on dispose d’un réseau interne, parfois d’un VPN, et alors c’est facile d’isoler le dashboard de l’internet public.

Mais parfois, ce n’est pas possible.

Ou alors, on veut donner l’accès à un public plus large, et on veut pouvoir gérer des utilisateurs et des droits d’accès.

Les besoins sont multiples.

Dans ce chapitre, on va voir comment répondre à ces besoins, du plus simple au plus complexe :

  1. Comment mettre un mot de passe unique avec nginx
  2. Comment utiliser un service tiers comme auth0
  3. Comment créer votre propre portail d’authentification maison

Avant de commencer, j’aimerais ajouter un petit avertissement.

Dans ce chapitre, on va parler sécurité. Je ne suis pas un expert en sécurité informatique, c’est un vrai métier et ce n’est pas le mien.

Vous restez responsable de la sécurité de vos applications.

Ce chapitre est purement informatif!

À présent que ceci est précisé, allons-y !

Les choix possibles

Dans un premier temps, je vais vous présenter succinctement les choix à votre disposition. Je vais ensuite décrire comment mettre en place chacun d’eux.

En gros, vous avez le choix entre :

Utiliser Shiny Server Pro

Haha.

Je plaisante.

Utiliser votre serveur web nginx

Shiny AWS create application

Dans les précédents chapitres, on a utilisé nginx comme reverse proxy.

nginx agit comme un gardien à l’entrée du serveur et redirige les visiteurs vers le bon service, comme notre Shiny Server en l’occurrence.

Une étape supplémentaire logique serait d’utiliser ce gardien pour autoriser ou refuser certains visiteurs.

Plutôt que de laisser passer tout le monde, il pourrait demander un mot de passe aux visiteurs et ne donner l’accès qu’à ceux disposant d’identifiants valides.

C’est une solution qui marche très bien, qui est très facile à mettre en place, et qui offre en plus une bonne sécurité puisqu’une partie du code de l’application n’est pas chargée tant qu’on n’est pas authentifié.

Utiliser un service tiers comme auth0

Shiny AWS create application

auth0, c’est une surcouche d’authentification que vous pouvez intégrer dans n’importe quelle application.

Vous le mettez en place, et c’est lui qui se chargera de gérer les utilisateurs et les droits d’accès.

C’est gratuit jusqu’à 7000 utilisateurs (pas mal !).

Et l’outil s’intègre automatiquement avec Shiny.

Le gros avantage, c’est que vous n’avez pas à vous soucier de la sécurité.

Ce sont eux qui s’en occupent, c’est leur spécialité, donc on peut imaginer qu’ils font ça mieux que nous.

Maintenant, ça reste un service tiers, ce qui crée une dépendance pas toujours désirable.

Utiliser une solution maison

Shiny AWS create application

Finalement, la dernière alternative que je vous propose : le faire vous-même.

Si vous êtes arrivé jusqu’ici dans ce guide, c’est que vous aimez quand même un peu vous faire du mal faire les choses par vous-même.

Il est important de noter toutefois que cette solution est celle qui apporte le moins de sécurité, puisque vous n’êtes pas un expert en sécurité, et moi non plus.

En plus, on ne sait pas trop si Shiny sécurise correctement les données ou non.

Imaginez par exemple que vous ajoutiez un portail d’authentification au début de votre appli Shiny.

Et ensuite, vous écrivez dans le code quelque chose du genre : « Si l’utilisateur se login, on montre le reste de l’application. »

Du coup, ça implique d’accéder au Shiny Server pour pouvoir se logger.

Mais est-ce que le reste du code est caché tant qu’on n’est pas loggé ?

Est-ce qu’il n’y a pas moyen de manipuler la console Javascript ou une autre backdoor pour accéder aux données ?

Est-ce que votre formulaire d’authentification est résistant à l’injection SQL ?

Les réponses à ces questions restent un peu floues, et même si on s’assure de pouvoir répondre à certaines, on ne sera jamais sûr à 100% d’avoir tout sécurisé correctement.

De mon point de vue, le gros problème, c’est qu’on a accès au Shiny Server pour pouvoir se logger. On charge déjà du code Shiny.

Dans la solution du serveur nginx, par exemple, le Shiny Server n’est pas accessible tant qu’on n’a pas passé l’étape d’authentification.

Et c’est généralement une bonne pratique de séparer le serveur d’authentification (le rôle que joue nginx par exemple) et le serveur applicatif.

Mélanger les deux, c’est chercher des ennuis.

Donc cette solution est très bien si vous voulez restreindre l’accès sans avoir une sécurité au top level.

Elle est relativement simple à mettre en place, d’autant plus que vous connaissez déjà Shiny, donc c’est très flexible si vous voulez créer des comptes admin, gérer des droits utilisateurs, etc. Ce que ne permet pas nginx.

Mais gardez en tête que faire appel à un vrai professionnel de la sécurité peut être une bonne idée aussi.

À présent, je vais détailler chacune de ces solutions.

Utiliser nginx comme serveur d’authentification

Cette solution est la plus simple à mettre en place, mais est aussi la moins flexible.

En gros, vous allez d’abord choisir et enregistrer les logins et mots de passe au niveau du serveur.

Ensuite, vous aurez un popup pas très accueillant à chaque connexion.

C’est un peu la solution rapide et pas chère.

Mais c’est très efficace si vous avez une appli que vous voulez montrer seulement à certaines personnes.

Par exemple, quand vous développez une application pour un client et que vous voulez lui montrer sans la rendre accessible au grand public. Généralement, le plus simple est de la mettre sur votre serveur, de paramétrer un mot de passe et de la partager à votre client.

De nouveau, on va devoir mettre les mains dans notre serveur et sa console.

D’abord, les prérequis : vous devez avoir suivi les deux chapitres précédents :

Vous devriez avoir un fichier de config (celui qui se trouve à /etc/nginx/sites-available/shiny.conf) qui ressemble à ça (à quelques détails près) :

server {
    listen 80;
    listen [::]:80;
    server_name shiny.datachamp.fr;
    server_tokens off;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    root /dev/null;
    server_tokens off;
    client_max_body_size 0;

    server_name shiny.datachamp.fr;

    access_log /var/log/nginx/shiny-access;
    error_log /var/log/nginx/shiny-error;

    location / {
        proxy_pass http://localhost:3838;
        proxy_redirect http://localhost:3838/ $scheme://$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;
    }

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    ssl_certificate /etc/letsencrypt/live/shiny.datachamp.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shiny.datachamp.fr/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem
    ssl_dhparam /etc/nginx/dhparam.pem;

    # intermediate configuration
    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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /etc/letsencrypt/live/shiny.datachamp.fr/chain.pem;
}

C’est un gros fichier qui fait un peu peur, mais comme on y est allé petit à petit, il ne fait pas si peur que ça.

Bon maintenant, on va devoir rajouter un nouveau bloc location qui est quasiment identique à celui qui existe déjà mais avec deux petites différences :

  1. D’abord la location en tant que telle ne sera plus à la racine (c’est-à-dire /) mais sur le dossier /051-movie-explorer.
  2. Et ensuite, on va rajouter de quoi mettre un mot de passe.

Voici à quoi ressemble le nouveau bloc que vous pouvez ajouter juste en dessous de l’autre :

location /051-movie-explorer {
    proxy_pass http://localhost:3838;
    proxy_redirect http://localhost:3838/ $scheme://$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;
    auth_basic "Restricted Content";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

Pourquoi rajouter une location ?

En fait, on pourrait seulement rajouter les deux lignes suivantes au précédent bloc location :

auth_basic "Restricted Content";
auth_basic_user_file /etc/nginx/.htpasswd;

Sauf que dans ce cas, toutes vos applications Shiny seront protégées par les mêmes mots de passe.

Ce n’est pas forcément ce qu’on veut.

On peut vouloir avoir certaines applications protégées, et d’autres accessibles.

Là, ça permet de choisir.

Enregistrez le fichier.

Maintenant, on va devoir remplir le fichier /etc/nginx/.htpasswd avec les identifiants.

C’est très simple. D’abord, lancez cette instruction pour créer l’identifiant :

$ sudo sh -c "echo -n 'charles:' >> /etc/nginx/.htpasswd"

Vous pouvez remplacer charles par le login que vous voulez.

Ensuite, la commande suivante vous permet d’entrer un mot de passe et de le stocker de manière chiffrée. On ne stocke jamais un mot de passe en clair ! :

$ sudo sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd"

Et voilà !

Vous pouvez répéter l’opération autant de fois que nécessaire pour créer davantage de combinaisons login & mot de passe.

À présent, on redémarre le serveur nginx :

$ sudo nginx -t
$ sudo systemctl restart nginx

Et maintenant, votre application va être protégée.

Vous pouvez essayer mon application à l’adresse https://shiny.datachamp.fr/movie-explorer-secure/ et vous remarquerez qu’une fenêtre d’authentification s’affiche.

Pas d’identifiant ? Pas d’accès.

Essayez avec :

  • login = guest
  • mot de passe = trololo

Et vous devriez pouvoir accéder à l’application !

Notez que bien que cette technique soit plutôt bien sécurisée, il faut absolument forcer le passage par nginx pour que ça marche.

Donc bloquez le port 3838 avec votre pare-feu (celui de AWS suffit, ou bien utilisez ufw).

Notez aussi que vous n’êtes pas protégé face à une attaque de type bruteforce.

Un mot de passe de seulement 8 caractères se casse en quelques secondes.

Donc soit vous utilisez des mots de passe plus longs, soit vous passez par l’outil fail2ban pour bannir les adresses IP qui font plusieurs tentatives de login infructueuses.

Maintenant, bien que cette technique soit très facile à mettre en place et offre une bonne sécurité, tout n’est pas parfait.

D’abord : C’est compliqué de créer de nouveaux comptes.

On n’a pas d’interface, il faut passer par le serveur, etc. On a vu plus simple.

Et puis, ça ne s’automatise pas facilement.

Ensuite : Ce n’est pas franchement convivial.

Franchement vous verriez un site e-commerce utiliser une fenêtre moche comme ça pour donner accès à un compte client ?

Non.

Et enfin : Ça ne répond pas à tous les besoins.

C’est soit on accède, soit on n’accède pas. Pas de gestion de compte ni de droits d’accès. On ne peut même pas savoir quel login a été utilisé pour accéder à l’application Shiny.

Du coup…

On peut essayer autrement.

Utiliser auth0 comme serveur d’authentification

Cette deuxième solution est un peu plus compliquée à mettre en place, mais elle a de gros avantages :

  • Sécurité optimale
  • Gestion d’utilisateurs
  • Simplicité

Auth0 est un fournisseur de services d’authentification et d’autorisation.

Plutôt que de créer nous-même une application d’authentification, Auth0 va s’en charger à notre place via un SDK (ou un appel à leur API).

Mettre en place Auth0

Première étape : Créer un compte Auth0.

Naviguez sur https://auth0.com/signup et créez un compte.

Vous pourrez utiliser le service gratuitement jusqu’à un maximum de 7000 utilisateurs.

Une fois le compte créé, connectez-vous et cliquez sur le gros bouton CREATE APPLICATION :

Shiny AWS create application

Donnez-lui un nom puis choisissez Regular Web Applications :

Shiny AWS create application

Vous ne trouverez pas Shiny dans les applications proposées par défaut, donc on va devoir configurer notre portail manuellement. Cliquez sur Settings

Les informations importantes à noter sont :

  • Domain
  • Client ID
  • Client Secret

On en aura besoin un peu plus tard.

Un peu plus bas, dans la case Allowed Callback URLs, vous devez remplir votre URL suivi de callback. Donc pour moi, j’ai rempli https://shiny.datachamp.fr/callback.

Attention : Pas de slash / à la fin !

À présent, vous pouvez enregistrer les changements.

Maintenant, il va falloir installer le portail d’authentification sur votre serveur. Connectez-y vous via SSH, comme d’habitude, puis effectuez les étapes suivantes :

1. Installez NodeJS

$ sudo apt update
$ sudo apt install curl
$ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
$ sudo apt install nodejs

2. Installez l’application de Auth0

$ git clone git@github.com:auth0/shiny-auth0.git
$ cd shiny-auth0
$ npm install

3. Paramétrez

Il ne reste plus qu’à paramétrer l’application Auth0 pour que le service utilise les paramètres que vous avez créé tout à l’heure dans votre compte !

Créez un fichier nommé .env (en tapant par exemple nano .env) et remplissez-le des informations suivantes :

AUTH0_CLIENT_SECRET=votreClientSecretAuth0
AUTH0_CLIENT_ID=votreClientIDAuth0
AUTH0_DOMAIN=votreDomainAuth0
AUTH0_CALLBACK_URL=https://shiny.datachamp.fr/callback
COOKIE_SECRET=r7QVJVVxj77CJyuX5tGrE238uLGGh8XZb84c3WdvWgQ5RPVwYc
SHINY_HOST=localhost
SHINY_PORT=3838
PORT=3000

Pour les quatre premiers, ce sont les informations qu’on a récupérées tout à l’heure dans votre compte Auth0.

Le COOKIE_SECRET doit être une longue chaîne de caractères aléatoires. J’ai utilisé un générateur de mots de passe pour ça.

Ensuite, laissez ce qui reste tel quel.

Pour l’instant, notre application Shiny n’est toujours pas sécurisée.

En effet, Shiny écoute sur le port 3838, tandis que le portail d’authentification est une autre application qui écoute sur le port 3000.

Or, avec nginx, on dit de rediriger https://shiny.datachamp.fr vers le port 3838. Donc on court-circuite le portail d’authentification !

Comment résoudre ce problème ?

Eh bien tout simplement en disant à nginx de rediriger directement vers le port 3000 et de ne pas donner l’accès direct au port 3838.

Pour ça, il suffit de changer les instructions proxy_pass et proxy_redirect dans votre fichier de configuration nginx :

proxy_pass http://localhost:3000;
proxy_redirect http://localhost:3000/ $scheme://$host/;

Puis on enregistre la configuration :

$ sudo nginx -t
$ sudo systemctl restart nginx

Si vous essayez à présent d’accéder à votre application, ça ne va plus fonctionner. C’est parce que nous n’avons pas démarré le portail d’authentification.

Pour ça, rendez-vous dans le dossier shiny-auth0 et lancez la commande suivante :

$ cd /home/charles/shiny-auth0
$ node bin/www

Vous remarquerez que la console n’affiche rien et vous n’avez plus la main.

En effet, la commande qu’on a écrite permet de démarrer l’application du portail d’authentification mais c’est un démarrage manuel. C’est pourquoi on n’a plus la main.

On peut néanmoins tester si le portail fonctionne.

Connectez-vous sur votre application Shiny. La fenêtre suivante devrait s’afficher :

Shiny AWS create application

Vous pouvez créer un compte, vous logger, puis accéder à l’application.

Pour reprendre la main dans la console, vous pouvez arrêter Auth0 avec Ctrl + C.

Super mais bon… Il reste quand même deux problèmes :

  1. Ce démarrage manuel est un peu bizarre. Il faudrait qu’on garde toujours la console ouverte ?
  2. Le but est de limiter l’accès, pas d’offrir la possibilité à n’importe qui d’ouvrir un compte !

On va s’en occuper.

Créer un service pour Auth0

On a déjà des “serveurs web” sur notre machine :

  • Shiny Server, qui est un serveur web qui écoute sur le port 3838.

  • nginx, qui est aussi un serveur web qui écoute sur le port 80.

Et là, Auth0 est un autre serveur web qui écoute sur le port 3000.

Alors pourquoi doit-on lancer Auth0 manuellement, et pas les autres ?

La raison : les autres sont des services. C’est-à-dire qu’ils sont lancés automatiquement dès le démarrage de la machine. Et la configuration du service a été automatique dès l’installation de ces serveurs web.

Pour Auth0, on va devoir créer manuellement le service, et tout sera ensuite automatique.

Pour ça, ce n’est pas très compliqué, vous allez simplement créer un nouveau fichier :

$ sudo nano /etc/systemd/system/shiny-auth0.service

Et ce fichier va contenir les informations suivantes :

[Service]
WorkingDirectory=/home/charles/shiny-auth0
ExecStart=/usr/bin/node /home/charles/shiny-auth0/bin/www
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=charles
User=charles
Group=charles
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Assurez-vous bien de remplacer le nom d’utilisateur charles par le vôtre !

En général, la bonne pratique est de créer un nouveau nom d’utilisateur spécifiquement pour chaque service (comme l’utilisateur shiny qui a été créé automatiquement pour le Shiny Server).

Grâce à ça, si le service est compromis, le reste du serveur n’est pas atteint.

Une fois cette configuration faite, il ne reste plus qu’à activer le service :

$ sudo systemctl enable shiny-auth0
$ sudo systemctl start shiny-auth0

À présent, votre portail d’authentification est en ligne, plus besoin de démarrage manuel !

Maintenant, l’autre problème : comment restreindre l’accès ?

Créer des règles de restriction

Pour cette partie-là, tout se passe dans l’interface de votre compte Auth0.

Vous aurez peut-être remarqué la section “Rules”, qui permet de créer des règles à l’aide de petits snippets Javascript. Après avoir cliqué sur CREATE RULE, vous accédez à plein de possibilités :

Shiny AWS create application

Vous pouvez par exemple :

  • Spécifier la liste des emails autorisés,
  • Interdire d’utiliser les Google, Facebook et cie pour créer un compte,
  • Vous connecter à une API pour vérifier si l’utilisateur est autorisé,
  • et créer vos propres règles personnalisées.

Je vous avoue que cette partie est un peu intimidante pour moi. Ma connaissance en Javascript reste très limitée, donc je préfère en rester aux règles pré-configurées.

Par exemple, pour un client qui veut que son appli ne soit accessible qu’à ses employées, limiter l’inscription aux emails du type xxx@nomdelentreprise.com est déjà très pratique !

D’un autre côté, le fait de pouvoir directement écrire les règles en Javascript rend le système de règles super flexible.

On peut très bien imaginer par exemple que vous vendiez votre application Shiny comme un produit SaaS, l’acheteur est ajouté à votre Mailchimp sur une liste spécifique, et votre règle utilise l’API de Mailchimp pour autoriser l’accès seulement aux acheteurs de cette liste.

D’ailleurs, l’inverse est aussi possible. Par exemple, l’accès à votre application Shiny est gratuite mais nécessite la création d’un compte. Puis les informations de ce compte sont automatiquement exportées vers Mailchimp pour que vous puissiez communiquer avec les utilisateurs.

Ça demande simplement un peu de Javascript.

Pour nous autres développeurs de Shiny, connaître un peu de Javascript ne peut pas faire de mal !

En tout cas, ce service Auth0 fait plutôt bien le job.

Mais pour ma part, je ne me vois pas trop l’utiliser, sauf dans des cas très spécifiques (comme les exemples que je viens de citer).

En fait :

  • Je n’aime pas trop que ça utilise NodeJS. Je ne connais pas du tout cette techno, et l’inconnu me fait peur.
  • Utiliser un service tiers me rend dépendant. Et si demain Auth0 décidait de changer les règles ?
  • Bien que le système de règles soit flexible, le portail ne l’est pas tellement. Et si je voulais créer une belle landing page ?

C’est pour ça que j’en arrive à la troisième et dernière solution : Créer votre propre portail d’authentification.

Comment créer votre portail d'authentification avec Shiny

Afin d’implémenter cette dernière solution, je vous propose un package appelé shinymanager.

Disponible sur le CRAN, vous pouvez l’installer (en local et sur votre serveur) facilement :

install.packages("shinymanager")

La page Github explique sommairement comment l’utiliser, la documentation reste encore très limitée : https://github.com/datastorm-open/shinymanager.

En descendant un peu sur la page, vous trouverez un exemple minimaliste pour ajouter le portail d’authentification.

Et ça marche :

Shiny AWS create application

Essayons de l’adapter à notre application sur l’explorateur de films (pour rappel, le code est disponible ici).

En lisant la page Github, on comprend qu’il faut faire les étapes suivantes :

  • Créer des identifiants,
  • Charger le package shinymanager,
  • Utiliser la fonction secure_app autour de l’UI,
  • Rajouter le module d’authentification dans la partie server.

C’est parti !

Créer des identifiants

C’est la partie la plus facile.

Je vais utiliser le même bout de code qu’eux pour rester simple pour le moment et je le mets dans le global.R :

credentials <- data.frame(
  user = c("shiny", "shinymanager"),
  password = c("azerty", "12345"),
  stringsAsFactors = FALSE
)

Alors oui, les identifiants sont écrits comme ça, en clair, pas chiffrés ni rien.

Vous vous doutez bien que ce n’est ni pratique ni sécurisé.

On verra un peu plus tard comment changer ça.

Charger le package shinymanager

Je le charge au début du fichier global.R, afin que les fonctions soient accessibles dans la partie serveur et dans la partie UI :

library(shinymanager)

Ajouter la fonction secure_app

Dans l’exemple de Github, l’appli minimaliste tient en un seul fichier.

Nous, on en a trois : global.R, server.R, et ui.R.

Les deux méthodes fonctionnent pour créer des applis Shiny.

Pas de souci ici.

Dans le fichier ui.R, repérez la partie UI. C’est le bout de code qui commence par fluidPage(, à la ligne 11.

Je rajoute la fonction secure_app :

secure_app(fluidPage(
  # Code à l'intérieur...
))

Et je n’oublie pas de refermer la parenthèse à la fin du fichier.

Ajouter le module d’authentification

Enfin, je vais juste copier/coller un bout de code au début de la fonction server, autour de la ligne 23 du fichier server.R :

# call the server part
# check_credentials returns a function to authenticate users
res_auth <- secure_server(
  check_credentials = check_credentials(credentials)
)

Et voilà !

Alors si on s’arrête là… ça ne va pas marcher.

En fait, l’appli va bugger à cause de ggvis.

Pour résoudre le problème, je lui dis juste de faire un graphe vide tant qu’on ne s’est pas connecté.

Je rajoute donc vis autour de la ligne 99, tout au début de la déclaration de la variable réactive :

vis <- reactive({
  if (is.null(input$xvar)) return(ggvis(data.frame(x = 0, y = 0), ~x, ~y))
  # Lables for axes
  # Reste du code...
})

Maintenant, c’est bon !

Vous pouvez voir le code mis à jour à l’adresse suivante : https://gitlab.datachamp.fr/charles/movie-explorer-login.

J’ai hébergé l’application sur mon serveur Shiny.

Et voilà le résultat : https://shiny.datachamp.fr/movie-explorer-login/.

Vous pouvez vous logger avec les identifants shiny/azerty par exemple.

Options supplémentaires

shinymanager ne s’arrête pas là heureusement.

Vous pouvez aussi :

  • avoir un mode admin pour gérer les utilisateurs,
  • utiliser une base de données chiffrée pour stocker les identifiants,
  • passer le portail en français.

Toutefois, tout n’est pas possible.

Par exemple, si vous voulez offrir une interface de création de compte pour vos utilisateurs… vous devrez le faire vous-même.

En fait, je trouve personnellement que shinymanager est un très bon point de départ.

Lorsque je crée des portails d’authentification pour mes clients, je suis les étapes suivantes :

  • Je vais chercher le code entier de shinymanager et l’ajoute à mon projet,
  • Je vire toutes les fonctionnalités dont je n’ai pas besoin (par exemple les logs ou l’interface d’admin),
  • Je rajoute les fonctionnalités dont j’ai besoin.

La première fois, ça m’a demandé de vraiment comprendre comment le package fonctionnait, notamment via l’utilisation de tokens d’authentification et une classe R6.

Mais c’était super intéressant et j’ai beaucoup appris !

Donc je recommande vraiment de passer du temps à comprendre ce package.

D’autant plus que comme la sécurité est un argument essentiel du portail d’authentification, le fait de comprendre les rouages de shinymanager vous permettra de comprendre aussi ses points forts et ses points faibles.

Par exemple le fait que le package ne hash pas les mots de passe avant de les stocker.. Pas bien !

Vous pouvez donc repartir de shinymanager et rajouter par exemple :

  • La possibilité de créer un compte,
  • L’utilisation de règles pour restreindre la création de compte (comme pour Auth0, mais vous l’écrivez en R cette fois-ci !),
  • Un service de “Mot de passe perdu”
  • Un système de cookies pour retenir la session de l’utilisateur
  • Le fait de hasher et saler les mots de passe
  • Et à peu près tout ce que vous voulez…

Au final, c’est une solution que j’aime beaucoup parce qu’elle est entièrement flexible.

Le seul point qui me dérange est la question de la sécurité.

Ce n’est pas mon expertise, je ne sais pas ce que je ne sais pas, et il faut se méfier très fortement de l’effet Dunning-Kruger (surestimer vos capacités en sécurité parce que vous avez lu 3 articles, en gros).

Donc si votre application brasse des données confidentielles, faites-vous accompagner par une personne compétente !

Comparatif final

Je vous ai récapitulé tous les avantages et inconvénients de chaque solution dans le tableau suivant :

Solution Prix Facilité de mise en œuvre Dépendance Sécurité Administration
Shiny Server Pro Cher Facile Indépendant Bonne Non
nginx Gratuit Facile Indépendant Bonne Non
Auth0 Gratuit jusqu’à 7000 utilisateurs Moyen Dépendant Bonne Oui
Solution maison Gratuit Difficile Indépendant Moyenne Oui

À présent, à vous de voir la solution la plus adaptée à votre situation !


Ce chapitre conclut le guide sur Comment déployer une application Shiny sur AWS.

J’espère que vous aurez trouvé ce guide utile ! Merci à tous ceux qui m’ont fait des retours et qui m’ont permis d’améliorer continuellement ce travail. Je compte le garder mis à jour régulièrement, donc n’hésitez pas à me faire part de tout changement que vous aurez remarqué !

Je sais qu’il y a encore plein d’autres sujets à traiter, comme le déploiement avec Docker, l’utilisation de shinyproxy.io, la scalabilité du déploiement, etc.

Ce sont des sujets que j’aborderai un peu plus tard, probablement dans un format différent.

Pour être tenu au courant de la publication de nouveaux articles, n’hésitez pas à vous inscrire ci-dessous !

Téléchargez le guide pour déployer une application Shiny

Disponible gratuitement au format PDF, je vous l'envoie directement par email :

    Pas de spam, promis. Vous pouvez vous désinscrire à tout moment.

    Mis à jour :

    Commentaires

    Ewen

    Merci encore pour ce tuto, il a été parfait pour le déploiement d’une première app ! Bonne continuation :-)

    Emmanuel Bonnet

    Super tuto très clair même pour un néophyte! Où l’on apprend comment déployer gratuitement (à un nom de domaine près si on veut aller jusqu’au bout) une appli shiny sur le web et en https! BRAVO Charles

    Florian

    Bonjour,

    Bien entendu merci pour cet excellent tuto. On développe actuellement notre appli shiny-server sur un serveur OVH et on souhaite utiliser un nom de domain plutôt que cette adresse ip avec les “:” 3838. En début de partie 4, vous dites : “Rappelez-vous, à la fin de l’article, on pouvait taper 3.121.42.9:3838 dans le navigateur et boum notre appli apparaissait !”

    Or la partie où l’on retire “:3838” dans l’adresse IP n’est pas explicité dans la partie 3. Notre app est directement stockée dans /srv/shiny-server, nous n’avons pas crée de raccourci comme suggéré dans votre tutoriel.
    La question est donc, comment passe-t-on de “ xxx.xxx.xx.xxx:3838” à “xxx.xxx.xx.xxx. Merci beaucoup

    Charles

    Hello Florian ! J’explique justement dans la partie 5 comment configurer le Shiny Server pour qu’il écoute sur le port 80 au lieu du port 3838 (c’est dans la sous-sous-section que j’ai appelée « La méthode rapide et sale »).

    Au niveau du fichier de configuration, il faut remplacer listen 3838 par listen 80.

    Eliot

    Bonjour,
    il semblerait qu’un t2 micro comme type d’instance ne soit pas suffisant dans certains cas (dont le mien) pour pouvoir installer R shiny sur le serveur.
    merci pour le tutoriel!

    Charles

    Salut Eliot. De mémoire, l’instance t2.micro ne dispose que d’1 GB de mémoire vive, ce qui est insuffisant pour installer certains packages (comme dplyr). Dans ce cas, on peut temporairement upgrader l’instance à 2 GB de ram, installer le package, puis downgrader de nouveau.

    Bastien

    Bonjour Charles,
    Merci beaucoup pour ce tutoriel très explicite.
    J’ai un problème tout au long de déploiement où on me sort tout le temps ce message : The application failed to start. The application exited during initialization.
    Penses tu que cela peut venir de la mémoire limitée de l’instance ?
    Merci encore

    Maxime

    Bonjour Charles,

    J’avais commencé à utiliser ce tutoriel, il y a 6 mois et je l’ai repris il y a quelques jours pour terminer la mise en place d’un projet.

    L’étape 6 me paraissait un jeu d’enfant après l’étape 5. Force est d’avouer qu’elle me pose beaucoup de problèmes.

    Je n’ai pas l’impression d’en avoir rencontrés lorsque j’ai suivi les étapes de l’article. Pourtant lorsque j’essaye de me connecter sur https://shiny.mydomain.com, rien ne se passe et je finis avec une erreur ‘échec d’ouverture’.

    Impossible de trouver d’où provient le problème. Un conseil? Parce que je viens de passer des dizaines d’heures à chercher sur google sans succès :(

    Merci,

    Charles

    Hello Maxime ! Ton problème ressemble à un timeout, ce qui me fait penser au pare-feu qui bloque la connexion. As-tu bien ouvert le port 443 ?

    Maxime

    Merci pour ta réponse,

    Je pense oui. Lorsque je me connecte en SSH à mon server sur DO et que je rentre la commande netstat -plunt | grep nginx dans mon terminal, j’observe notamment la ligne suivante:

    "tcp6 0 0 :::443 :::* LISTEN 26464/nginx: master".

    Cela me fait penser que le port 443 est ouvert.

    J’ai aussi regardé dans la partie ‘Networking’ de DO et je lis ‘No Firewalls applied to this droplet’. Je suppose donc ne pas avoir de pare-feu qui s’applique…

    Si j’interprete mal les éléments ci-dessus ou que tu penses à autre chose, je suis preneur pour tout tester :)

    Adrian

    Ton tuto est vraiment génial ! Je ne suis pas sur AWS, mais la partie déploiement m’a été super précieuse. Le ton léger de l’article, en plus de la précision du discours, m’a presque fait oublier que je déteste le sysadmin <3

    Comme Maxime, j’ai eu un petit quelque chose à faire pour ouvrir mon firewall.
    sudo ufw status m’a révélé que le port 80 n’était pas ouvert. Et un petit sudo ufw allow 80 a résolu ça (et évidemment sudo ufw allow 443 pour https).

    Merci beaucoup pour ton travail de synthèse et de pédagogie.

    Charles

    Merci pour ton feedback Adrian! Ça me fait super plaisir de voir que mon guide est apprécié !
    J’essaie d’être exhaustif, mais il y a toujours des petits détails qui passent outre de temps en temps, comme le problème de ufw que tu as rencontré. Je compte un peu sur la partie commentaires pour s’entraider à résoudre ce type de problème. Pour ufw, je pense que c’est assez rare quand même que des règles soient déjà existantes sur un VPS.

    Matthieu

    Bonjour
    Super tuto ! Merci beaucoup.
    Pour ceux qui utilisent l’instance gratuite t2.micro d’AWS, le manque de RAM lors de l’installation peut être réglé en utilisant le SWAP.
    voir le tuto : https://linuxize.com/post/how-to-add-swap-space-on-ubuntu-20-04/

    Fanny

    Bonjour,

    J’ai déployé mon app sur DigitalOcean. Je suivais en parallèle aussi ce tuto : https://deanattali.com/2015/05/09/setup-rstudio-shiny-server-digital-ocean/#user-libraries Mais j’ai suivi toutes tes étapes. Tu expliques tout bien, ce que l’on fait et pourquoi.
    Supper ton tuto. J’ai beaucoup appris.

    Moi j’ai surtout eu des problèmes lors de l’installation des packages. Mais en copiant collant le non des erreurs ou des dépendances qui manquaient dans google , j’ai trouvée les commandes à taper.

    Merci !!!

    Laisser un commentaire

    Les champs obligatoires sont marqués *

    Chargement...

    Les commentaires sont validés manuellement. La page va se rafraîchir après envoi.