Portail captif

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

Procédure validée sur Debian 8.

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 :

eth0
Carte connectée à Internet, en DHCP.
eth1
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-get install apache2 php5 conntrack sudo isc-dhcp-server

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

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet static
	address 10.0.0.254
	network 10.0.0.0
	netmask 255.255.255.0
	post-up bash /etc/iptables.rules.sh

Valider la configuration réseau :

service networking restart

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;
}

Redémarrer le serveur DHCP :

service isc-dhcp-server restart

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

net.ipv4.ip_forward=1

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, créer le fichier /etc/iptables.rules.sh :

iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE 
iptables -t mangle -N internet
iptables -t mangle -A internet -j MARK --set-mark 99
iptables -t mangle -A PREROUTING -i eth1 -p tcp -m tcp --dport 80 -j internet
iptables -t nat -A PREROUTING -i eth1 -p tcp -m mark --mark 99 -m tcp --dport 80 -j DNAT --to-destination 10.0.0.254
iptables -t mangle -A PREROUTING -i eth1 -p tcp -m tcp --dport 443 -j internet
iptables -t nat -A PREROUTING -i eth1 -p tcp -m mark --mark 99 -m tcp --dport 443 -j DNAT --to-destination 10.0.0.254

Nous avons fait en sorte que le script soit exécuté après la configuration de l'interface eth1 lors de l'édition du fichier /etc/network/interfaces.

Création du portail captif

Notre portail captif comportera trois 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.
kick.php
Cette page permet de révoquer l'authentification d'un utilisateur et l'oblige à s'authentifier de nouveau.

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

Voici le contenu de la page 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 process.php.

<?php
    $ip = $_SERVER['REMOTE_ADDR'];
    $arp = "/usr/sbin/arp";
    $mac = shell_exec("sudo $arp -an " . $ip);
    preg_match('/..:..:..:..:..:../',$mac , $matches);
    $mac = @$matches[0];
    exec("sudo iptables -I internet 1 -t mangle -m mac --mac-source $mac -j RETURN");
    exec("sudo rmtrack " . $ip);
    sleep(1);
    header('Location: http://www.example.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"];

Voici le contenu de la page kick.php :

<?php
    $ip = $_GET['ip'];	// Entrer ici l'adresse IP du client à révoquer. 
    $arp = "/usr/sbin/arp";
    $mac = shell_exec("sudo $arp -an " . $ip);
    preg_match('/..:..:..:..:..:../',$mac , $matches); $mac = @$matches[0];
    if( $mac === NULL) {
        echo "Erreur: Impossible de retrouver l'adresse MAC.";
        exit;
    }
    while( $chain = shell_exec("sudo iptables -t mangle -L | grep ".strtoupper($mac) ) !== NULL ) {
        exec("sudo iptables -D internet -t mangle -m mac --mac-source ".strtoupper($mac)." -j RETURN");
    }
    exec("sudo rmtrack " . $ip);
    echo "Utilisateur révoqué";
?>

Nous allons créer le script permettant à un client d'outrepasser le portail captif. Pour cela, éditer le fichier /usr/bin/rmtrack :

/usr/sbin/conntrack -L  | grep $1  | grep ESTAB  | grep 'dport=80'  | awk  "{ system(\"conntrack -D --orig-src $1 --orig-dst \"  substr(\$6,5) \" -p tcp --orig-port-src \" substr(\$7,7) \"  --orig-port-dst 80\"); }"

Changer les droits sur le script :

chmod 755 /usr/bin/rmtrack

Accorder les privilèges au serveur Web pour effectuer ces opérations. Pour cela, exécuter la commande suivante :

visudo

et ajouter les lignes suivantes :

www-data ALL=NOPASSWD: /usr/sbin/arp
www-data ALL=NOPASSWD: /sbin/iptables
www-data ALL=NOPASSWD: /usr/bin/rmtrack [0-9]*.[0-9]*.[0-9]*.[0-9]*

Redémarrer et tester.