Artisan Numérique

/système/virtualisation/novnc/rfb/vnc/websocket/x11vnc/ Partager son écran avec VNC et un simple navigateur WEB

Dans mon quotidien de dev indépendant, les moyens de communication, c'est un peu la priorité. Et dans la batterie d'outils, le partage d'écran arrive juste après la voix. La chalenge est donc de mettre au point une solution tout terrain me permettant de partager mon écran sans pour autant imposer d'installation particulière sur les postes de travail de mes clients. En somme un partage à la demande, utilisable d'un simple navigateur WEB moderne. Impossible ? Mais non, mais non...

VNC et RFB

Lorsque l'on pense "partage d'écran", VNC ne tarde pas à pointer son nez. Si en plus on rajoute : libre, multi-plateformes, multi-languages, multi-usages, cela devient une simple évidence.

Technique VNC est un système client-server de prise de contrôle à distance prenant en charge le clavier, la souris, la vidéo, mais aussi le presse-papier et même le transfert de fichier.

Développé il y a plus de 10 ans par les labos AT&T; de Cambridge, VNC repose sur le protocole RFB (Remote Frame Buffer) permettant de transmettre des portions d'écran d'une machine source (serveur VNC) à une machine cible (client VNC). Il existe aujourd'hui une multitude d'implémentation de ce protocole, tant pour les clients que pour les serveurs. Bref, c'est un truc très universel, largement documenté et très largement éprouvé. Le protocole permet en outre de s'adapter au débit en jouant sur la qualité de compression des trames.

X11VNC

Pour commencer il nous faut donc un serveur VNC. Un truc sans tracas, qui se lance en ligne de commande et qui partage tout ou partie de l'écran courant. Cet outil c'est le très grand classique x11vnc, qui est un peu à VNC, ce que Rsync est au transfert de fichier. Un couteau suisse gavé d'options, ultra-portable, ultra-léger et très rapide. En somme, dans la pure ligne des outils Unix, il fait une chose et le fait bien.

Pour l'installer, sudo aptitude install x11vnc et le tour est joué. Il suffit ensuite de lancer un x11vnc --help pour prendre conscience du volume d'options disponibles. Pour notre usage ce sera quelque chose comme cela :

gastonx11vnc -rfbport 9000 -clip 1280x1024+2560+0 -viewonly -noclipboard -nosetclipboard -nosel -nosetprimary -noprimary -passwd mon_mot_de_passe

Petit décryptage :

  • -rfbport permet d'utiliser un port précis pour le serveur. Sinon c'est généralement 5900 qui sera utilisé (5900 + le numéro de l'écran à partager).
  • -clip 1280x1024+2560+0, ça c'est pour flamber un peu et dire que j'ai 3 écrans et que je ne vais partager que la zone correspondant au 3ième.
  • -viewonly on est sympa mais on ne veut pas que notre client s'amuse à cliquer avec sa souris :-).
  • -noclipboard -nosetclipboard -nosel -nosetprimary -noprimary évite de transmettre son presse papier au client.
  • -passwdpour indique un mot de passe de connexion.

Une fois le serveur en route, vous pouvez installer par exemple le paquet tightvncviewer et lancer un vncviewer localhost::9000 pour tester le serveur (si vous n'avez qu'un écran, attention à la mise en abime ;-).

noVNC

Pour le serveur s'est réglé, reste l'épineux problème du client. Il y a bien le très classique tightVNC qui propose une applet Java. Mais s'il faut imposer l'installation de Java à mes clients, autant leur demander d'installer directement un client VNC.

La "bonne" solution c'est le tout nouveau client noVNC qui implémente le protocole RFB en JavaScript et fait le rendu visuel dans un Canvas HTML5. Cela fonctionne à merveille avec les navigateurs basés sur Gecko (FireFox, IceWeasel, etc) et WebKit (Safari, Konqueror, Chrome, etc.).

Côté communication, la petite application en JavaScript va, pour parler avec le serveur VNC, devoir utiliser une implémentation de WebSocket. Ce qui implique du côté serveur une couche intermédiaire pour passer de WebSocket à VraiSocket ;-). Mais rassurez-vous, tout cela est encapsulé par le projet noVNC en une seul commande très simple d'usage (à faire dans un autre terminal que X11VNC évidemment ;-) :

D'abord la récupération du projet
gastongit clone git://github.com/kanaka/noVNC

Ensuite lancement du serveur
gastoncd noVNC
gaston./utils/wsproxy.py --web $PWD :8080 localhost:9000

Ceci fait, il ne reste plus qu'à tester en allant, avec votre navigateur, à l'adresse http://localhost:8080. À l'apparition de la mire noVNC, il faut cliquer sur l'icône en haut à gauche, saisir l'adresse et le port du serveur VNC, éventuellement le mot de passe et vous connecter. Et là, magie :)

Pour décrypter un peu la commande wsproxy, il s'agit d'un script en python qui va jouer le rôle de serveur web (comme apache). Le paramètre --web indique la racine à servir. Ensuite est indique le port utilisé pour recevoir les requêtes HTTP (:8080). A ce stade le script permet donc au navigateur de recevoir l'ensemble des données (js,css,html, etc.) pour que le client s'initialise. Ensuite vient le paramètre indiquant le port à utiliser par WebSocket, qui n'est autre que celui que nous avons déclaré à x11vnc.

Industrialisation

Maintenant que les fondamentaux sont posés, un petit script pour mettre tout cela en musique :

#! /bin/bash

# Le nom externe de la machine à contacter par le client
MACHINE="ma_machine.mon_domaine.net"

# Un mot de passe pour plus de "sécurité"
PASSWORD="pouet"

# Le chemin où se trouve noVNC
NOVNC=~/noVNC

# Le port à utiliser par x11VNC
VNC_PORT=9000

# Le port à utiliser par le client WEB
WEB_PORT=8080

# La région de l'écran à partage
REGION=1280x1024+2560+0

# Les PID des deux process
proxy_pid=""
vnc_pid=""

# On intercepte toute interruption
trap "cleanup" TERM QUIT INT EXIT

# Procédure qui va faire le ménage en fin de session
cleanup() {
  trap - TERM QUIT INT EXIT
  trap "true" CHLD
  if [ -n "${proxy_pid}" ]; then
    echo "Arrêt du proxy (${proxy_pid})"
    kill ${proxy_pid}
  fi
  if [ -n "${vnc_pid}" ]; then
    echo "Arrêt de vnc (${vnc_pid})"
    kill ${vnc_pid}
  fi
}

# Démarrage du serveur VNC
x11vnc \
  -rfbport $VNC_PORT \
  -clip $REGION \
  -viewonly \
  -noclipboard -nosetclipboard -nosel -nosetprimary -noprimary \
  -quiet \
  -passwd $PASSWORD \
  2> /dev/null &

# On récupère le PID du processus et on utilise nc (netcat) pour attendre que le port soit
# correctement ouvert par x11vnc
vnc_pid="$!"
until nc -zw30 localhost $VNC_PORT;  do
  sleep 1
  done

# Lancement du proxy
$NOVNC/utils/wsproxy.py --web $NOVNC :$WEB_PORT localhost:$VNC_PORT &

# On récupère le PID du proxy et on attend une seconde que tout s'inititialise
proxy_pid="$!"
sleep 1

# On fabrique l'URL de connexion au partage d'écran
url="http://$MACHINE:$WEB_PORT/vnc.html?host=$MACHINE&port=$WEB_PORT&password=$PASSWORD"

# On le colle dans le presse papier
echo " $url" | xsel -psb

# On l'affiche aussi à l'écran
echo $url

# x11VNC s'arrêtera si le client se déconnecte. C'est donc lui que l'on attend pour faire
# le ménage.
wait $vnc_pid
cleanup

Et voilà, maintenant il suffit de lancer la commande pour initialiser votre partage et de faire CTRL-C pour arrêter le dit partage. Le partage sera aussi arrêté si le client se déconnecte.

Conclusion

Après tout ceci peut être encore amélioré. Il est possible de passer certains paramètres comme le mot de passe en option, ou mieux, le générer de manière aléatoire. Il est aussi possible de monitorer x11vnc pour émettre une notification lorsque le client se connecte. Enfin, il existe de petits outils comme xrectsel (à compiler par un simple gcc -o xrectsel xrectsel.c) permettant de définir la zone à partager de manière interactive.

Dans tout les cas, voilà une solution simple de partage avec un rendu de haute qualité. Il ne faut pas se mentir, cela reste moins rapide que ce que l'on peut avoir avec un client classique par noVNC est tout de même un projet bien bluffant.