Отдалечен мониторинг и управление на устройства на базата на Lunix/OpenWrt/Lede през 80-ти порт…

Здравейте на всички, това е моят първи опит в Хабра. Искам да напиша как нестандартно да управлявате мрежово оборудване във външната мрежа. Какво означава нестандартно: в повечето случаи, за управление на оборудването във външната мрежа, ще ви е необходимо:

  • Публичен IP адрес. Или, ако оборудването е зад нечий NAT, то публичен IP и „пробит“ порт.
  • Тунел (PPTP/OpenVPN/L2TP+IPSec и т.н.) до централния възел, през който да бъде достъпен.

Следователно, „моят велосипед“ ще е нужен, когато стандартните методи не са подходящи, например:

  1. Оборудването е зад NAT и освен обикновения http (80-ти порт) — всичко е затворено. Напълно нормална ситуация за големи федерални корпоративни мрежи. Могат да отворят портовете — но не веднага, не бързо и не за вас.
  2. Нестабилен и/или „тесен“ канал за връзка. Ниска скорост, постоянни загуби. Болка и разочарование при опити за организиране на тунел.
  3. Скъп канал за връзка, където буквално всеки мегабайт е на цена. Например сателитна връзка. Плюс големи закъснения и „тясна“ лента.
  4. Ситуация, при която трябва да „жонглирате“ с голямо количество малки рутери, на които от една страна е инсталиран OpenWrt/Lede за разширяване на възможностите, а от друга страна ресурсите (паметта) на рутера не достигат за всичко.

Забележка номер едно Какво пречи да поставите „флашка“ в USB порта на рутера и да разширите паметта на рутера?

Често изискванията за цената на решението като цяло, но понякога ключова роля играе и формата. Например на обекта стои TP-Link ML3020, единственият му USB порт се използва за 2G/3G модем, всичко това е опаковано в някакъв малък пластмасов корпус и е поставено някъде много високо (на мачта), далеч-далеч (в полето, на 30 км от най-близката базова станция на мобилния оператор). Да, можете да поставите USB-хъб и да разширите броя на портовете, но опитът показва, че това е обемисто и ненадеждно.

И така, опитах се да ви опиша моята типична ситуация: „някъде далеко-далеч, стои много важен, самотен и малък рутер, управляван от 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 влизат библиотеките за криптиране с размер около 2MB и поради това трудно ще успеете да съберете образ за малкия TP-LINK ML3020 например. А с wget — моля.

2) На страната на сървера (при мен е Ubuntu) ще използваме Zabbix. Защо: искам да е красиво (с графики) и удобно (да изпращам команди през контекстното меню). Zabbix има такава приятно нещо, като zabbix-агент. Чрез агента ще извикаме php-скрипт на сървър, който ще връща информация за това, регистриран ли е нашият рутер в необходимия период от време. За съхранение на информацията за времето на регистрация и командите за устройствата, използвам MySQL, отделна таблица users с приблизително следните полета:

		СЪЗДАЙ ТАБЛИЦА `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, 
		  `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 репозиторий на адрес: https://github.com/BazDen/iotnet.online.git
Сега PHP скриптове, разположени на сървъра (за удобство, може да се поставят в папка /usr/share/zabbix/):

Файл a.php:

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();
		}
		// Сега трябва да запазим времето на регистрация на потребителя, неговия IP и съобщение от него. В момента само съобщението
		$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-агент):

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“ — извикваният скрипт с указание на удостоверителните данни на устройството.

Добавяне на графици: отворете уеб интерфейса на zabbix, в менюто изберете:
Настройка -> Узли на мрежата -> Създайте узел на мрежата. Тук е достатъчно да посочите името на узела на мрежата, неговата група, интерфейса на агента по подразбиране:

Отдалечен мониторинг и управление на устройства на базата на Lunix/OpenWrt/Lede през 80-ти порт…

Сега за този узел на мрежата трябва да добавим елемент от данни. Обърнете внимание на двете полета: „ключ“ — това е параметърът, който написахме във файла /etc/zabbix/zabbix_agentd.conf (в нашия случай това е test), и „интервал на обновление“ — аз задавам 5 минути, тъй като и оборудването се регистрира на сървера веднъж на пет минути.

Отдалечен мониторинг и управление на устройства на базата на Lunix/OpenWrt/Lede през 80-ти порт…

Сега добавяме график. Препоръчвам да изберете стил на рисуване „Запълване“.

Отдалечен мониторинг и управление на устройства на базата на Lunix/OpenWrt/Lede през 80-ти порт…

Накрая получавате нещо много лаконично, например така:

Отдалечен мониторинг и управление на устройства на базата на Lunix/OpenWrt/Lede през 80-ти порт…

На основателния въпрос: „и това ли си струва?“, отговарям: разбира се, вижте „причини за създаването на велосипеда“ в началото на статията.

Ако моят първи графомански опит предизвика интерес у читателите, в следващите статии искам да опиша как да изпращате команди на отдалечено оборудване. Успях да реализирам цялата схема и за устройства на базата на RouterOS (Mikrotik).

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster