Portail captif

Source de la documentation : https://projectzme.wordpress.com

Procédure validée sur Debian 13.

Présentation du projet

La technique de portail captif (captive portal) consiste à forcer les clients HTTP d'un réseau de consultation à afficher une page web spéciale (le plus souvent dans un but d'authentification) avant d'accéder à Internet normalement.

Source : Wikipedia

Nous allons installer ce portail captif sur un ordinateur équipé de deux cartes réseaux :

enp1s0
Carte connectée à Internet, en DHCP.
enp2s0
Carte connectée au réseau à soumettre au portail captif. Adresse : 10.0.0.254/24. Un serveur DHCP.

Installation des paquets et configuration réseau

Nous allons installer les paquets nécessaires :

apt install apache2 php conntrack sudo isc-dhcp-server nftables net-tools

Configurer les interfaces réseau comme suit en éditant le fichier /etc/network/interfaces :

auto lo
iface lo inet loopback

auto enp1s0
iface enp1s0 inet dhcp

auto enp2s0
iface enp2s0 inet static
	address 10.0.0.254/24

Valider la configuration réseau :

systemctl restart networking

Configurer le serveur DHCP. Pour cela, éditez le fichier /etc/dhcp/dhcpd.conf et ajouter ce qui suit :

subnet 10.0.0.0 netmask 255.255.255.0 {
	range 10.0.0.10 10.0.0.200;
	option routers 10.0.0.254;
	option domain-name-servers 87.98.175.85; # DNS OpenNIC ns10.fr
	option subnet-mask 255.255.255.0;
	option broadcast-address 10.0.0.255;
}

Définissez l'interface d'écoute du serveur DHCP en modifiant la ligne suivante dans /etc/default/isc-dhcp-server :

INTERFACESv4="enp2s0"

Redémarrer le serveur DHCP :

systemctl restart isc-dhcp-server

Activer le routage en ajoutant la ligne suivante dans le fichier /etc/sysctl.d/forward.conf :

net.ipv4.ip_forward=1

Appliquer ce changement :

sysctl --system

Redirection du trafic vers le portail captif

Nous allons activer le NAT, identifier le trafic des clients et le rediriger vers le portail captif. Pour cela, modifier le fichier /etc/nftables.conf :

Pensez à créer une règle pour SSH si vous utilisez ce service pour configurer votre serveur. Ne vous enfermez pas dehors !
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority filter; policy drop;
		iifname lo accept
		ip protocol icmp accept
		udp dport 67 accept
		tcp dport 80 accept
		tcp dport 443 accept
		ct state { established, related } accept
	}

	chain forward {
		type filter hook forward priority filter;
	}

	chain output {
		type filter hook output priority filter;
	}

	chain prerouting {
		type nat hook prerouting priority dstnat;
	}

	chain postrouting {
		type nat hook postrouting priority srcnat;
		oifname enp1s0 meta mark 99 masquerade
	}
}

table ip filter {
	chain prerouting {
		type nat hook prerouting priority dstnat;
		iifname enp2s0 meta mark 0 tcp dport {80,443} dnat to 10.0.0.254
	}
}

table inet mangle {
	chain prerouting {
		type nat hook prerouting priority mangle;
	}
}

Activer et redémarrer nftables :

systemctl enable nftables.service
systemctl restart nftables.service

Création du portail captif

Notre portail captif comportera deux pages :

index.php
La page sur laquelle sera redirigée les utilisateurs non connectés (y mettre les CGU de votre service ou un formulaire d'authentification).
process.php
La page traitant les demandes d'authentification.

Notre exemple présentera une page avec un simple lien afin d'accéder à Internet.

Voici le contenu de la page /var/www/html/index.php :

<!DOCTYPE html>
<html lang="fr">
	<head>
	 	<meta charset="utf-8">
		<meta name="author" content="Antoine Pernot">
		<title>Portail captif</title>
	</head>
	<body>
		<h1>Portail captif</h1>
		<p>Cliquez <a href="http://10.0.0.254/process.php">ici</a> pour accéder à Internet. </p>
	</body>
</html>

Voici le contenu de la page /var/www/html/process.php.

<?php
	$ip = $_SERVER['REMOTE_ADDR'];
	exec("sudo /opt/captive-portal/add-rule ".$ip);
	sleep(1);
	header('Location: http://www.google.com');
	exit;
?>

Remplacez http://www.example.com par la page à afficher dès qu'un client est authentifié. Vous pouvez notamment placer dans le formulaire d'authentification un champ caché contenant l'URL demandée afin de rediriger le client vers la page qu'il a souhaité :

$schema = (@$_SERVER["HTTPS"] == "on") ? "https://" : "http://";
$url = $schema.$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];

Nous allons créer les scripts permettant d'autoriser un utilisateur à outrepasser le portail captif et de révoquer cet accès.

mkdir /opt/captive-portal

Voici le script appelé par la page process.php pour autoriser un utilisateur. Pour cela, créer le fichier /opt/captive-portal/add-rule :

#!/bin/bash
ip=$1
mac=$(arp -an $ip | sed -n 's/.*\([a-zA-Z0-9]\{2\}\(:[a-zA-Z0-9]\{2\}\)\{5\}\).*/\1/p')
nft add rule inet mangle prerouting ether saddr $mac mark set 99 return
conntrack -D --src $ip

Le script pour révoquer les accès est situé dans /opt/captive-portal/del-rule :

#!/bin/bash
ip=$1
mac=$(arp -an $ip | sed -n 's/.*\([a-zA-Z0-9]\{2\}\(:[a-zA-Z0-9]\{2\}\)\{5\}\).*/\1/p')
handle=$(nft -a list ruleset | grep $mac | sed -r 's/.*# handle ([^ ]+).*/\1/')
nft delete rule inet mangle prerouting handle $handle
conntrack -D --src $ip

Rendons ces scripts exécutables :

chmod +x /opt/captive-portal/add-rule
chmod +x /opt/captive-portal/del-rule

Accorder les privilèges au serveur Web pour ajouter les règles nécessaires. Pour cela, exécuter la commande suivante :

visudo

et ajouter les lignes suivantes :

www-data ALL=NOPASSWD: /opt/captive-portal/add-rule [0-9]*.[0-9]*.[0-9]*.[0-9]*

Pour révoquer un client, exécuter la commande suivante en modifiant son adresse IP :

/opt/captive-portal/del-rule 10.0.0.10