Surveillance et contrôle à distance des appareils basés sur Lunix/OpenWrt/Lede via le port 80…

Bonjour à tous, c'est ma première expérience sur Habré. Je souhaite écrire sur la façon de gérer les équipements réseau sur un réseau externe de manière non standard. Que signifie non standard : dans la plupart des cas, pour gérer des équipements sur un réseau externe il vous faut :

  • Adresse IP publique. Eh bien, ou si l'équipement est derrière le NAT de quelqu'un, alors une adresse IP publique et un port « transféré ».
  • Tunnel (PPTP/OpenVPN/L2TP+IPSec, etc.) vers le nœud central par lequel il serait accessible.

Ainsi, vous aurez besoin de « mon vélo » lorsque les méthodes standards ne vous conviennent pas, par exemple :

  1. L'équipement est situé derrière NAT et, à l'exception du http habituel (port 80), tout est fermé. C'est une situation tout à fait normale pour les grands réseaux d'entreprises fédérales. Ils peuvent enregistrer des ports, mais pas tout de suite, ni rapidement, et pas pour vous.
  2. Canal de communication instable et/ou « étroit ». Faible vitesse, pertes constantes. Douleur et frustration en essayant d'organiser un tunnel.
  3. Un canal de communication coûteux, où chaque mégaoctet compte littéralement. Par exemple, les communications par satellite. Plus de longs délais et une bande « étroite ».
  4. Une situation où vous devez « jongler » avec un grand nombre de petits routeurs sur lesquels, d'une part, OpenWrt/Lede est installé pour étendre les capacités, et d'autre part, les ressources (mémoire) du routeur ne suffisent pas pour tout.

Notez le nombre de fois Qu'est-ce qui vous empêche d'installer une clé USB dans le port USB du routeur et d'étendre la mémoire du routeur ?

Le plus souvent, les exigences concernent le coût de la solution dans son ensemble, mais parfois le facteur de forme joue également un rôle clé. Par exemple, il y a un TP-Link ML3020 sur le site, son seul port USB est utilisé pour un modem 2G/3G, tout cela est emballé dans une sorte de petit boîtier en plastique et placé quelque part en hauteur (sur le mât), loin, très loin (sur le terrain, à 30 km de la station de base de l'opérateur mobile la plus proche). Oui, vous pouvez brancher un hub USB et augmenter le nombre de ports, mais l'expérience montre que cela est fastidieux et peu fiable.

J'ai donc essayé de vous décrire ma situation typique : « quelque part très, très loin, il y a un petit routeur très important, solitaire et exécutant Linux. Il est important de savoir au moins une fois par jour qu'il est « vivant » et, si nécessaire, des commandes lui sont envoyées, par exemple « chérie, redémarre !

Passons à la mise en œuvre :

1) Côté routeur, via cron, toutes les 5/10/1440 minutes, ou quand vous le souhaitez, vous devez envoyer une requête http au serveur en utilisant wget, enregistrer le résultat de la requête dans un fichier, rendre le fichier exécutable , et exécutez-le.

Ma ligne cron ressemble à ceci :

Fichier /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

, où:
xn--80abgfbdwanb2akugdrd3a2e5gsbj.xn--p1ai est le domaine de mon serveur. Permettez-moi de noter tout de suite : oui, vous pouvez spécifier une adresse IP spécifique du serveur, nous le faisions auparavant jusqu'à ce que notre état, dans un juste élan de lutte, je dirai, je ne sais pas, bloque l'accès au lion part des « clouds » de DigitalOcean et d’Amazon. Si vous utilisez un domaine symbolique, si un tel incident se produit, vous pouvez facilement créer un cloud de sauvegarde, y rediriger le domaine et restaurer la surveillance des appareils.

a.php est le nom du script côté serveur. Oui, je sais qu'il est faux de nommer les variables et les noms de fichiers avec la même lettre... Je propose de cette façon de sauvegarder quelques octets lors de l'envoi d'une requête :)
u - nom d'utilisateur, connexion matérielle
p - mot de passe
« -O /tmp/wa.sh » est un fichier sur le routeur distant dans lequel la réponse du serveur, par exemple la commande de redémarrage, sera enregistrée.

Remarque numéro deux : Ahhh, pourquoi utilisons-nous wget et non curl, car via curl vous pouvez envoyer des requêtes https non pas avec GET, mais avec POST ? Ahhh parce que, comme dans la vieille blague « NE monte dans le pot ! » curl comprend des bibliothèques de cryptage d'une taille d'environ 2 Mo et de ce fait, il est peu probable que vous puissiez assembler une image pour un petit TP-LINK ML3020, par exemple. Et avec wget - s'il vous plaît.

2) Côté serveur (j'ai Ubuntu) nous utiliserons Zabbix. Pourquoi : je veux que ce soit beau (avec des graphiques) et pratique (envoyer des commandes via le menu contextuel). Zabbix a une chose tellement merveilleuse que l'agent Zabbix. Grâce à l'agent, nous appellerons un script PHP sur le serveur, qui renverra des informations indiquant si notre routeur s'est enregistré pendant la période requise. Pour stocker des informations sur l'heure d'enregistrement et les commandes pour les appareils, j'utilise MySQL, une table utilisateur distincte avec approximativement les champs suivants :

		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;

Toutes les sources peuvent être téléchargées depuis le référentiel Git à l'adresse : https://github.com/BazDen/iotnet.online.git
Désormais les scripts PHP sont placés côté serveur (pour plus de commodité, ils peuvent être placés dans le dossier /usr/share/zabix/) :

un fichier.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();
	?>

Fichier Agent.php (c'est le script de l'agent zabbix appelé) :

<?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();
?>		

Eh bien, la dernière étape : enregistrer un agent et ajouter des horaires.

Si vous n'avez pas encore installé l'agent zabbix, alors :

apt-get install zabbix-agent

Editez le fichier /etc/zabbix/zabbix_agentd.conf.

Ajoutez la ligne :

UserParameter=test,php /usr/share/zabbix/agent.php user password

, où:
test est le nom de notre agent
"Mot de passe utilisateur php /usr/share/zabbix/agent.php" - un script appelé indiquant les données d'enregistrement de l'appareil.

Ajout de graphiques : ouvrez l'interface Web de Zabbix, sélectionnez dans le menu :
Paramètres -> Nœuds de réseau -> Créer un nœud de réseau. Ici, il suffit de spécifier le nom de l'hôte réseau, son groupe et l'interface de l'agent par défaut :

Surveillance et contrôle à distance des appareils basés sur Lunix/OpenWrt/Lede via le port 80…

Nous devons maintenant ajouter un élément de données pour ce nœud de réseau. Faites attention à deux champs : « clé » - c'est exactement le paramètre que nous avons écrit dans le fichier /etc/zabbix/zabbix_agentd.conf (dans notre cas, c'est test), et « intervalle de mise à jour » - je l'ai réglé sur 5 minutes , car et l'équipement est également enregistré sur le serveur une fois toutes les cinq minutes.

Surveillance et contrôle à distance des appareils basés sur Lunix/OpenWrt/Lede via le port 80…

Eh bien, ajoutons un graphique. Je recommande de choisir « Remplir » comme style de rendu.

Surveillance et contrôle à distance des appareils basés sur Lunix/OpenWrt/Lede via le port 80…

Le résultat est quelque chose de très laconique, par exemple comme ceci :

Surveillance et contrôle à distance des appareils basés sur Lunix/OpenWrt/Lede via le port 80…

A la question raisonnable : « est-ce que ça valait le coup ? », je répondrai : eh bien, bien sûr, voir « raisons de créer un vélo » au début de l'article.

Si ma première expérience graphomane suscite l'intérêt des lecteurs, alors dans les articles suivants je souhaite décrire comment envoyer des commandes à des équipements distants. Nous avons également réussi à mettre en œuvre l'ensemble du schéma pour les appareils basés sur RouterOS (Mikrotik).

Source: habr.com

Ajouter un commentaire