Chat en « temps réel » sous Django avec Channels

Par défaut

Le Développeur Django adore les poneys !

Je suis un développeur Django

Comme vous le savoir si vous avez lu la page d’accueil de ce site internet, je suis (entre autres) un Développeur Django, c’est l’un de mes Frameworks de prédilection pour le développement web back-end entre autres sous Python.

Un outil que j’utilise maintenant depuis une dizaine d’années, quasiment depuis le genèse de ce projet.

Python Django Framework

Pourquoi Django, Pourquoi les poneys ?

Contrairement à Flask et Pyramid sont plutot sympas. Le code source de celui-ci a été formateur pour moi et ses patterns m’ont souvent inspirés même sur d’autres technos.

Il y a pas très longtemps ( en 2016 ) j’ai découvert le projet Channels. je voulais donc vous en parler car avec du JavaScript on peut faire des choses assez intéressantes.

Voici donc pour illustrer quelques commandes et lignes de code pour illustrer le développement d’une application de chat en temps réel.

L’environnement

Pour ne pas mélanger toutes le dépendances je crée un environnement virtuel Python sur le quel j’installe Django et Channels.

virtualenv live
source live/bin/activate
pip install Django channels

Ensuite dans le dossier live, je génère la structure de mon projet

cd ./live
django-admin startproject live .

Je commence donc par configurer celui-ci en editant le fichier settings.py comme suite

INSTALLED_APPS = [
        ....
        'channels',
        'chat'
]
....
LANGUAGE_CODE = 'fr-fr'
....
TIME_ZONE = 'Europe/Paris'
....
CHANNEL_LAYERS = {
        'default': {
                'BACKEND': 'asgiref.inmemory.ChannelLayer',
                'ROUTING': 'live.routing.channel_routing'
        },
}

A quoi ressemble le code

Du coup je crée un fichier routing.py dans live avec la variable channel_routing pour aiguiller les requêtes asynchrones.

from channels import include, route
from .consumers import websocket_receive, websocket_add, websocket_disconnect

channel_routing = [
    route('websocket.connect', websocket_connect,
        path=r'^/chat/(?P<room>[-\w]+)/$'),
    route('websocket.receive', websocket_receive),
    route('websocket.disconnect', websocket_disconnect)
]

Maintenant je vais écrire les trois fonctions websocket_connect, websocket_receive et websocket_disconnect qui vont gérer les trois éventements respectivement de connexion, de reception de données et de deconnexion.

from channels import Group
from channels.sessions import channel_session
from channels.auth import channel_session_user_from_http

@channel_session_user_from_http
def websocket_add(message, room):
  name = 'Anonymous troll'
  if message.user.is_authenticated():
    name = message.user.get_full_name()
  message.reply_channel.send({
    'accept': True,
    'text': 'Bienvenue %s !' % name
  })

  message.channel_session['room'] = room
  message.channel_session['name'] = name

  Group(room).add(message.reply_channel)

@channel_session
def websocket_receive(message):
  room = message.channel_session.get('room')
  name = message.channel_session.get('name')
  Group(room).send({
    'text': "<%s> %s" % ( name,  message.content.get('text') )
  })

@channel_session
def websocket_disconnect(message):
  room = message.channel_session.get('room')
  Group(room).discard(message.reply_channel)

Ayant déjà fait du Twisted et du Celery pour gerer ce type d’architecture asyncrhone je trouve l’integration de Channels plutot élégante. avec les decorateurs, cert a ralonge, mais bon ça a le merite d’etre claire.

Ensuite comment ça marche

On peut commencer a s’amuser avec les web sockets une fois le serveur de développement lancé à l’aide de la commande

./manage.py runserver

Je navigue alors sur mon site, j’ouvre la console de mon navigateur préféré et je lance ces petites lignes

ws = new WebSocket("ws://" + window.location.host + "/chat/4chan-b/");
ws.onmessage = function(event){
  console.log(event.data);
}
ws.send("Salut les gens !");.

Voila, il reste plus qu’à développer l’application JavaScript.

Certificat SSL simple et rapide avec Let’s Encrypt

Par défaut

Certificat SSL par ci certificat SSL par la… mais au fait mis à part le joli cadenas affiché sur votre navigateur favori c’est quoi exactement ?

Il s’agit d’un certificat électronique délivré par une autorité de certification de confiance, une espèce de carte d’identité sous forme d’un fichier qui lie une paire de clés de cryptage asymétrique à une organisation un site web typiquement.

Cela permet d’une part d’authentifier l’interlocuteur à travers son nom de domaine de l’autre de chiffrer et d’assurer l’intégrité des échanges avec celui-ci ce qui confère un certain niveau de sécurité aux communications web.

Malgré le fait que ce schéma représente un standard de l’industrie web, la procédure de sa mise en place reste couteuse et habituellement assez fastidieuse.

Miracle Let’s Encrypt ! Une autorité ce certification gratuite avec un outil ouvert et automatisé qui a pour objectif de rendre le web plus sur et plus respectueux des droits à la vie privé de ses utilisateurs.

Let's Encrypt

L’Opensourcesite que je suis n’a pas pu s’empêcher de faire un petit how-to à l’aide du client historique qui devient maintenant Certbot packagé aujourd’hui pour la plus part des distribution GNU/Linux

Imaginons que vous êtes sur une Ubuntu et que vous utilisez Apache comme serveur httpd la sécurisation de votre serveur ne prend pas plus de 3 directives:

Installation du client avec aptitude / apt-get


toi@ta-machine$ sudo apt install python-letsencrypt-apache 

Et la magie opère ! c’est parti pour 90 jours de certification


toi@ta-machine$ sudo letsencrypt --apache

Bon pour ceux qu’on besoin de savoir ce qui se passe ou qui sont dans un environnement de production dans la mesure ou il s’agit encore d’une beta préférer ceci


toi@ta-machine$ sudo letsencrypt --apache certonly

Cela ajoute un hôte virtuel à votre configuration Apache pour pouvoir vous authentifier à travers un échange d’informations puis le supprime une fois c’est fait en redémarrant le serveur « gracieusement »

Vous allez me dire et après les 3 mois que se passe-t-il ? je vous dirait rien de mieux qu’une tâche planifiée pour renouveler grace à


letsencrypt renew

Et voila comment installer un Certificat SSL de façon simple rapide et efficace à l’aide de Let’s Encrypt

OpenVPN avec Docker

Par défaut

L’une des toutes première chose que j’ai l’habitude de faire après l’installation d’une nouvelle machine est la mise en place d’un réseau privé virtuel c’est à dire un logiciel VPN comme OpenVPN pour pouvoir y accéder directement depuis mon réseau, avec le moins de restriction et le plus de sécurité possibles.

Exemple d'utilisation de OpenVPN

Cette installation relativement assez fastidieuse, surtout l’initialisation, la génération de certificats et clefs après l’installation de Open VPN se voit franchement simplifié grâce aux scripts d’automatisation de Kyle Manna

Ces scripts ainsi que leurs dépendances sont distribués sous image Docker. trés populaire avec plus d’un million de telechargement sur le Docker hub vous pouvez la retrouver sous le nom de kylemanna/openvpn

Le code source de ces script quant a lui est disponible sur Github

Installation de OpenVPN avec Docker

Initialisation

Coté serveur

Avec Docker déjà installé la mise en place d’un réseau sous Open VPN avec kylemanna/docker-openvpn devient un jeu d’enfant

Nous allons dans un premier temps, afin de pouvoir stocker durablement les informations de cryptage de Open VPN, créer un Volume Docker vierge.


OVPN_DATA="ovpn-data-volume"
docker run --name $OVPN_DATA -v /etc/openvpn busybox

Cette commande crée tout simplement un volume vide nomé ovpn-data-volume

Afin de l’initialiser avec l’ensemble des informations, clefs et certificats nécessaires nous allons utilisé le script de génération de configuration de l’image docker ovpn_genconfig ainsi que le script d’initialisation de certificat de signature ovpn_initpki comme suite:


FQDN="vpn.nomdomaine.ltd"
PORT="1194"
docker run --volumes-from $OVPN_DATA --rm kylemanna/openvpn ovpn_genconfig -u udp://$FQDN:$PORT
docker run --volumes-from $OVPN_DATA --rm -it kylemanna/openvpn ovpn_initpki

Un prompt demande de remplir les informations nécessaires pour la génération du certificat: nom de la machine, de l’unité, mot de passe etc.

Coté client

Coté client, nous avons besoin de créer une configuration Open VPN avec notamment les clés généré par le certificat du serveur créée précédemment dans le volume ovpn-data-volume.

Pour cela un autre script existe, il suffit de lancer la commande suivant avec le mot de passé utilisé précédemment dans la génération du certificat et le un nom de client afin de simplifier la gestion des clés


docker run --volumes-from $OVPN_DATA --rm -it kylemanna/openvpn easyrsa build-client-full NOM_CLIENT nopass

 

Lancement

à ce stade le volume openvpn-data-volume est initialisé et prêt à l’emploi, vous pouvez ajouter votre conteneur Docker en tant que service en créant un fichier de configuration upstart ou un script systemD

Voici un exemple pour upstart


description "Conteneur Docker pour le serveur Open VPN"
start on filesystem and started docker
stop on runlevel [!2345]
respawn
script
  exec docker run --volumes-from openvpn-data-volume --rm -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn
end script

 

Voila, comment en quelques lignes avec cet outil nous pouvons mettre en place rapidement un réseau VPN sur un serveur disposant de Docker.