2013-09-26 3 views
0

У меня есть 25 (скоро будет 100+) узел (OpenWRT) adhoc L2 сетка сети (batman-adv) с 4 узлами шлюза. Каждый узел имеет 5-гигабайтный и 2,4-дюймовый беспроводной интерфейс. 2.4 предназначен для доступа клиента, а сетка работает на интерфейсе 5ghz.Удаленная манипуляция iptables (OpenWRT)

Все узлы почти идентичны (программное обеспечение). Доступ GW к широковещательному Интернету (Ethernet) к сетке и пару узлов имеет LAN-порт (Ethernet), подключенный к одному серверу, предоставляющий сетью DHCP, SQL и http-сервисы. По дизайну нет ограничений относительно того, какие узлы физически обеспечивают доступ к Интернету или к серверу DCHP/SQL/HTTP (т. Е. Совместное размещение не является вариантом дизайна).

В настоящее время клиенты беспроводной связи имеют доступ к портам глобальной сети GW без ограничений. Следующим этапом проекта является ограничение доступа к интерфейсу WAN на основе информации учетной записи, размещенной на сервере ЛВС (т. Е. Информации об учетной записи в базе данных).

Что я хотел бы сделать, это дистанционно управлять iptables узлов GW для управления доступом в Интернет на основе информации с серверов, но я не уверен, что лучший способ получить команды iptables для GW. Моя первая мысль заключалась в том, чтобы выполнять командные команды через SSH или потоковые команды для SSH-клиента. Я мог бы написать собственный простой TCP/IP-сервер. Вероятно, есть и RPC-модель.

Есть ли рекомендуемый метод, приведенный выше или «за» и «против», который я должен рассмотреть. Спасибо.

EDIT: iptables блокируется при одновременных вызовах или необходимо, чтобы пользователь сериализовал использование?

ответ

0

В конце концов я остановился на использовании 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> 

Как вы можете видеть, это очень грубо. Однако он поражает все функции, которые мне нужны. Надеюсь, это оказалось полезным для кого-то другого.

1

Я предполагаю, что у вас будет такая же политика на всех шлюзах. Вероятно, вы поддерживаете политику централизованно. В этом случае я бы просто построил политику в формате «iptables-restore» на каком-то централизованном сервере и выполнил одну команду на каждом GW. Вероятно, вы захотите выполнить эту команду по защищенному каналу. Я бы просто использовал scp + ssh.

Это точно так же, как вы предложили, за исключением замены всей таблицы на новую таблицу. Добавление одного правила за раз может привести к некоторой несогласованности в политике и может привести к возникновению дыр. Кроме того, сохранение всех GW в синхронизации может быть сложным в среде с потерями. Отсюда стресс на iptables-восстановлении.

+0

Извините за поздний ответ. Я думал использовать iptables только для добавления исключений в правило перенаправления для тех, у кого есть учетные данные для доступа в Интернет. Имеет ли смысл использовать iptables-restore для небольших изменений таблиц? т. е. добавить/удалить одно правило MAC? –

+1

Если это всего лишь одно правило, то обновление не по порядку не является проблемой. Вам просто нужно убедиться, что вы не создаете повторяющиеся записи из-за неудачной/неправильной связи. Чтобы обойти это, я создавал цепочку каждый раз, удаляю существующий и добавляю вновь созданный. Поскольку вы сказали, что распределены и многие узлы, я просто ищу последовательный способ сделать это. –

+0

Отличная мысль о создании цепи. Я пытался выяснить, как я могу обеспечить, чтобы база данных и iptables для узлов шлюза находились в синхронизации. Если я правильно вас понимаю, вы думаете, что, создав цепочку как оболочку для правила, iptables будет генерировать исключение, если цепочка уже существует? Поддерживает ли поддержание отношения «один к одному» цепи для каждого правила, значительно увеличивает требования к памяти или нагрузку? –