ポート 80 経由の Lunix/OpenWrt/Lede ベースのデバイスのリモート監視と制御…

皆さん、こんにちは。これが私の初めてのハブレ体験です。非標準的な方法で外部ネットワーク上のネットワーク機器を管理する方法について書きたいと思います。非標準とはどういう意味ですか: ほとんどの場合、外部ネットワーク上の機器を管理するには、次のものが必要です。

  • パブリックIPアドレス。そうですね、または機器が誰かの NAT の背後にある場合は、パブリック IP と「転送された」ポートになります。
  • アクセス可能な中央ノードへのトンネル (PPTP/OpenVPN/L2TP+IPSec など)。

したがって、次のような標準的な方法が適さない場合は、「マイバイク」が必要になります。

  1. 装置は NAT の背後に配置されており、通常の http (ポート 80) を除いてすべてが閉じられています。これは、大規模な連邦企業ネットワークにとってはまったく正常な状況です。ポートを登録することはできますが、すぐには登録できず、すぐに登録できるわけでも、自動的に登録されるわけでもありません。
  2. 不安定または「狭い」通信チャネル、あるいはその両方。低速、一定の損失。トンネルを整理するときの痛みとイライラ。
  3. 文字通りあらゆるメガバイトが重要な、高価な通信チャネル。例えば衛星通信。さらに、長い遅延と「狭い」帯域も発生します。
  4. 機能を拡張するために OpenWrt/Lede がインストールされている多数の小型ルーターを「ジャグリング」する必要があるが、ルーターのリソース (メモリ) が十分ではない状況全てにおいて。

音符の回数 ルーターの USB ポートにフラッシュ ドライブを取り付けてルーターのメモリを拡張できないのは何ですか?

ほとんどの場合、要件はソリューション全体のコストに関するものですが、場合によってはフォーム ファクターも重要な役割を果たします。たとえば、現場には TP-Link ML3020 があり、その唯一の USB ポートは 2G/3G モデムに使用され、これはすべてある種の小さなプラスチック ケースに包まれて、どこか高いところ (マストの上) に置かれています。遠く、遠く (現場では、最寄りの携帯電話会社の基地局から 30 km)。はい、USB ハブを接続してポートの数を拡張することはできますが、経験上、これは面倒で信頼性が低いことがわかっています。

そこで、私は私の典型的な状況を説明しようとしました。「はるか遠くのどこかに、Linux を実行している非常に重要で孤独な小さなルーターがあります。少なくとも 1 日に 1 回は彼が「生きている」ことを知ることが重要であり、必要に応じて、たとえば「ハニー、再起動!」などのコマンドが彼に送信されます。

実装に移りましょう。

1) ルーター側では、cron 経由で、5/10/1440 分ごと、または必要に応じて、wget を使用してサーバーに http リクエストを送信し、リクエストの結果をファイルに保存し、ファイルを実行可能にする必要があります。を選択して実行します。

私の 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」は、再起動コマンドなどのサーバー応答が保存されるリモート ルーター上のファイルです。

2 番目に注意してください: ああ、curl ではなく wget を使用するのはなぜでしょうか。curl を使用すると、https リクエストを GET ではなく POST で送信できるからです。ああ、古いジョークにあるように、「NE が瓶に登る!」だからです。 curl にはサイズが約 2MB の暗号化ライブラリが含まれており、このため、たとえば小型の TP-LINK ML3020 用のイメージをアセンブルできる可能性はほとんどありません。そして wget を使ってください - お願いします。

2) サーバー側 (私は Ubuntu を使用しています) では Zabbix を使用します。理由: 美しく (グラフがあり)、かつ便利 (コンテキスト メニューからコマンドを送信できる) であることを望みます。 Zabbix には zabbix エージェントという素晴らしい機能があります。エージェントを通じて、サーバー上の PHP スクリプトを呼び出します。これにより、ルーターが必要な期間内に登録されたかどうかに関する情報が返されます。登録時間やデバイスのコマンドに関する情報を保存するには、MySQL を使用します。MySQL は、次のフィールドを含む別のテーブル ユーザーです。

		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 リポジトリからダウンロードできます。 https://github.com/BazDen/iotnet.online.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 ユーザーパスワード」 - デバイス登録データを示す呼び出されるスクリプト。

チャートの追加: zabbix Web インターフェイスを開き、メニューから選択します。
設定 -> ネットワーク ノード -> ネットワーク ノードの作成。ここでは、ネットワーク ホストの名前、そのグループ、およびデフォルトのエージェント インターフェイスを指定するだけで十分です。

ポート 80 経由の Lunix/OpenWrt/Lede ベースのデバイスのリモート監視と制御…

次に、このネットワーク ノードのデータ要素を追加する必要があります。 5 つのフィールドに注目してください: 「key」 - これは /etc/zabbix/zabbix_agentd.conf ファイルに書き込んだパラメータです (この場合はテストです)、「更新間隔」 - XNUMX 分に設定しました, なぜなら、機器もXNUMX分にXNUMX回サーバーに登録されるからです。

ポート 80 経由の Lunix/OpenWrt/Lede ベースのデバイスのリモート監視と制御…

さて、グラフを追加しましょう。レンダリングスタイルとして「塗りつぶし」を選択することをお勧めします。

ポート 80 経由の Lunix/OpenWrt/Lede ベースのデバイスのリモート監視と制御…

出力は非常に簡潔なもので、たとえば次のようになります。

ポート 80 経由の Lunix/OpenWrt/Lede ベースのデバイスのリモート監視と制御…

「それだけの価値はありましたか?」という当然の質問に対して、私はこう答えます。もちろん、この記事の冒頭にある「自転車を作成する理由」を参照してください。

私の最初の書記マニアの経験が読者の興味を惹いたのであれば、次の記事でリモート機器にコマンドを送信する方法を説明したいと思います。また、RouterOS (Mikrotik) に基づいたデバイスのスキーム全体を実装することにも成功しました。

出所: habr.com

コメントを追加します