Всім привіт це мій перший досвід на Хабре. Хочу написати про те, як нестандартно керувати мережевим обладнанням у зовнішній мережі. Що означає нестандартно: в більшості випадків для керування обладнанням у зовнішній мережі Вам необхідно:
- Публічна IP-адреса. Ну, або якщо обладнання знаходиться за чиїмось NAT-ом, то публічний IP та «прокинутий» порт.
- Тунель (PPTP/OpenVPN/L2TP+IPSec тощо) до центрального вузла, через який була доступна.
Тому «мій велосипед» знадобиться Вам, коли стандартні методи Вам не підходять, наприклад:
- Обладнання знаходиться за NAT-ом і окрім звичайного http (80-го порту) - все закрите. Цілком нормальна ситуація для великих федеральних корпоративних мереж. Прописати порти можуть, але не відразу, не швидко і не Вам.
- Нестабільний та/або «вузький» канал зв'язку. Невелика швидкість, постійні втрати. Біль та розчарування при спробі організувати тунель.
- Дорогий канал зв'язку, де буквально кожен мегабайт на рахунку. Наприклад, супутниковий зв'язок. Плюс великі затримки та «вузька» смуга.
- Ситуація, коли Вам треба "жонглювати" великою кількістю маленьких роутерів, на яких з одного боку встановлена OpenWrt/Lede для розширення можливостей, а з іншого боку ресурсів (пам'яті) роутера вистачає далеко не на все.
Примітка номер раз А що заважає USB-порт роутера встановити «флешку» і розширити пам'ять роутера?
Найчастіше вимоги до вартості рішення в цілому, але іноді ключову роль відіграє форм-фактор. Наприклад, на об'єкті стоїть TP-Link ML3020, його єдиний USB-порт використовується під 2G/3G модем, все це загорнуто в якусь нитку невеликий пластиковий корпус і розміщено десь високо-високо (на щоглі), далеко-далеко (в поле, за 30 км від найближчої базової станції мобільного оператора). Так, можна встромити USB-hub і розширити кількість портів, але досвід показує, що це громіздко і ненадійно.
Отже, я постарався описати Вам мою типову ситуацію: десь далеко-далеко, стоїть дуже важливий, самотній і маленький роутер під управлінням Linux. Важливо знати хоча б раз на день, що він „живий“ і за необхідності відсила йому команди, наприклад „сонечко, перезавантажись!“
Перейдемо до реалізації:
1) На стороні роутера по cron-у кожні 5/10/1440 хвилин або коли завгодно необхідно відсилати http-запит на сервер за допомогою wget, результат запиту зберігати у файл, файл робити виконуваним і виконувати його.
У мене рядок у cron-і виглядає приблизно так:
Файл /etc/crontabs/root:
*/5 * * * * wget "http://xn--80abgfbdwanb2akugdrd3a2e5gsbj.xn--p1ai/a.php?u=user&p=password" -O /tmp/wa.sh && chmod 777 /tmp/wa.sh && /tmp/wa.sh
, Де:
xn - 80abgfbdwanb2akugdrd3a2e5gsbj.xn - p1ai - домен мого сервера. Відразу зауважу: так, можна вказати і конкретну ip-адресу сервера, ми так раніше робили, поки наша держава, у праведному пориві боротьби не скажу незнаю - не закрила доступ до левової частки "хмар" DigitalOcean і Amazon. У разі використання символьного домену при виникненні подібного казусу, ви спокійно зможете підняти резервну хмару, перенаправити на неї домен і відновити моніторинг пристроїв.
a.php – ім'я скрипта на стороні сервера. Так, я знаю, що це неправильно, називати змінні та імена файлів однією літерою… пропоную вважати, що так ми заощаджуємо кілька байт при надсиланні запиту 🙂
u - ім'я користувача, логін залізки
p - пароль
„-O /tmp/wa.sh“ — файл на віддаленому роутері, куди буде збережена відповідь сервера, наприклад команда reboot.
Примітка номер два: Аааа, чому ми використовуємо wget, а не curl, адже через curl можна відправляти https запити і не GET-ом, а POST-ом? Аааа тому, що як у старому анекдоті „У глечик не лізе!“. До складу curl входять бібліотеки шифрування розміром близько 2МБ і завдяки цьому Вам вдасться зібрати образ для маленького TP-LINK ML3020 наприклад. А з wget – будь ласка.
2) На стороні сервера (у мене це Ubuntu) ми будемо використовувати Zabbix. Чому: хочу щоб було красиво (з графіками) та зручно (надсилати команди через контекстне меню). Заббікс має таку чарівну річ, як zabbix-агент. Через агента ми будемо викликати php-скрипт на сервері, який повертатиме інформацію про те, чи реєструвався наш роутер у потрібний період часу. Для зберігання інформації про час реєстрації, команди для пристроїв, я використовую MySQL, окрему таблицю users приблизно з такими полями:
CREATE TABLE `users` (
`id` varchar(25) NOT NULL,
`passwd` varchar(25) NOT NULL,
`description` varchar(150) NOT NULL,
`category` varchar(30) NOT NULL,
`status` varchar(10) NOT NULL,
`last_time` varchar(20) NOT NULL, // время последнего соединения
`last_ip` varchar(20) NOT NULL, // IP последнего соединения
`last_port` int(11) NOT NULL, // порт последнего соединения
`task` text NOT NULL, // задача которую получает роутер
`reg_task` varchar(150) NOT NULL, // "регулярная" задача, если мы захотим чтобы задача выполнялась всегда при регистрации
`last_task` text NOT NULL, // лог задач
`response` text NOT NULL, // сюда пишется ответ устройства
`seq` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Усі вихідники можна забрати з Git-репозиторію за адресою:
Тепер PHP-скрипти, що розміщуються на стороні сервера (для зручності їх можна покласти в папку /usr/share/zabbix/):
Файл a.php:
<?php
// Получаем входные параметры: имя пользователя, пароль и сообщение от удаленного роутера
// Зачем нужен message ? Это способ ответа роутера, например если вы захотите посмотреть содержимое файла роутера
$user=$_REQUEST['u'];
$password=$_REQUEST['p'];
$message=$_REQUEST['m'];
// Подключаемся к нашей базе данных (MySQL)
$conn=new mysqli("localhost","db_login","db_password","DB_name");
if (mysqli_connect_errno()) {
exit();
}
$conn->set_charset("utf8");
// здесь ищем наш роутер в таблице базы данных
$sql_users=$conn->prepare("SELECT task, reg_task, response, last_time FROM users WHERE id=? AND passwd=? AND status='active';");
$sql_users->bind_param('ss', $user, $password);
$sql_users->bind_result($task, $reg_task, $response, $last_time);
$sql_users->execute();
$sql_users->store_result();
if (($sql_users->num_rows)==1){
$sql_users->fetch();
// здесь мы роутеру отправляем его задачи
echo $task;
echo "n";
echo $reg_task;
// вот здесь мы пишем время ответа и сам ответ роутера
$response_history="[".date("Y-m-d H:i")."] ".$message;
// задачу отправили, теперь надо ее удалить,а после удаления отметить в логах, что такая-то задача выполнена
$last_ip=$_SERVER["REMOTE_ADDR"];
$last_port=$_SERVER["REMOTE_PORT"];
$ts_last_conn_time=$last_time;
$sql_users=$conn->prepare("UPDATE users SET task='', seq=1 WHERE (id=?);");
$sql_users->bind_param('s', $user);
$sql_users->execute();
if (strlen($message)>1){
$sql_users=$conn->prepare("UPDATE users SET response=?, seq=1 WHERE (id=?);");
$sql_users->bind_param('ss', $response_history, $user);
$sql_users->execute();
}
// теперь надо сохранить время регистрации пользователя, его айпи и сообщение от него. Пока только сообщение
$ts_now=time();
$sql_users=$conn->prepare("UPDATE users SET last_time=?, last_ip=?, last_port=? WHERE (id=?);");
$sql_users->bind_param('ssss', $ts_now, $last_ip, $last_port, $user);
$sql_users->execute();
}
// если мы не нашли роутер в нашей базе данных, или его статус "неактивный", то ему ... будет отправлена команда reboot....
// Почему так жестоко ? Потому что роутеры иногда пропадают, а это маленький способ проучить "новых владельцев".
else
{
echo "reboot";
}
$sql_users->close();
?>
Файл agent.php (це скрипт викликаного zabbix-агента):
<?php
// файл агента Zabbix. Данный скрипт обращается к таблице users и получает "1" если устройство регистрировалось с момента последнего обращения
// user и password - учетные данные оборудования
$user = $argv[1];
$password = $argv[2];
// подключаемся к нашей базе данных
$conn=new mysqli("localhost","db_user","db_password","db_name");
if (mysqli_connect_errno()) {
exit();
}
$conn->set_charset("utf8");
$sql_users=$conn->prepare("SELECT seq FROM users WHERE id=? AND passwd=? AND status='active';");
$sql_users->bind_param('ss', $user, $password);
$sql_users->bind_result($seq);
$sql_users->execute();
$sql_users->store_result();
// обмен данными происходит через поле seq. При регистрации железка ставит данное поле в "1"
if (($sql_users->num_rows)==1){
$sql_users->fetch();
echo $seq;
}
// обнуляем $seq.
$sql_users=$conn->prepare("UPDATE users SET seq=0 WHERE id=? AND passwd=? AND status='active';");
$sql_users->bind_param('ss', $user, $password);
$sql_users->execute();
$sql_users->close();
?>
Ну і заключний етап: прописання агента та додавання графіків.
Якщо у Вас ще не встановлений zabbix-агент, то:
apt-get install zabbix-agent
Редагуємо файл /etc/zabbix/zabbix_agentd.conf.
Додаємо рядок:
UserParameter=test,php /usr/share/zabbix/agent.php user password
, Де:
test - ім'я нашого агента
„php /usr/share/zabbix/agent.php user password“ — викликаний скрипт із зазначенням реєстраційних даних пристрою.
Додавання графіків: відкриваємо web-інтерфейс zabbix, у меню вибираємо:
Налаштування -> Вузли мережі -> Створити вузол мережі. Тут достатньо вказати ім'я вузла мережі, його групу, стандартний інтерфейс агента:
Тепер нам для цього вузла мережі треба додати елемент даних. Зверніть увагу на два поля: „ключ“ — це саме той параметр, який ми прописували у файлі /etc/zabbix/zabbix_agentd.conf (у нашому випадку це test), та „інтервал оновлення“ — я ставлю 5 хвилин, тому що та обладнання реєструється на сервері теж один раз на п'ять хвилин.
Ну і додаємо графік. Рекомендую в якості стилю малювання вибрати „Заповнення“.
На виході виходить щось дуже лаконічне, наприклад:
На резонне запитання: „і це того варто?“, відповім: ну звичайно, дивіться „причини створення велосипеда“ на початку статті.
Якщо мій перший графоманський досвід викликає інтерес читачів, то в наступних статтях я хочу описати як відправляти команди на віддалене обладнання. Також вдалося продати всю схему і для пристроїв на базі RouterOS (Mikrotik-ов).
Джерело: habr.com