В конце концов я остановился на использовании php-ssh2, php-mysqli и комбинации iptables и ipset в OpenWRT (пакеты ipset, iptables-mod-ipset и kmod-ipt-ipset).
Я создал ipset содержит IP-адреса, которым разрешен полный доступ в Интернет:
ipset create ip_whitelist hash:ip
На OpenWRT я настроить IPTables перенаправить любой HTTP трафик трафика на мой интернет-сервер, если исходный IP не в ip_whitelist:
iptables --table nat --new prerouting_mychain
iptables --table nat --insert PREROUTING -j prerouting_mychain
iptables --table nat --append prerouting_mychain --match set --match-set ip_whitelist src -j RETURN
iptables --table nat --append prerouting_mychain --dport 80 -j DNAT --to-destination <internal http server>
Теперь это будет обрабатывать перенаправление для IP-адресов не в ipset, но нам все еще нужно, чтобы блокировать другой несанкционированный трафик. Это сделано в таблице фильтра в цепи FORWARD:
iptables --table filter --new forward_mychain
iptables --table filter --insert FORWARD -j forward_mychain
iptables --table filter --insert forward_mychain -j DROP (this ends up being the last rule)
iptables --table filter --insert forward_mychain --match set --match-set ip_whitelist src -j ACCEPT
В моем случае я хочу, чтобы всегда разрешать DNS, чтобы браузер не замерзает на поиске:
iptables --table filter --insert forward_mychain -p udp --dport 53 -j ACCEPT
iptables --table filter --insert forward_mychain -p udp --sport 53 -j ACCEPT
Теперь FORWARD цепи настраивается только для обеспечения трафика с IP-адресов в ipset и DNS-трафике.
IP-адресов, которые разрешены добавляются в ipset:
ipset add ip_whitelist 10.10.10.10
и удалены с:
ipset del ip_whitelist 10.10.10.10
В моем случае я просто задать срок вступления в силу редирект обратно к внутренний сайт для проверки базы данных:
ipset add ip_whitelist 10.10.10.10 timeout 3600
(секунды)
Этот таймаут также можно установить по умолчанию при первом создании ipset.
Все, что осталось сделать, это создать страницу php для перенаправленного HTTP-трафика, запросить базу данных и отправить команды ipset маршрутизаторам шлюза.
Первая моя простая база данных:
CREATE DATABASE Testdb;
CREATE TABLE `Clients` (
`IP` varchar(15) DEFAULT NULL,
`Created` datetime DEFAULT NULL,
`Expiry` datetime DEFAULT NULL,
`LastAccess` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY `ipidx` (`IP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
И redirect.php:
<!DOCTYPE html >
<html>
<head>
<title></title>
</head>
<body>
<?php
$ClientIPAddr = filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP);
$sqlQuery = "";
if (($ClientIPAddr === FALSE) || ($ClientIPAddr === NULL)) {
echo "Failed to obtain client IP [" . $_SERVER["REMOTE_ADDR"] . "]";
exit;
}
База данных запросов/вставка:
$mysqli = new mysqli("<database ip>", "dbuser", "dbpass", "Testdb");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
exit;
} else {
echo "Connected (" . $mysqli->server_info . ")<br />";
}
echo "Now for a query on " . $ClientIPAddr . ":<br />";
// Check to see if IP is in database and has not expired
$sqlQuery = "SELECT IP, TIMEDIFF(Expiry, now()) from Clients where IP=\"" . $ClientIPAddr . "\";";
if (($result = $mysqli->query($sqlQuery)) === FALSE) {
echo "Query Error (" . $mysqli->error . ") on (" . $sqlQuery . ")<br />";
exit;
} else {
echo "<h2>Query Result(" . $result->num_rows . "):</h2>";
echo "<table>";
while (($row = $result->fetch_array(MYSQLI_NUM)) !== NULL) {
echo "<tr>";
foreach ($row as $value) {
echo "<td>" . $value . "</td>";
}
echo "</tr>";
}
echo "</table>";
echo "<h1>Welcome to my Test Page</h1>";
if ($result->num_rows === 0) {
echo "<p>New(" . $result->num_rows . "): " . $ClientIPAddr . "</p>";
echo "<p>You now have full Internet access.</p>";
} else {
echo "<p>Returning(" . $result->num_rows . "): " . $ClientIPAddr . "</p>";
echo "<p>Your Internet access has been extended.</p>";
}
$result->free();
$sqlQuery = "INSERT INTO Clients (IP, Created, Expiry) VALUES (\"" . $ClientIPAddr . "\", now(),
timestampadd(hour, 24, now())) ON DUPLICATE KEY UPDATE Expiry=timestampadd(hour, 24, now());";
if (($result = $mysqli->query($sqlQuery)) === FALSE) {
echo "Query Error (" . $mysqli->error . ") on (" . $sqlQuery . ")<br />";
exit;
} else {
Добавить IP в ipset для авторизации доступа в Интернет:
// **************** UPDATE IPSET *****************
//ssh2_connect, ssh2_fingerprint, ssh2_auth_pubkey_file, ssh2_auth_password, ssh2_exec
if (($sshConnect = ssh2_connect("<GW IP>")) === FALSE) {
$err = error_get_last();
echo $err["message"];
exit;
}
if (ssh2_auth_pubkey_file($sshConnect, "root", "/usr/share/nginx/rsa.pub", "/usr/share/nginx/rsa") === FALSE) {
$err = error_get_last();
echo $err["message"];
exit;
}
if (($stream = ssh2_exec($sshConnect, "ipset add ip_whitelist " . $ClientIPAddr)) === FALSE) {
$err = error_get_last();
echo $err["message"];
exit;
}
$stderr_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
if ((stream_set_blocking($stream, true) === FALSE) ||
(stream_set_blocking($stderr_stream, true) === FALSE)) {
$err = error_get_last();
echo $err["message"];
exit;
}
if (($resultStr = stream_get_contents($stream)) === FALSE) {
$err = error_get_last();
echo $err["message"];
exit;
}
if ($resultStr === "") { // Likely an error
if (($resultStr = stream_get_contents($stderr_stream)) === FALSE) {
$err = error_get_last();
echo $err["message"];
exit;
}
}
echo "<pre>" . $resultStr . "</pre>";
}
}
$mysqli->close();
?>
</body>
</html>
Как вы можете видеть, это очень грубо. Однако он поражает все функции, которые мне нужны. Надеюсь, это оказалось полезным для кого-то другого.
Извините за поздний ответ. Я думал использовать iptables только для добавления исключений в правило перенаправления для тех, у кого есть учетные данные для доступа в Интернет. Имеет ли смысл использовать iptables-restore для небольших изменений таблиц? т. е. добавить/удалить одно правило MAC? –
Если это всего лишь одно правило, то обновление не по порядку не является проблемой. Вам просто нужно убедиться, что вы не создаете повторяющиеся записи из-за неудачной/неправильной связи. Чтобы обойти это, я создавал цепочку каждый раз, удаляю существующий и добавляю вновь созданный. Поскольку вы сказали, что распределены и многие узлы, я просто ищу последовательный способ сделать это. –
Отличная мысль о создании цепи. Я пытался выяснить, как я могу обеспечить, чтобы база данных и iptables для узлов шлюза находились в синхронизации. Если я правильно вас понимаю, вы думаете, что, создав цепочку как оболочку для правила, iptables будет генерировать исключение, если цепочка уже существует? Поддерживает ли поддержание отношения «один к одному» цепи для каждого правила, значительно увеличивает требования к памяти или нагрузку? –