Каждый из нас подключался к беспроводной сети (аэропорты, кафе и тд) где необходимо согласится с некоторыми условиями или пройти авторизации прежде чем начинать пользоваться интернетом. Такая технология называется captive portal.
В мою задачу входило создать captive portal где каждому пользователю раз в 30 минут будет показываться определенный сайт (реклама), и пока он не нажмет «кнопочку» «далее» интернета не будет.
Для создания нам понадобится:
Краткое описание принципа работы
У нас имеется маршрутизатор на базе debian, где eth0 — локальная сеть в моем случае (192.168.11.0/24), eth0:1 — интерфейс который смотрит в интернет.
Настройку интерфейсов мы пропустим, это каждый выполнит сам. Сразу перейдем к настройке iptables, я обычно прописываю iptables в rc.local:
/etc/rc.local
На этом конфигурация iptables заканчивается и переходим к странице которую получает пользователь: /var/lib/index.php
Данная страница может быть любой, смысл лишь в том, чтобы получить мак пользователя и добавить его в iptables.
В текущий момент получилась следующая схема:
Осталось только сделать скрипт который будет:
Так как мои знания в программировании довольно плачевны был написан простенький скрипт на перле:
Все, задача выполенна, модифицируя файлик index.php можем показывать пользователю релкаму, смешные видео, заставлять его оставлять свою почту и т.д.
В мою задачу входило создать captive portal где каждому пользователю раз в 30 минут будет показываться определенный сайт (реклама), и пока он не нажмет «кнопочку» «далее» интернета не будет.
Для создания нам понадобится:
- Wifi-точка доступа любая (на практике можно использовать и проводной интернет)
- Маршрутизатор на любой *nix системе (в моем случае это debian wheezy)
Краткое описание принципа работы
- Любой пакет пришедший на маршрутизатор маркируем
- Любой запрос на 80 порт (пакеты уж маркированны) перенаправляем на нужную нам страницу
- При «авторизации»(нажатии кнопки далее) пользователя добавляем его мак в исключения и разрешаем доступ в интернет
- Скрипт вычисляет у кого прошел лимит времени и удаляет данного клиента.
У нас имеется маршрутизатор на базе debian, где eth0 — локальная сеть в моем случае (192.168.11.0/24), eth0:1 — интерфейс который смотрит в интернет.
Настройку интерфейсов мы пропустим, это каждый выполнит сам. Сразу перейдем к настройке iptables, я обычно прописываю iptables в rc.local:
/etc/rc.local
IPTABLES="/sbin/iptables"
EBTABLES="/sbin/ebtables"
DHCP="67:68"
SSH="22"
WWW="80"
$IPTABLES -t mangle -F
$IPTABLES -F
$IPTABLES -A INPUT -i lo -j ACCEPT
#ssh
$IPTABLES -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
# разрешаем серверу и клиентам dns гугла
$IPTABLES -A INPUT -s 8.8.8.8 -j ACCEPT
$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# создаем правило интернет
$IPTABLES -N internet -t mangle
$IPTABLES -t mangle -A PREROUTING -j internet
#добавляем разрешенные маки в исключения путем добавления их в Return
awk 'BEGIN { FS="\t"; } { system("$IPTABLES -t mangle -A internet -m mac --mac-source "$4" -j RETURN"); }' /var/lib/users
#маркируем все пакеты
$IPTABLES -t mangle -A internet -j MARK --set-mark 99
# все маркированные пакеты которые идут на 80 порт отправляем на наш сервер
$IPTABLES -t nat -A PREROUTING -m mark --mark 99 -p tcp --dport 80 -j DNAT --to-destination 192.168.11.38
#дроппаем все маркированное
#$IPTABLES -t filter -A FORWARD -m mark --mark 99 -j DROP
#dns
$IPTABLES -t filter -A INPUT -s 8.8.8.8 -j ACCEPT
#http
$IPTABLES -t filter -A INPUT -p tcp --dport 80 -j ACCEPT
#port dns
$IPTABLES -t filter -A INPUT -p udp --dport 53 -j ACCEPT
#drop
$IPTABLES -t filter -A INPUT -m mark --mark 99 -j DROP
echo "1" > /proc/sys/net/ipv4/ip_forward
#настройка нат
$IPTABLES -A FORWARD -i eth0 -o eth0:1 -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A FORWARD -i eth0:1 -o eth0 -j ACCEPT
$IPTABLES -t nat -A POSTROUTING -o eth0 -j MASQUERADE
На этом конфигурация iptables заканчивается и переходим к странице которую получает пользователь: /var/lib/index.php
<?php
$server_name = "192";
$domain_name = "168.11.38";
$site_name = "Wireless Network";
// arp
$arp = "/usr/sbin/arp";
// файл где хранятся текущие пользователи
$users = "/var/lib/users";
// Check if we've been redirected by firewall to here.
// If so redirect to registration address
if ($_SERVER['SERVER_NAME']!="$server_name.$domain_name") {
header("location:http://$server_name.$domain_name/index.php?add="
.urlencode($_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']));
exit;
}
// получаем и добавляем мак пользователя
$mac = shell_exec("$arp -a ".$_SERVER['REMOTE_ADDR']);
preg_match('/..:..:..:..:..:../',$mac , $matches);
@$mac = $matches[0];
if (!isset($mac)) { exit; }
if (!isset($_POST['email']) || !isset($_POST['name'])) {
// Name or email address not entered therefore display form
?>
<h1>Welcome to <?php echo $site_name;?></h1>
To access the Internet you must first enter your details:<br><br>
<form method='POST'>
<table border=0 cellpadding=5 cellspacing=0>
<tr><td>Your full name:</td><td><input type='text' name='name'></td></tr>
<tr><td>Your email address:</td><td><input type='text' name='email'></td></tr>
<tr><td></td><td><input type='submit' name='submit' value='Submit'></td></tr>
</table>
</form>
<?php
} else {
enable_address();
}
function enable_address() {
global $name;
global $email;
global $mac;
global $users;
// время добавления пользователя храним в unixtime
file_put_contents($users,$_POST['name'].";".$_POST['email'].";"
.$_SERVER['REMOTE_ADDR'].";$mac;".date("U")."\n",FILE_APPEND + LOCK_EX);
// добавляем правило в iptables
exec("sudo iptables -I internet 1 -t mangle -m mac --mac-source $mac -j RETURN");
// The following line removes connection tracking for the PC
// This clears any previous (incorrect) route info for the redirection
exec("sudo rmtrack ".$_SERVER['REMOTE_ADDR']);
sleep(1);
header("location:http://".$_GET['add']);
exit;
}
// Function to print page header
function print_header() {
?>
<html>
<head><title>Welcome to <?php echo $site_name;?></title>
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
<LINK rel="stylesheet" type="text/css" href="./style.css">
</head>
<body bgcolor=#FFFFFF text=000000>
<?php
}
// Function to print page footer
function print_footer() {
echo "</body>";
echo "</html>";
}
?>
Данная страница может быть любой, смысл лишь в том, чтобы получить мак пользователя и добавить его в iptables.
В текущий момент получилась следующая схема:
- Iptables автоматически редиректит любой запрос на 80 порт на нашу страницу
- пользователь вводит свое имя и почту, скрипт php получает мак пользователя и добавляет в iptables правило исключения для этого мака
- Php скрипт складывает все данные пользователей (имя, почта, время добавления) в файл /var/lib/users
Осталось только сделать скрипт который будет:
- удалять правило из iptables для мака у которого вышло время
- удалять из файла данные по этому пользователю
Так как мои знания в программировании довольно плачевны был написан простенький скрипт на перле:
#!/usr/bin/perl
$file = "/var/lib/users";
$curtime = time();
# время "бана" 30 минут время в секундах так как считаем потом в unixtime
$bantime = 1800;
$tmpfile = "out.tmp";
open(my $data, '<', $file) or die "Could not open '$file' $!\n";
while (my $line = <$data>) {
chomp $line;
(@field) = split(";", $line);
#прибавляем время "бана" ко времени когда зашел пользователь (4 поле в нашем случае) получаем время когда нужно удалить пользователя
if (defined($exptime)) { undef $exptime; }
$result = $field[4];
$exptime = $bantime + $result;
# такая коснтрукция потому что перл не хотел прибавлять $field[4] + $bantime
# если текущее время больше времени для удаления, удаляем мак и перезаписываем файл
if ($curtime > $exptime) {
#printf "BINGO\n";
system "iptables -D internet -t mangle -m mac --mac-source $field[3] -j RETURN";
# system "rm /var/lib/users";
}
else {
printf "$field[3]; $exptime\n";
open SF, ">>$tmpfile";
printf SF "$field[0];$field[1];$field[2];$field[3];$field[4]\n";
close SF;
system "mv /root/out.tmp /var/lib/users";
system "chown www-data. $file";
}
}
Все, задача выполенна, модифицируя файлик index.php можем показывать пользователю релкаму, смешные видео, заставлять его оставлять свою почту и т.д.