rss logo

Mise en place d'un serveur de filtrage Web avec Unbound et RPZ sur Linux

Unbound Logo

Je cherchais un moyen de mettre en place un filtrage Web sur GNU/Linux. Je connaissais la méthode avec l'utilisation de squidguard, mais je la trouve un peu pénible à configurer (les paramètres proxy) et assez difficile à déployer automatiquement sur chaque poste de travail, surtout lorsqu'ils ne sont pas gérés par un domaine Active Directory. Bref j'ai découvert en lisant un article sur le pare-feu open source DynFi: https://dynfi.com/, que ce dernier faisait du filtrage Web avec RPZ: https://fr.wikipedia.org/. J'ai donc commencé à étudier cette solution et j'ai trouvé un moyen de mettre en place le filtrage Web avec RPZ.

Schéma réseau

Dans cette architecture, nous aurons un serveur Debian agissant à la fois en tant que serveur DNS et serveur web. Lorsqu'un client fait une demande pour un site interdit (selon une liste de blocage préétablie), il sera redirigé vers le serveur web, avec une page web l'avertissant que le contenu est bloqué.

  • Prérequis :
    • Bloquer le port 53 (UDP et TCP) sur votre passerelle pour empêcher les postes de travail d'effectuer des requêtes vers des serveurs DNS externes.
Diagramme réseau illustrant une architecture de web filtering avec un poste de travail et un serveur DNS Unbound

Serveur Debian

Logo Debian

Comme décrit précédemment, nous aurons deux services sur notre serveur Debian : un serveur web pour afficher un message texte simple informant les utilisateurs qu'ils tentent de se connecter à un site web interdit, et un service DNS pour fournir aux clients des réponses DNS correctes ou modifiées. Pour le serveur web, j'utiliserai micro-httpd, qui est un serveur HTTP léger parfaitement adapté à nos besoins ici, et Unbound en tant que serveur DNS.

micro-httpd

Pour informer les utilisateurs que la page demandée est bloquée, nous aurons besoin d'un serveur web qui affichera une page web les informant que le site web auquel ils essaient d'accéder est interdit.

Installation

  • Installer le paquet micro-httpd:
root@host:~# apt install micro-httpd

Configuration

  • La configuration de micro-httpd se trouve dans le fichier
    • /lib/systemd/system/micro-httpd@.service :
[Unit] Description=micro-httpd Documentation=man:micro-httpd(8) [Service] User=nobody Group=www-data ExecStart=-/usr/sbin/micro-httpd /var/www/html StandardInput=socket
  • Et le fichier
    • /lib/systemd/system/micro-httpd.socket :
[Unit] Description=micro-httpd Documentation=man:micro-httpd(8) [Socket] ListenStream=0.0.0.0:80 Accept=true [Install] WantedBy=sockets.target
  • Créer un fichier /var/www/html/index.html et définir les autorisations appropriées :
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Access Forbidden</title> </head> <body> <h1>Access Forbidden</h1> <p>Sorry, but you do not have permission to access this page.</p> </body> </html> root@host:~# chown -R www-data:www-data /var/www/html
  • Si nécessaire, pour redémarrer micro-httpd on utilisera la commande suivante :
root@host:~# systemctl restart micro-httpd.socket

Ouvrir un navigateur web et accéder à l'adresse http://192.168.0.200/ pour vérifier que l'on accède bien à la page web bloquée.

Unbound

Installation

  • Installer le paquet unbound:
root@host:~# apt install unbound

Configuration

  • Créer un fichier /etc/unbound/unbound.conf.d/rpz.conf:
server: module-config: "respip validator iterator" # Load respip, validator, and iterator modules interface: 192.168.0.200 # Network interface used for DNS queries interface: 127.0.0.1 # Loopback interface used for local DNS queries do-ip4: yes # Enable IPv4 support do-ip6: no # Disable IPv6 support do-udp: yes # Enable UDP support for DNS queries do-tcp: yes # Enable TCP support for DNS queries do-daemonize: yes # Run Unbound as a daemon (in the background) access-control: 0.0.0.0/0 allow # Allow all IP addresses to make DNS queries local-zone: "std.priv." static # Define a local zone for DNS queries local-data: "denied.std.priv. IN A 192.168.0.200" # Define a local DNS entry for a specific domain local-data-ptr: "192.168.0.200 denied.std.priv." # Define a local DNS PTR entry for a specific IP address rpz: name: rpz.std.rocks # RPZ zone name zonefile: /etc/unbound/blacklist.zone # RPZ zone file used for DNS query filtering
  • Créer un fichier /etc/unbound/blacklist.zone où, à des fins de test, nous redirigerons chaque requête pour orange.fr et google.fr vers notre page web bloquée :
*.orange.fr IN A 192.168.0.200 *.google.fr IN A 192.168.0.200
  • Redémarrer le service unbound :
root@host:~# systemctl restart unbound

Workstation

  • Depuis la station de travail, essayer d'ouvrir www.google.fr. Si la redirection fonctionne on devrait être redirigé vers la page de blocage :
Capture d'écran du navigateur Firefox affichant la page 'Accès interdit'
  • Lorsque nous effectuons une requête DNS, nous constatons que www.google.fr et orange.fr sont redirigés vers l'adresse 192.168.0.200 :
Capture d'écran de la fenêtre de commandes Windows (CMD) avec deux commandes 'nslookup'

Téléchargement et application d'une liste de blocage

Maintenant que nous avons construit notre système de filtrage web, il est temps de le rendre utile. Pour ce faire, nous allons télécharger un fichier de liste de blocage et le formater pour le faire fonctionner avec notre architecture. Il existe de nombreuses listes différentes disponibles sur Internet (chercher avec le terme liste de blocage RPZ). Essayons l'une des listes disponibles sur https://github.com/hagezi/dns-blocklists.

  • Les listes RPZ disponibles ont ce format :
website.to.block CNAME .
  • Cependant, pour être utile dans notre cas, nous avons besoin de les reformater en :
website.to.block IN A 192.168.0.200

Alors, comment traduire le format par défaut en celui utile dans notre architecture, tel qu'indiqué ci-dessus ? Il existe différentes approches, mais personnellement, j'utiliserai l'éditeur sed. Voyons comment faire !

  • Tout d'abord, télécharger l'une des listes RPZ :
root@host:~# wget https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/multi.txt
  • Ensuite, formater-la en utilisant sed :
root@host:~# sed -i 's/CNAME.*/IN A 192.168.0.200/' multi.txt

Note : Pour empêcher les utilisateurs de contourner la politique, je recommande d'ajouter une liste DoH/VPN/TOR/Proxy et de bloquer les adresses IP DoH sur votre pare-feu. (Consultez cette liste : https://github.com/crypt0rr/public-doh-servers).

Aller plus loin

Une chose qui peut être utile est d'utiliser le contrôle d'accès afin de pouvoir appliquer des filtres différents à des réseaux ou hôtes. Voyons comment cela fonctionne.

  • Modifiez le fichier de configuration /etc/unbound/unbound.conf.d/rpz.conf :
server: module-config: "respip validator iterator" # Load respip, validator, and iterator modules interface: 192.168.0.200 # Network interface used for DNS queries interface: 127.0.0.1 # Loopback interface used for local DNS queries do-ip4: yes # Enable IPv4 support do-ip6: no # Disable IPv6 support do-udp: yes # Enable UDP support for DNS queries do-tcp: yes # Enable TCP support for DNS queries do-daemonize: yes # Run Unbound as a daemon (in the background) access-control: 0.0.0.0/0 allow # Allow all IP addresses to make DNS queries local-zone: "std.priv." static # Define a local zone for DNS queries local-data: "denied.std.priv. IN A 192.168.0.200" # Define a local DNS entry for a specific domain local-data-ptr: "192.168.0.200 denied.std.priv." # Define a local DNS PTR entry for a specific IP address define-tag: "social adult dnsbypass" access-control-tag: 192.168.10.0/24 "social adult dnsbypass" access-control-tag: 192.168.10.200/32 "social adult" access-control-tag: 192.168.20.0/24 "adult dnsbypass" rpz: name: rpz.social.std.rocks # RPZ zone name zonefile: /var/lib/unbound/social_networks/blacklist.zone tags: "social" rpz: name: rpz.adult.std.rocks # RPZ zone name zonefile: /var/lib/unbound/adult/blacklist.zone tags: "adult" rpz: name: rpz.dnsbypass.std.rocks # RPZ zone name zonefile: /var/lib/unbound/dns_bypass/blacklist.zone tags: "dnsbypass"

Comme nous pouvons le voir ci-dessus, nous avons créé trois types de listes de filtrage web : social (réseaux sociaux), adulte et dnsbypass. Nous avons appliqué les listes social adulte dnsbypass au réseau 192.168.10.0/24, les listes social adulte à l'hôte 192.168.10.200 et les listes adulte dnsbypass au réseau 192.168.20.0/24.

Dépannage

L'utilisation de listes comportant plus de centaines de milliers d'entrées peut provoquer une erreur lors du démarrage d'unbound. En général, cela est dû au fait que le service met trop de temps à démarrer et qu'il est automatiquement arrêté par le système. Voici comment modifier le délai de démarrage du service unbound.

  • Message d'erreur lors du démarrage du service unbound:
root@host:~# systemctl restart unbound Job for unbound.service failed because a timeout was exceeded. See "systemctl status unbound.service" and "journalctl -xeu unbound.service" for details.
  • (Optionnel) On paramètre notre éditeur, personnelement j'utilise vim:
root@host:~# export EDITOR=vim
  • Modifier les paramètres systemd pour le service unbound:
root@host:~# systemctl edit unbound.service
  • Ajouter ces trois lignes afin d'augmenter la durée que le système permet au service unbound pour démarrer:
### Editing /etc/systemd/system/unbound.service.d/override.conf ### Anything between here and the comment below will become the new contents of the file [Service] TimeoutStartSec=300 TimeoutStopSec=300 ### Lines below this comment will be discarded ### /lib/systemd/system/unbound.service # [Unit] # Description=Unbound DNS server # Documentation=man:unbound(8) # After=network.target # Before=nss-lookup.target # Wants=nss-lookup.target # # [Service] # Type=notify # Restart=on-failure # EnvironmentFile=-/etc/default/unbound # ExecStartPre=-/usr/libexec/unbound-helper chroot_setup # ExecStartPre=-/usr/libexec/unbound-helper root_trust_anchor_update # ExecStart=/usr/sbin/unbound -d -p $DAEMON_OPTS # ExecStopPost=-/usr/libexec/unbound-helper chroot_teardown # ExecReload=+/bin/kill -HUP $MAINPID # # [Install] # WantedBy=multi-user.target
  • Le service unbound devrait maintenant pouvoir se lancer sans erreur:
root@host:~# systemctl restart unbound
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Contact :

contact mail address