[Raspberry Pi] Système de vidéosurveillance avancé avec ustreamer et motion

La vidéo-surveillance est un des projets fréquents du Raspberry Pi et cela depuis les premières versions de la carte. Des outils comme motion ou des OS dédiés comme motionPie sont vite devenus une référence pour ce type d’utilisation.

Pourtant, en tant qu’utilisateur de la première heure du combo Raspberry Pi + Camera Pi + motion, j’ai toujours été déçu de la performance globale de motion, la fluidité globale des images laissant à désirer.

Le dernier Raspberry Pi (v4) est sorti récemment, une carte beaucoup plus performante et pourtant rien ne change lorsqu’il s’agit de faire de la vidéo-surveillance avec motion. C’est toujours aussi lent.

La faute à qui, pas forcément à motion et pas vraiment au Raspberry Pi non plus qui malgré sa récente upgrade fait ce qu’il peut. Le traitement ou l’analyse d’image a toujours été une opération lourde et dans le cas de motion, non seulement il y a du traitement d’image mais il y a d’abord de la capture puis de l’enregistrement sur le disque, et le tout avec plusieurs images (souvent HD voir full-HD) par seconde à traiter. Malheureusement le RPi – et ses concurrents d’ailleurs – sont loin d’êtres des machines capables d’endosser ces gros traitements.

Alors qu’elles sont les solutions alternatives pour monter un système de vidéo-surveillance un minimum performant ?

Déjà il va falloir oublier le critère « à coût réduit » du projet car selon moi il ne faut pas un mais au moins deux Raspberry Pi. 

Voici le système souhaité :

  • Je souhaite voir en direct ce qu’il se passe devant la caméra à tout moment (24/7).
  • Je souhaite que le système soit capable de détecter un mouvement, envoyer une alerte et enregistrer les images de la détection (photos et/ou vidéos). Inutile d’enregistrer des images si il n’y a pas de détection. Bref ce que sait faire motion.
  • Mais du coup je souhaite pouvoir visionner le direct si je reçois une alerte (pour voir ce qu’il se passe à distance) et aussi avoir les images sauvegardées quelque part si j’ai besoin de preuves. En gros combiner les deux points ci-dessus.

Un gouffre à performance vu comme ça quand on repense à quel point ça rame avec motion, alors si en plus on ajoute du stream en direct…

Mais pas si on splitte les tâches :

  • 1 Raspberry Pi et sa caméra, dédié à capturer l’image et la diffuser (en http). Ce sera la caméra disposée à l’intérieur ou à l’extérieur de la maison.
  • 1 Raspberry Pi « serveur » dédié à analyser le flux http et enregistrer l’image si détection il y a. Il fera également office de reverse-proxy pour rediffuser le flux en https. Lui on le disposera quelque part au sec, un peu caché et avec un disque externe pour stocker les images.

Schéma résumant cette solution et les outils utilisés :

Pré-requis

  • 1 Raspberry Pi (ou concurrent) le plus puissant possible, qui assurera la partie serveur. Pour ma part j’utilise un RPi 4.
  • 1 Raspberry Pi, pas forcément puissant, pour assurer la partie camera. Ça fonctionne avec un RPi1.
  • 1 camera USB avec LED infrarouge et étanche. Pour ma part j’ai celle-ci : amazon (parfois en rupture de stock). Si vous ne souhaitez pas de vision de nuit ni placer votre camera à l’extérieur, alors la camera Pi officielle fera très bien l’affaire.
  • un nom de domaine pour accéder au stream de la caméra depuis l’extérieur. Si vous n’avez pas de nom de domaine vous pouvez en acheter un chez OVH (les .ovh ne sont vraiment pas cher, environ 3€/an). C’est toujours possible de faire sans mais il faudra bidouiller son fichier hosts…

Installer Raspbian sur les deux RPi, leur assigner une adresse IP fixe (nombreux tutos sur Internet). Mettre à jour le firmware et les paquets systèmes.

Note : le Wi-Fi n’est pas envisageable pour ce projet, connecter les cartes en filaire sinon adieu les images fluides. Et puis quand je vois toutes ces cameras Wi-Fi en vente sur le net, je me dis qu’il faut être naif pour faire confiance à ce genre de produit et se penser en sécurité. Si je suis un voleur, je me pointe avec un brouilleur d’ondes et merci au revoir la camera Wi-Fi.

Une fois fait on s’attaque à l’installation de la camera.

Partie 1 : Installation et configuration de la caméra

Brancher la caméra sur le Raspberry Pi et ouvrir une connexion SSH. L’ensemble des configurations s’effectuent en root. Sur RPi l’utilisateur par défaut est pi, vous pouvez passer root avec :

sudo su

Il existe quelques logiciels de streaming en direct sur Linux, un des plus connus est mjpg-streamer. Ce dernier fonctionne très bien mais je vais privilégier ici ustreamer qui m’a été présenté par son créateur lui-même et qui est présent de temps en temps sur le github de mjpg-streamer. Ce n’est pas un fork de mjpg-streamer, c’est juste un équivalent.

L’avantage de ustreamer c’est qu’il possède plus d’options, il est plus clair et plus simple à utiliser et surtout il dégueule nativement plus de logs que mjpg-streamer ce qui facilite grandement le debugage.

Lien vers le GitHub de ustreamer : https://github.com/pikvm/ustreamer

Pour l’installer, il faut le compiler (c’est facile) :

apt install make gcc libevent-dev uuid-dev libbsd-dev libjpeg62-turbo-dev
cd /home/pi/
git clone --depth=1 https://github.com/pikvm/ustreamer
cd ustreamer
make

Vérifier avec lsusb que la caméra branchée est bien reconnue par le système, dans mon cas ça affiche ceci : 

lsusb
Bus 001 Device 008: ID 05a3:9230 ARC International Camera
Bus 001 Device 009: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 007: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 006: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Créer les scripts de démarrage et d’arrêt du stream, c’est pi qui exécutera ces scripts :

mkdir /home/pi/scripts
cd /home/pi/scripts/
vim start-camera.sh

Contenu du script de démarrage :

#!/bin/bash

RESOLUTION="1920x1080" # Resolution du stream, à adapter en fonction de la résolution maximale dont est capable la camera
FRAMERATE="30" # Nombre d'images par seconde qui seront diffusées par le stream, du moins si le RPi en est capable
DATE=$(date +%Y-%m-%d) # Date du jour
LOG="/home/pi/scripts/ustreamer-live.log" # Emplacement du fichier de log 

echo -n> /home/pi/scripts/ustreamer-live.log # Vidage du fichier de log

echo "Démarrage du stream"
/home/pi/ustreamer/ustreamer --device=/dev/video0 -K 0 -r $RESOLUTION -f $FRAMERATE -m JPEG --host 0.0.0.0 --port 8888 2>&1 | tee $LOG &

# MJPG-streamer : # Equivalent de commande pour mjpg-streamer au cas où des irréductibles souhaitent quand même l'utiliser
#/usr/local/bin/mjpg_streamer -i "/usr/local/lib/mjpg-streamer/input_uvc.so -n -f $FRAMERATE -r $RESOLUTION -timeout 30" -o "/usr/local/lib/mjpg-streamer/output_http.so -p 8888 -w /usr/local/share/mjpg-streamer/www" 2>&1 | tee /home/pi/scripts/ustreamer-live.log &

exit
vim stop-camera.sh

Contenu du script d’arrêt : 

#!/bin/bash

# Pour arrêter le stream, il faut tuer le processus, du coup on cherche le PID correspondant :

PID="$(/bin/ps -aux | /bin/grep '/home/pi/ustreamer/ustreamer' | /bin/grep -v 'grep' | /usr/bin/awk '{print $2}')"

echo "Arrêt de ustreamer :"
sudo /bin/kill "$PID"
sleep 1

# Vraiment au cas où le processus n'a pas été tué, on retente une deuxième fois :

if /bin/ps -aux | /bin/grep '/home/pi/ustreamer/ustreamer' | /bin/grep -v 'grep';then
echo "Le processus n'a pas été tué, nouvelle tentative..."
sudo /bin/kill -9 "$PID"
else
echo "OK"
fi

exit

Attribuer le droit d’exécution sur ces scripts et remettre les bons droits sur tout ce qu’on vient d’ajouter :

sudo chmod 700 start-camera.sh stop-camera.sh
sudo chown -R pi:pi /home/pi

Se déconnecter temporairement de l’utilisateur root (Ctrl+D), et en tant qu’utilisateur pi, démarrer le stream pour tester :

cd /home/pi/scripts/
./start-camera.sh &

Ca devrait afficher quelques logs à l’écran.

A l’aide d’un navigateur en se connectant à http://ADRESSE_IP_CAMERA:8888 on devrait pouvoir accéder à la page d’accueil de ustreamer et au stream en cliquant sur /stream.

Partie 2 : Installation et configuration du reverse-proxy et de motion

Le stream en direct est fonctionnel, encore faut il qu’on puisse y accéder depuis l’extérieur et qu’on puisse détecter une présence et enregistrer les images.

Ouvrir une connexion SSH sur le serveur. L’ensemble des configurations s’effectuent en root. Sur RPi l’utilisateur par défaut est pi, vous pouvez passer root avec :

sudo su

Reverse-proxy nginx 

Terminons la partie stream en direct avant de passer à motion. Pour voir depuis l’extérieur ce qu’il se passe à l’intérieur de la maison, il faut mettre en place un reverse proxy qui fera office d’intermédiaire. Alors on aurait pu utiliser le serveur web intégré de ustreamer mais celui-ci ne gère pas l’https ni la possibilité de mettre un mot de passe pour accéder à la page de stream (en tout cas pas dans la version actuelle à l’heure où j’écris ces lignes).

Sur le RPi « serveur », installer nginx : 

apt install nginx

Supprimer le vhost installé par défaut et éditer un nouveau vhost qui fera office de reverse-proxy : 

rm /etc/nginx/sites-*/default -f
vim /etc/nginx/sites-available/reverse_ustreamer.conf

Dans ce vhost dédié à écouter sur le port 80, on n’indiquera que le répertoire racine pour le moment :

server {
listen 80;
server_name cam.mondomaine.com;

# Forcer https
# return 301 https://$server_name$request_uri; # Commenter cette ligne qu'on gardera pour plus tard
root /var/www/cam.mondomaine.com;

access_log /var/log/nginx/cam.mondomaine.com_access.log;
error_log /var/log/nginx/cam.mondomaine.com_error.log;
}

Activer ce nouveau vhost :

cd /etc/nginx/sites-enabled/
ln -s ../sites-available/reverse_ustreamer.conf

Tester la configuration, nginx ne doit pas retourner d’erreur :

sudo nginx -t

Redémarrer le service :

service nginx restart

A ce stade et sous réserve que le paramétrage DNS et les redirections de port de votre box sont en place, le vhost devrait fonctionner et votre navigateur devrait afficher la page d’accueil nginx ou au moins une page blanche.. mais pas d’erreur 404 ou autre.

Nous reviendrons plus tard pour la configuration du SSL (https) et du reverse proxy car il faut d’abord commander un certificat, ce que nous allons faire tout de suite.

Certificat Let’s Encrypt 

J’ai déjà créé un article sur getssl, le script bash qui va permettre de commander un certificat SSL. Pour éviter les doublons, je vous invite à suivre l’article suivant jusqu’à la fin et de commander un certificat pour le nom de domaine cam.mondomaine.com. Lien vers l’article : https://yavin4.ovh/index.php/2019/09/19/certificat-ssl-lets-encrypt-avec-getssl/

A ce stade, vous devriez exécuter la commande suivante pour commander votre certificat : 

./getssl cam.mondomaine.com

Maintenant que nous avons un certificat SSL il faut mettre en place le vhost nginx qui va écouter sur le port 443 afin de passer le site en HTTPS.

D’abord, il faut limiter le vhost 80 à renvoyer vers le vhost 443, c’est tout ce qu’il devra faire. Editer le vhost précédemment créé :

vim /etc/nginx/sites-available/reverse_ustreamer.conf

Et décommenter la ligne précédemment commentée, afin de rediriger toutes les requêtes sur le port 80 (http) vers 443 (https) :

server {
listen 80;
server_name cam.mondomaine.com;

# Forcer https
return 301 https://$server_name$request_uri; # celle-ci
root /var/www/cam.mondomaine.com;

access_log /var/log/nginx/cam.mondomaine.com_access.log;
error_log /var/log/nginx/cam.mondomaine.com_error.log;
}

Vhost 443

Ceci étant fait, créer le nouveau vhost écoutant sur le port 443 :

vim /etc/nginx/sites-available/reverse_ustreamer_ssl.conf

C’est ce vhost qui fera office de reverse-proxy entre le serveur web http de ustreamer et le navigateur client :

upstream ustreamer { # Défini le groupe de serveurs qui va répondre aux requêtes derrière le reverse proxy. Ici en l’occurrence c'est le RPI-caméra et le service ustreamer écoutant sur le port 8888
server IP_RPI_CAMERA:8888;
}

server {
listen 443 ssl;
server_name cam.mondomaine.com;

ssl_certificate /etc/nginx/ssl/cam.mondomaine.com/cam.mondomaine.com.crt;
ssl_certificate_key /etc/nginx/ssl/cam.mondomaine.com/cam.mondomaine.com.key;

# Add headers to serve security related headers
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains";
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;

# Racine du site
root /var/www/cam.mondomaine.com;

# Fichiers de logs
access_log /var/log/nginx/cam.mondomaine.com_ssl_access.log;
error_log /var/log/nginx/cam.mondomaine.com_ssl_error.log;

# Ne pas autoriser les robots à indexer le site
location = /robots.txt {
deny all;
log_not_found off;
access_log off;
}

location / {
include /etc/nginx/proxy_params; # Inclut quelques directives et en-têtes pour les proxys
proxy_pass http://ustreamer/; # On redirige les requêtes vers le groupe de serveurs 'ustreamer' défini plus haut
}
}

Tester la conf : 

nginx -t

Si rien n’a été oublié, nginx ne devrait pas retourner d’erreur, redémarrer le service :

service nginx restart

Sur votre box, configurez vos redirections de ports : les requêtes entrantes http (80) et https (443) doivent rediriger vers l’IP du pi-serveur où est hébergé le reverse-proxy. Attention si vous redirigez ces ports, des scans et des tentatives d’intrusions en provenance du monde entier commenceront à affluer sur votre serveur. Il ne faudra pas tarder à mettre des règles de pare-feu si ce n’est pas déjà fait. Tester l’accès dans le navigateur, l’interface de ustreamer et le stream devraient être accessibles : https://cam.mondomaine.com

Si le test est positif, vous pouvez couper les redirections mises en place si vous n’avez pas encore de règles de pare-feu.

En local, vous pouvez ajouter une entrée dans le fichier hosts de votre PC pour indiquer que cam.mondomaine.com pointe vers l’IP de votre serveur.

Partage samba

Cette partie est optionnelle mais si on souhaite accéder facilement aux images et vidéos enregistrées par motion depuis un PC, il faut mettre en place un partage samba. Sinon il est possible de les récupérer en les copiant par ssh, dans ce cas vous pouvez ignorer cette partie et passer à la suite (motion).

Installer samba : 

apt install samba samba-testsuite

Créer le répertoire où seront stockées les images et vidéos (par exemple ici sur un disque externe monté sur /mnt/disque) :

mkdir -p /mnt/disque/samba/camera
chown -R root:sambashare /media/disque/samba
chmod -R 550 /media/disque/samba

Passons à la conf. Faire une sauvegarde du fichier de conf d’origine :

cp /etc/samba/smb.conf /etc/samba/smb.conf.old

Éditer le fichier et ajouter la conf suivante :

vim /etc/samba/smb.conf
#======================= Global Settings =======================

[global]
workgroup = WORKGROUP
netbios name = piserver
server string = %h server (Samba, Ubuntu)
security = user
dns proxy = no
log level = 2
log file = /var/log/samba/samba.log
max log size = 50


#======================= Share Definitions =======================
# Répertoire Camera contenant les captures de la caméra de surveillance
[Camera]
comment = Captures de la camera de surveillance
path = /mnt/disque/samba/camera
browseable = yes
public = no
writeable = yes
create mask = 0770
directory mask = 0770
force group = sambashare
printable = no

Enregistrer puis tester les paramètres avec la commande suivante :

testparm -s

La commande ne doit pas renvoyer d’erreurs, sinon vérifier la configuration.

Redémarrer samba :

/etc/init.d/samba restart

Pour créer un utilisateur Samba, il faut qu’un utilisateur Linux du même nom existe. Créer par exemple un utilisateur « toto » Linux qui fera partie du groupe sambashare :

useradd -s /usr/sbin/nologin -G sambashare toto  # ici on crée un utilisateur toto, membre du groupe sambashare et n'ayant aucun home directory sur le NAS ni aucun accès au shell car c'est inutile

Créer un mot de passe pour cet utilisateur :

passwd toto

L’utilisateur Linux est prêt, créer ensuite l’utilisateur Samba du même nom :

smbpasswd -a toto

Motion

Installer motion :

apt install motion

Editer le fichier /etc/default/motion et passer l’option à yes afin d’activer le mode daemon de motion :

start_motion_daemon=yes

Editer le fichier de conf /etc/motion/motion.conf et modifier les options suivantes :

daemon on
log_level 7
log_type all
v4l2_palette 8
width 1280 # Adapter en fonction de la résolution du stream
height 720 # Adapter en fonction de la résolution du stream
framerate 25
netcam_url http://ADRESSE_IP_CAMERA:8888/stream # de la même manière que le fait le reverse proxy nginx, on capture le stream web de la caméra
netcam_keepalive on
pre_capture 2
post_capture 2
event_gap 30
quality 95
target_dir /mnt/disque/samba/camera/ # Répertoire à adapter, ici je choisis de stocker les images et vidéos sur le disque externe attaché au serveur, et qui fera office de partage samba afin que les images soient facilement visualisables depuis un PC du réseau
picture_filename %Y-%m-%d/images/%v_%Y-%m-%d_%Hh%Mm%Ss_%q
movie_filename %Y-%m-%d/videos/%v_%Y-%m-%d_%Hh%Mm%Ss_video
on_event_start sh /etc/motion/on_event_start.sh # Dès qu'un mouvement est détecté, il s'agit d'un nouvel évènement. L'évènement se termine lorsqu'il n'y a pas eu de mouvement détecté pendant 30sec (event_gap). On exécute un script qui envoie un mail lorsqu'un évènement est généré.
on_movie_end sh /etc/motion/on_movie_end.sh %f # Lorsqu'une vidéo est générée suite à un évènement, on exécute un script qui envoie un mail avec le bout de vidéo.

Configurer les alertes mail

Les deux dernières options configurées dans le fichier de conf de motion ci-dessus permettent d’exécuter un script qui envoie un mail d’alerte. Il est tout à fait possible d’exécuter directement la commande de mail plutôt que d’exécuter un script mais l’avantage de ce dernier est qu’on va pouvoir configurer des horaires d’absences auxquelles on choisira d’envoyer des alertes mail. Si on est à la maison il est inutile d’envoyer une alerte, c’est pour cela qu’on configure des horaires.

Editer un nouveau fichier on_event_start.sh dans le répertoire motion. Ce script enverra juste un mail d’alerte au début d’un évènement pour prévenir l’utilisateur : 

vim /etc/motion/on_event_start.sh

Contenu du script :

#!/bin/bash

CONF="/etc/motion/horaires.conf"
JOUR=$(date +%A)
HEURE=$(date +%H%M)

for HEURES in $(cat $CONF | grep "$JOUR");do
HEURE_DEBUT=$(echo $HEURES | awk -F: '{print $2}')
HEURE_FIN=$(echo $HEURES | awk -F: '{print $3}')

if [ "$HEURE" -gt "$HEURE_DEBUT" ] && [ "$HEURE" -lt "$HEURE_FIN" ];then
echo 'alerte' | mutt -F "/etc/motion/.muttrc" -s 'Alerte détection' adresse_mail
fi
done

exit

Puis éditer un nouveau fichier on_movie_end.sh. Ce script enverra un mail avec la vidéo capturée lors de la dernière détection :

vim /etc/motion/on_movie_end.sh

Contenu du script :

#!/bin/bash

CONF="/etc/motion/horaires.conf"
JOUR=$(date +%A)
HEURE=$(date +%H%M)

for HEURES in $(cat $CONF | grep "$JOUR");do
HEURE_DEBUT=$(echo $HEURES | awk -F: '{print $2}')
HEURE_FIN=$(echo $HEURES | awk -F: '{print $3}')

if [ "$HEURE" -gt "$HEURE_DEBUT" ] && [ "$HEURE" -lt "$HEURE_FIN" ];then
echo 'Vidéo en pièce jointe' | mutt -F "/etc/motion/.muttrc" -s 'Vidéo capturée suite à la précédente détection' -a $1 -- adresse_mail
fi
done

exit

Enfin, éditer le fichier horaires.conf afin de configurer les horaires d’absences :

vim /etc/motion/horaires.conf

Dans ce fichier, indiquer les horaires d’absence de la maison (par exemple ici absence de 08h00 à 19h00 tous les jours de la semaine) :

lundi:0800:1900
mardi:0800:1900
mercredi:0800:1900
jeudi:0800:1900
vendredi:0800:1900
samedi:0800:1900
dimanche:0800:1900

Note : même lorsque les alertes sont désactivées, motion continue de tourner et à enregistrer sur le disque les mouvements qu’il détecte (pratique si on s’absente en dehors des horaires indiquées et qu’on a oublié de les modifier).

Appliquer les droits suivants sur les fichiers créés : 

cd /etc/motion/
chown motion:pi *
chmod 660 *
chmod 760 on_*.sh

Enfin installer mutt, un client mail permettant d’envoyer un mail avec pièce jointe en ligne de commande :

apt install mutt

Puis éditer un nouveau fichier de conf :

vim /etc/motion/.muttrc

Contenu du fichier de conf pour un compte gmail (on configure ici le compte mail qui enverra des alertes) :

# Nom de l'expéditeur du message
set realname = "pi-server"

# Activer TLS si disponible sur le serveur
set ssl_starttls=yes
# Toujours utiliser SSL lors de la connexion à un serveur
set ssl_force_tls=yes

set my_pass='mot de passe'
set my_user='utilisateur' # ce qui se trouve avant l'arobase @

set from = 'utilisateur@gmail.com'
set use_from = yes

set smtp_url=smtps://$my_user:$my_pass@smtp.gmail.com
set ssl_force_tls = yes

Appliquer les droits suivants sur ce fichier :

chown motion:pi /etc/motion/.muttrc
chmod 600 /etc/motion/.muttrc

Tester un envoi de mail en tant qu’utilisateur motion pour vérifier : 

sudo -u motion echo 'test' | mutt -F '/etc/motion/.muttrc' -s 'test' adresse_mail

La conf de motion et les scripts sont en place, il ne reste plus qu’à redémarrer motion pour qu’il applique tous les changements : 

service motion restart

Faire un geste devant la caméra et vérifier que : 

  • Des images s’enregistrent sur le disque
  • Un premier mail d’alerte est envoyé
  • Puis un deuxième mail avec une vidéo en pièce jointe est envoyé

Partie 3 : Sécurité

Il y a la sécurité :

  • des flux entre la caméra et le serveur
  • des flux entre le serveur et le client (smartphone, pc…)
  • de l’OS

Il ne faudrait pas que la caméra (j’entends par là le RPi qui y est relié en USB) soit compromise. Si un pirate parvenait à obtenir l’accès au système, les images pourraient se retrouver sur Internet (comme c’est le cas de nombreuses caméras mal sécurisées) ou la caméra pourrait être mise hors service, l’OS détruit.

Il faut également faire le maximum pour limiter les tentatives de connexion au serveur par des personnes mal intentionnées, surtout si les ports http(s) sont ouverts de l’extérieur et redirigés vers celui-ci (dans le cas où on souhaite pouvoir visionner le stream depuis l’extérieur).

Pare-feu

La première des choses à faire est de mettre en place des règles de parefeu (iptables, firewalld…). Vous pouvez décider vous même des règles à mettre en place mais voici comment j’ai décidé de le faire de mon côté :

La première porte d’entrée doit être le serveur, on ne peut pas se connecter directement à la caméra (sous-entendu le RPi qui y est relié en USB). Tout doit passer par le serveur central.

C’est à dire que les demandes d’accès en SSH et au flux vidéo HTTP de la caméra doivent provenir du serveur et d’aucune autre source externe (refusé). Le serveur joue donc son rôle de reverse-proxy http et de sorte de relai pour le ssh. On peut éventuellement faire une exception en autorisant le réseau local (ou une plage) ou le PC administrateur à accéder en ssh à la caméra.

Pour plus de clarté voici le schéma de base :

S

 

 

 

 

 

 

 

 

Le même schéma avec des exemples :

Exemple lorsqu’on demande de visualiser le stream en direct : c’est le serveur et son reverse-proxy nginx qui fait office de relais pour fournir le flux produit par la caméra Exemple lorsqu’on souhaite se connecter en ssh à la caméra : on se connecte d’abord au serveur en ssh, puis on se connecte à la caméra en ssh

Le principe est le même si la demande vient de l’extérieur (redirection des ports http(s) vers le serveur), en veillant à mettre de bonnes restrictions IP pour ne pas autoriser tout l’extérieur à visualiser la vidéo…

Ainsi, voici quelques règles de pare-feu iptables traduisant ces schémas :

Sur le serveur :

Les éléments en rouge sont ceux à adapter en fonction du nom de votre interface réseau, du port SSH (qu’il est judicieux de changer, traité dans le point Hardening SSH) et de votre réseau local.

  • Autoriser le ssh entrant depuis le réseau local de la maison
iptables -t filter -A INPUT -i eth0 -s 192.168.0.0/24 -p tcp -m tcp --dport 22 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
  • Autoriser le ping depuis le réseau local de la maison :
iptables -t filter -A INPUT -i eth0 -s 192.168.0.0/24 -p icmp -m icmp --icmp-type 8/0 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

Concernant les règles d’accès depuis l’extérieur il faut être vigilant.

  • Vous pouvez par exemple autoriser l’IP de votre lieu de travail à se connecter en http(s) au serveur afin de pouvoir visualiser le stream quand vous êtes sur votre lieu de travail :
iptables -t filter -A INPUT -i eth0 -s IP_publique_boulot -p tcp -m tcp --dport http -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -A INPUT -i eth0 -s IP_publique_boulot -p tcp -m tcp --dport https -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
  • Il est possible également de n’autoriser que les plages IP de votre opérateur téléphonique. Les opérateurs ont des plages IP réservées qui ne changent normalement pas. Par exemple je suis chez bouygues et j’autorise les plages IP de bouygues à accéder au stream afin de pouvoir le visualiser depuis mon téléphone. Attention néanmoins ça laisse une porte ouverte à tous les autres smartphones chez bouygues. 

Pensez à refuser tout le reste, sinon ces règles ne servent à rien. La bonne pratique est de tout refuser (DROP), et d’autoriser seulement ce que l’on souhaite.

Sur la caméra : 

Les éléments en rouge sont ceux à adapter en fonction du nom de votre interface réseau, du port SSH (qu’il est judicieux de changer, traité dans le point Hardening SSH) et de votre réseau local.

J’autorise tout en provenance du serveur, et refuse tout le reste :

iptables -t filter -A INPUT -i eth0 -s IP_serveur -p tcp -j ACCEPT

Éventuellement j’autorise un PC perso (dont l’IP reste fixe) à se connecter en ssh : 

iptables -t filter -A INPUT -i eth0 -s IP_PC -p tcp -m tcp --dport 22 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

Concernant les règles d’accès depuis l’extérieur, on en configure aucune.

Pensez à refuser tout le reste, sinon ces règles ne servent à rien. La bonne pratique est de tout refuser (DROP), et d’autoriser seulement ce que l’on souhaite.

A noter que toutes ces règles peuvent être incluses dans un script de parefeu iptables, ce que j’ai fait mais que je ne fournirait pas ici. Il existe beaucoup d’exemple de script iptables sur le net.

Hardening SSH et système

La deuxième des choses à faire est de renforcer la configuration du service sshd et plus généralement du système. On ne peut pas tout voir ici mais un script d’audit interne tel que lynis peuvent donner quelques pistes d’améliorations.

Veillez à mettre en place quelques bonnes pratiques de base au fichier /etc/ssh/sshd_config, comme changer le port ssh par défaut, refuser la connexion ssh de l’utilisateur root et créer à la place un utilisateur Linux lambda pour se connecter en ssh, l’idée étant qu’un pirate devra à la fois deviner l’utilisateur et le mot de passe si il parvenait à entrer dans une des machines de votre réseau local (ce qui est tout de même assez peu probable en soit, d’être visé personnellement par un pirate, mais quitte à faire de la sécurité autant imaginer le pire scénario et se protéger) :

Exemple de configuration ssh basique :

vim /etc/ssh/sshd_config
# Package generated configuration file
# See the sshd_config(5) manpage for details

# What ports, IPs and protocols we listen for
Port 22123
# Use these options to restrict which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes

# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 1024

# Logging
SyslogFacility AUTH
LogLevel VERBOSE

# Authentication:
LoginGraceTime 120
PermitRootLogin no
StrictModes yes
ClientAliveCountMax 2
Compression no
MaxAuthTries 3
MaxSessions 2
TCPKeepAlive no

AllowAgentForwarding no
AllowTcpForwarding no

RSAAuthentication yes
PubkeyAuthentication yes
#AuthorizedKeysFile %h/.ssh/authorized_keys

# Don't read the user's ~/.rhosts and ~/.shosts files
IgnoreRhosts yes
# For this to work you will also need host keys in /etc/ssh_known_hosts
RhostsRSAAuthentication no
# similar for protocol version 2
HostbasedAuthentication no
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
#IgnoreUserKnownHosts yes

# To enable empty passwords, change to yes (NOT RECOMMENDED)
PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication no

# Change to no to disable tunnelled clear text passwords
#PasswordAuthentication yes

# Kerberos options
#KerberosAuthentication no
#KerberosGetAFSToken no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes

X11Forwarding no
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
#UseLogin no

#MaxStartups 10:30:60
#Banner /etc/issue.net

# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

Subsystem sftp /usr/lib/openssh/sftp-server

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes

Redémarrer le service sshd pour prendre en compte les modifications (attention la connexion en root ne sera plus possible, s’assurer qu’on peut se connecter avec un autre utilisateur avant) :

service sshd restart

Pour continuer le hardening système, exécuter le script lynis pour obtenir d’autres pistes d’améliorations.

Partie 4 : Aller plus loin

Quelques points pour aller plus loin dans le projet :

  • Les caméras USB ont une forte tendance à planter au bout d’un certain temps de fonctionnement en continu, c’est le cas avec ma caméra USB. Il n’existe pas de réelle solution pour corriger ce problème puisque cela dépend souvent du firmware (buggé ou mal optimisé) inclut dans la caméra. Je fournirai ici quelques scripts permettant de redémarrer la caméra lorsqu’il est détecté que celle-ci est plantée.
  • Ajouter des scripts de surveillance sur le serveur pour vérifier que les caméras sont bien actives (stream accessible) et faire le nécessaire si ce n’est pas le cas (redémarrage du stream, envoi d’alerte). En cours de développement
  • Ajouter des scripts de surveillance sur les caméras pour vérifier que le serveur est bien accessible (allumé) et faire le nécessaire si ce n’est pas le cas. En cours de développement
  • Tenter d’ajouter une seconde camera analysée par motion.

Vous aimerez aussi...

1 réponse

  1. 07/05/2020

    […] Source : [Raspberry Pi] Système de vidéosurveillance avancé avec ustreamer et motion […]

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

%d blogueurs aiment cette page :