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.
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é.
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.
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.
root@host:~# apt install micro-httpd
[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
[Unit]
Description=micro-httpd
Documentation=man:micro-httpd(8)
[Socket]
ListenStream=0.0.0.0:80
Accept=true
[Install]
WantedBy=sockets.target
<!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
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.
root@host:~# apt install unbound
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
*.orange.fr IN A 192.168.0.200
*.google.fr IN A 192.168.0.200
root@host:~# systemctl restart unbound
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.
website.to.block CNAME .
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 !
root@host:~# wget https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/multi.txt
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).
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.
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.
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.
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.
root@host:~# export EDITOR=vim
root@host:~# systemctl edit unbound.service
### 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
root@host:~# systemctl restart unbound
Contact :