Un hotspot wifi qui filtre les urls

Written by Sebastien Lambot on . Posted in Réseaux

Vous souhaitez partager votre connection wifi mais garder le contrôle sur les accès? N’achetez pas un firewall cher et compliqué, utilisez un Raspberry Pi!

accesspoint1

Prérequis

  • Raspbian installé et configuré (SSH autorisé)
  • dongle wifi branché (DWA-127 dans mon cas)
  • connexion réseau RJ45 branchée

Vérifiez que le dongle Wifi peut être configuré en access point:

Compilez et installez iw (utilitaire pour les interfaces wifi):

sudo apt-get install gcc make libnl1 libnl-dev pkg-config
wget https://www.kernel.org/pub/software/network/iw/iw-3.14.tar.gz
tar -xvzf iw-3.14.tar.gz
cd iw3.14
make
./iw list

Le mode « AP » doit être présent dans les « supported interface modes »:

portal1

 

Installation de l’access point (AP)

Installez les paquets nécessaires:

apt-get install hostapd nginx isc-dhcp-server iptables iptables-persistent

Hostapd est utilisé pour créer l’AP (config dans /etc/default/hostapd et /etc/hostapd/hostapd.conf).
Nginx est un serveur web (config dans /etc/nginx/sites-available/default).
Isc-dhcp-server est un serveur dhcp (config dans /etc/default/isc-dhcp-server et /etc/dhcp/dhcpd.conf).
Iptables est un firewall.

Configurez l’ip des interfaces en éditant le fichier (avec sudo nano) /etc/network/interfaces. Dans mon cas, le réseau ethernet est 192.168.100.0/24 tandis que le réseau wifi sera 192.168.10.0/24

auto lo

iface lo inet loopback

iface eth0 inet static
address 192.168.100.200
netmask 255.255.255.0
network 192.168.100.0
gateway 192.168.100.1
dns-nameservers 192.168.100.1

allow-hotplug wlan0
iface wlan0 inet static
address 192.168.10.1
netmask 255.255.255.0
network 192.168.10.0

Vous pouvez enlever le wpa-supplicant

sudo mv /usr/share/dbus-1/system-services/fi.epitest.hostap.WPASupplicant.service ~/

Rebootez ensuite pour que les adresses IP soient assignées

sudo reboot

Hostapd

Dans le fichier /etc/default/hostapd décommentez la ligne

DAEMON_CONF="/etc/hostapd/hostapd.conf"

et créez le fichier mentionné /etc/hostapd/hostapd.conf

# interface wlan du Wi-Fi
interface=wlan0
# nl80211 avec tous les drivers Linux mac80211
driver=nl80211
# Nom du spot Wi-Fi
ssid=PiHomeServerAP
# mode Wi-Fi (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g)
hw_mode=g
# canal de fréquence Wi-Fi (1-14)
channel=6
# Wi-Fi ouvert, pas d'authentification !
auth_algs=1
# Beacon interval in kus (1.024 ms)
beacon_int=100
# DTIM (delivery trafic information message)
dtim_period=2
# Maximum number of stations allowed in station table
max_num_sta=255
# RTS/CTS threshold; 2347 = disabled (default)
rts_threshold=2347
# Fragmentation threshold; 2346 = disabled (default)
fragm_threshold=2346

et démarrez ensuite le service hostapd

sudo service hostapd start

A ce moment-ci, le réseau Wifi devrait apparaître dans la liste des réseaux disponibles. Vous pouvez donc demander à démarrer ce service au prochain reboot:

update-rc.d hostapd enable

Serveur DHCP

Configurez le serveur dhcp en ajoutant les lignes suivantes au fichier /etc/dhcp/dhcpd.conf

subnet 192.168.10.0 netmask 255.255.255.0 {
range 192.168.10.10 192.168.10.200;
option routers 192.168.10.1;
option domain-name-servers 192.168.100.1;
}

Modifiez également le fichier /etc/default/isc-dhcp-server afin que le serveur dhcp ne soit actif que sur l’interface wlan0

INTERFACES="wlan0"

Vous pouvez ensuite démarrer le service dhcp

sudo service isc-dhcp-server start

Afin de résoudre un problème qui empêche l’attribution de l’IP fixe à l’interface wlan0 lors du démarrage, il faut éditer le fichier /etc/default/ifplugd afin qu’il contienne les lignes suivantes:

INTERFACES="eth0"
HOTPLUG_INTERFACES="eth0"
ARGS="-q -f -u0 -d10 -w -I"
SUSPEND_ACTION="stop"

A ce stade-ci, vous devriez pouvoir vous connecter au wifi et recevoir une adresse ip.Vous pouvez donc demander à démarrer ce service au prochain reboot:

update-rc.d isc-dhcp-server enable

Configuration de la liaison entre les deux interfaces

Activez l’ip forwarding (et donc le routage) en ajoutant la ligne suivante dans le fichier /etc/sysctl.conf

net.ipv4.ip_forward=1

Ce sera pris en compte au prochain reboot, mais pour éviter de redémarrer, vous pouvez lancer la commande

sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"

Faites ensuite le lien entre les deux interfaces avec iptables

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT

et sauvez la configuration

sudo sh -c "iptables-save > /etc/iptables/rules.v4"

Vous pouvez ensuite tester le reload de la config avec iptables-persistent

sudo service iptables-persistent restart

et l’activer lors du démarrage

sudo update-rc.d iptables-persistent defaults

Votre hotspot wifi est maintenant configuré. Faites un reboot afin de valider la config et vous devriez pouvoir accéder au net en vous connectant sur son wifi.

Dnsmasq, le serveur DNS

Pour l’installer:

sudo apt-get install dnsmasq

Et nous pouvons directement plonger dans son fichier de configuration /etc/dnsmasq.conf pour activer les paramètres ci-dessous

domain-needed
bogus-priv
filterwin2k
interface=wlan0

L’explication de ces paramètres se trouve dans le fichier même, rien de bien compliqué.

Maintenant que le serveur DNS est configuré, nous pouvons l’utiliser en spécifiant son adresse dans le fichier de config du serveur DHCP /etc/dhcp/dhcpd.conf :

option domain-name-servers 192.168.10.1;

Le service démarre automatiquement lors du boot.

Un filtrage du trafic est possible mais est également facilement contournable, ce point sera développé plus loin lors de la mise en place d’un proxy transparent avec filtrage des urls.

Installation du proxy

Nginx, notre serveur web

Installez les packages additionnels

sudo apt-get install php5-fpm libgd2-xpm libpcrecpp0 libxpm4

et ensuite vous pouvez installer nginx si ce n’est pas encore fait

sudo apt-get install nginx

Il faut ensuite créer le répertoire qui contiendra les fichiers web

sudo mkdir /var/www
sudo chown -R www-data:www-data /var/www

Créez ensuite un fichier index.php dans ce répertoire avec le contenu suivant

<?php phpinfo(); ?>

Modifiez le fichier de config de nginx /etc/nginx/sites-available/default

root /var/www;
index index.php index.html index.htm;

Et décommentez les 2 parties (sauf la ligne fastcgi_pass)

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
# # With php5-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# # With php5-fpm:
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.ht {...}

Pour combler une faille de sécurité, il faut ensuite aller modifier le fichier /etc/php5/fpm/php.ini et décommenter la ligne

;cgi.fix_pathinfo=1

Nous pouvons finalement redémarrer nginx

sudo service nginx restart

et tester la page web du serveur qui devrait afficher les infos de php. Vous pouvez maintenant modifier la page index.php pour qu’elle affiche par exemple des conditions d’utilisation avec un bouton submit « I agree », ou encore implémenter un login, un enregistrement des adresses ip/mac, etc. Libre à vous de coder la page php qui vous convient le mieux. Cette page sera par la suite affichée lors du blocage des sites non-autorisés.

Squid, le serveur proxy

Pour installer squid3:

sudo apt-get install squid3

Recréez le fichier de config /etc/squid3/squid.conf (faites-en un backup .bak avant):

#Access Lists
acl manager proto cache_object
acl localhost src 127.0.0.1/32
acl guest_network src 192.168.10.0/24

#Ports allowed through Squid
acl Safe_ports port 80 #http
acl Safe_ports port 443 #https
acl SSL_ports port 443
acl SSL method CONNECT
acl CONNECT method CONNECT

#allow/deny
http_access allow localhost
http_access allow guest_network
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access deny all

#proxy ports
http_port 192.168.10.1:3128
http_port 192.168.10.1:8080 intercept

#caching directory
#cache_dir ufs /home/user/squidcache/ 2048 16 128
#cache_mem 1024 MB

#refresh patterns for caching static files
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i \.(gif|png|jpg|jpeg|ico)$ 10080 90% 43200 override-expire ig$
refresh_pattern -i \.(iso|avi|wav|mp3|mp4|mpeg|swf|flv|x-flv)$ 43200 90% 432000$
refresh_pattern -i \.(deb|rpm|exe|zip|tar|tgz|ram|rar|bin|ppt|doc|tiff)$ 10080 $
refresh_pattern -i \.index.(html|htm)$ 0 40% 10080
refresh_pattern -i \.(html|htm|css|js)$ 1440 40% 40320
refresh_pattern . 0 40% 40320

#nameservers
dns_nameservers 192.168.10.1

et redémarrez squid:

sudo service squid3 restart

Squid démarre automatiquement avec le raspberry pi.

Vous pouvez tester qu’il fonctionne correctement en allant sur internet à partir d’une machine connectée au wifi et pour laquelle le proxy à été configuré pour pointer vers 192.168.10.1:3128 ou :8080. Comme nous n’avons pas encore configuré de restrictions au niveau des ACL, tout le trafic est ouvert. Pour tester que le trafic passe bien via le proxy, stoppez le service squid3 et vous verrez que vos pages internet ne s’afficheront plus.

Mise en place du filtrage

Jusqu’à présent, l’installation était facile. La mise en place du filtrage va être plus ardue, donc accrochez-vous!

Le scénario ici consistera à autoriser quelques urls (nous utiliserons http://perdu.com pour l’exemple), tout en bloquant tout le reste (https, applications, ping, etc.).

Configuration de dnsmasq

Editez le fichier /etc/dnsmasq.conf pour lui ajouter quelques paramètres:

server=8.8.8.8
server=208.67.220.220

J’ai ajouté les serveurs de Google et d’Opendns pour les résolutions DNS externes. Pour un blocage au niveau IP, on peut même aller jusqu’à activer le no-resolv et n’utiliser que le fichier /etc/hosts mais il est préférable d’utiliser iptables pour bloquer les IPs  ou carrément des subnets entiers (une résolution DNS n’est au final qu’une traduction vers une IP. Limiter les requêtes DNS est inutile si les accès se font directement vers les IPs).

Dans ce cas-ci, j’autorise les résolutions DNS mais le trafic sera filtré.

Iptables (netfilter) pour filtrer les paquets IP

La configuration d’iptables nécessite de bien comprendre les couches 3 et 4 du modèle OSI ainsi que leur fonctionnement. Iptables est intégré à Raspbian et n’a donc pas besoin d’être installé.

Voici la configuration qui correspond à nos besoins:

iptables2

 

 

Pas d’inquiétude, chaque règle est expliquée:

  • INPUT 1,2,3: autorise les utilisateur du wlan0 a effectuer des requêtes vers les ports web (80,8080) et DNS (53 udp)
  • INPUT 4: permet à un admin situé du côté eth0 de se connecter en SSH sur le raspberry (cette règle ne doit pas être supprimée car elle est notre porte d’accès en cas de blocage non intentionnel lors de la config)
  • INPUT 5,6: on rejette les autres connections provenant de wlan0 mais on autorise celles venant de eth0
  • INPUT 7: les echo-reply des ping ICMP sont bloquées, ce qui entraînera un « request timeout » lorsqu’on voudra faire un ping à partir du wlan0
  • INPUT8: les paquets dont le trafic a déjà été établi (state related, established) sont autorisés
  • INPUT default rule: le reste est accepté (si je droppe par défaut, les connections web ne s’établissent pas)
  • OUTPUT: tout est autorisé, le filtrage est fait uniquement sur les INPUT dans notre cas
  • FORWARD: tout le trafic est droppé, sauf les requêtes https (tcp 443) qui sont rejetées (afin de faire savoir au navigateur qu’il ne doit pas attendre une connection). C’est le seul moyen de bloquer toutes les requêtes https car Squid n’est pas capable de jouer le rôle de proxy lorsque des certificats entrent en jeu (à moins d’utiliser sslbump dans une config « squid-in-the-middle »).

Pour la table nat:

  • la règle de PREROUTING indique que le trafic entrant sur wlan0 avec comme port de destination 80 sera redirigé vers le port 8080 (notre proxy Squid)
  • la règle de POSTROUTING permet de faire du nat en remplaçant l’ip source par l’ip de l’interface eth0 (masquerade), ce qui permet à wlan0 d’avoir accès à internet
sudo iptables -P INPUT ACCEPT
sudo iptables -A INPUT -i wlan0 -p tcp -m tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -i wlan0 -p udp -m udp --dport 53 -j ACCEPT
sudo iptables -A INPUT -i wlan0 -p tcp -m tcp --dport 8080 -j ACCEPT
sudo iptables -A INPUT -i eth0 -p tcp -m tcp --dport 22 -m comment --comment "SSH access, do not delete" -j ACCEPT
sudo iptables -A INPUT -i wlan0 -j DROP
sudo iptables -A INPUT -i eth0 -j ACCEPT
sudo iptables -A INPUT -p icmp -m icmp --icmp-type 0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -p tcp -m tcp --dport 443 -j REJECT --reject-with icmp-port-unreachable
sudo iptables -A PREROUTING -i wlan0 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
sudo iptables -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
sudo sh -c "iptables-save > /etc/iptables/rules.v4"

Squidguard, pour filtrer les urls passant par Squid3

Pour installer squidguard:

sudo apt-get install squidguard

Un peu de configuration est nécessaire:

sudo rm -rf /etc/squid
sudo ln -s /etc/squid3 /etc/squid
ls -l /etc | grep squid

Editez le fichier /etc/squid3/squid.conf et ajoutez à la fin du fichier le lien vers le module squidguard:

# SquidGuard config
url_rewrite_program /usr/bin/squidGuard -c /etc/squidguard/squidGuard.conf
url_rewrite_children 5

Editez ensuite le fichier de config de squidguard /etc/squidguard/squidGuard.conf :

dbhome /var/lib/squidguard/db
logdir /var/log/squidguard
...
src clients {
 ip 192.168.10.0/24
}
...
dest whitelist {
 domainlist whitelist
 urllist whiteurl
}
...
acl {
 clients {
 pass whitelist none
 redirect http://192.168.10.1
 }

 default {
 pass none
 redirect http://192.168.10.1
 }
}

En gros, tout ce qui n’est pas défini dans la whitelist renvoie vers http://192.168.10.1 (notre page html fournie par nginx)

Créez le fichier whitelist ( /var/lib/squidguard/db/whitelist ) contenant les domaines autorisées:

perdu.com
it-tude.be

Ainsi que le fichier whiteurl ( /var/lib/squidguard/db/whiteurl ) contenant les urls autorisées. Ces urls DOIVENT être accessibles par vos clients, sinon une page de login s’ouvrira car le système client aura détecté que vous êtes derrière un captive portal grâce au protocole WISPr. Le protocole WISPr, intégré à la plupart des appareils teste de manière invisible l’accès à une page pour détecter la présence de captive portal. Dans le cas où un captive portal est détecté, une page web s’ouvre en popup et pointe vers la page de connexion du captive portal. (références: Erratasec, cadinc, wikipedia)

www.apple.com/library/test/success.html
www.google.com/blank.html
clients3.google.com/generate_204
captive.apple.com
www.appleiphonecell.com

Et changez le propriétaire du répertoire db et de tous ses fichiers (sinon vous aurez une erreur « db_open permission denied » dans squidguard, qui va alors passer en mode emergency et ne filtrera rien du tout):

sudo chown -R proxy:proxy /var/lib/squidguard/db

N’oubliez pas de recompiler les règles de squidGuard (attention à la majuscule présente dans certains cas) afin de créer les fichiers .db nécessaires:

sudo /usr/bin/squidGuard -C all

et vérifiez que tout s’est bien passé grâce au log:

sudo tail /var/log/squidguard/squidGuard.log

Vous pouvez alors faire un reload de squid3:

sudo service squid3 reload

Si vous retournez dans le log de squidguard, vous devriez voir comme dernière ligne « squidGuard ready for requests » qui confirme que tout est ok.

Rendre l’access point « plug’n’play »

Pour qu’il soit plug’n’play, sa configuration ne doit pas dépendre de ce qui se trouve du côté de l’interface eth0.

Nous devons donc reconfigurer /etc/network/interfaces pour mettre l’interface en dhcp (sauf si vous préférez garder une ip fixe, ce qui est l’idéal):

iface eth0 inet dhcp

Et c’est tout!

Modifier la liste des urls autorisées

Modifiez le fichier whitelist ( /var/lib/squidguard/db/whitelist ) contenant les urls autorisées:

perdu.com
it-tude.be

Recompilez les règles de squidGuard:

sudo /usr/bin/squidGuard -C all

Reload de Squid et vérification du log:

sudo tail /var/log/squidguard/squidGuard.log
sudo service squid3 reload
sudo tail /var/log/squidguard/squidGuard.log

Dernier check

Pour la page vers laquelle les utilisateurs sont renvoyés en cas d’accès à une url non autorisée, j’ai simplement créé un fichier /var/www/index.php qui redirige les utilisateurs vers un site par défaut:

<?php header( 'Location: http://www.perdu.com' ) ; ?>

Une dernière petite précision à apporter du poitn de vue de l’utilisation: les urls en http sont traitées par Squid et SquidGuard mais les urls en https (facebook par exemple) sont tout simplement bloquées et n’aboutissent pas (message d’erreur: webpage not available).

Sécurité

L’utilisateur root est désactivé par défaut, laissez ce paramètre comme ça.

Par contre il faudra absolument changer le mot de passe de l’utilisateur pi avec la commande sudo passwd.

Troubleshoot

En cas de pépin, il suffit de brancher un clavier et un écran (hdmi) et vous aurez un accès direct à la console (si celle-ci n’est plus accessible en ssh depuis eth0).

En cas d’oubli du mot de passe de l’utilisateur, il vous faudra booter en console runlevel 1 avec sudo init 1 pour faire un passwd.

accesspoint2

Tags: , , , , , , , , , ,

Trackback from your site.

Comments (2)

  • sovakle

    |

    Bonjour,
    Je suis très intéressé par cette solution pour mettre à disposition un spot wifi mais en filtrant les consultations. Je cherche notamment à comprendre iptables en profondeur ainsi que squid.

    Une remarque préliminaire. La commande
    sudo iptables -A PREROUTING -i wlan0 -p tcp -m tcp –dport 80 -j REDIRECT –to-ports 8080
    retourne une erreur car PREROUTING n’est pas une chaîne de la table filter qui est la table par défaut si non précisée dans une commande.

    Il faut donc ajouter
    -t nat
    pour que la commande ne produise pas d’erreur et soit bien ajoutée à la table nat

    Néanmoins il y a un point qui m’échappe. Supposons qu’un utilisateur connecté au wifi du spot ouvre son navigateur web et ouvre une adresse http, disons http://www.legeek.info pour l’exemple. Voici ma vision de ce qui se passe: sa requête produit des paquets avec le port 80 et avec comme adresse de destination 176.139.7.154 (qui est l’IP de http://www.legeek.info résolue par DNS). Ces paquets arrivent dans le firewall du rasp par l’interface wlan0.

    La commande ci-dessus matche sur ces paquets et change le port 80 en le port 8080. Mais cette commande ne change pas l’adresse de destination qui reste 176.139.7.154. Comment se fait-il alors qu’un processus tournant sur le rasp (donc ici squid) et écoutant le port 8080 puisse récupérer ce paquet qui n’est pas destiné au rasp lui-même mais à une IP externe ?

    Reply

    • Sebastien Lambot

      |

      Bonjour,
      C’est le principe d’encapsulation: le paquet contient une IP de destination, celle du gateway (le routage vers un subnet différent se fait au niveau du gateway où il y aura dé-encapsulation et ré-encapsulation avec l’ip de destination, celle du prochain routeur).
      Lors de l’initation de la connexion, iptables redirige vers le port 8080 et Squid est configuré pour intercepter le trafic sur ce port 8080.

      Reply

Leave a comment

You must be logged in to post a comment.