From 994d1c3b5711a20a855db9da2f78146200e08349 Mon Sep 17 00:00:00 2001 From: mercury Date: Thu, 6 Oct 2022 00:38:43 +0400 Subject: [PATCH] named peers extended status --- app/bot.php | 267 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 189 insertions(+), 78 deletions(-) diff --git a/app/bot.php b/app/bot.php index a4fef4f..0ecdc8a 100644 --- a/app/bot.php +++ b/app/bot.php @@ -118,12 +118,50 @@ class Bot case preg_match('~^/import$~', $this->input['callback'], $m): $this->import(); break; + case preg_match('~^/rename (.+)$~', $this->input['callback'], $m): + $this->rename($m[1]); + break; case !empty($this->input['reply']): $this->reply(); break; } } + public function rename(int $client) + { + $r = $this->send( + $this->input['chat'], + "@{$this->input['username']} введите название:", + $this->input['message_id'], + reply: 'введите название:', + ); + $_SESSION['reply'][$r['result']['message_id']] = [ + 'start_message' => $this->input['message_id'], + 'start_callback' => $this->input['callback_id'], + 'callback' => 'renameClient', + 'args' => [$client], + ]; + } + + public function renameClient(string $name, int $client) + { + $clients = $this->readClients(); + $clients[$client]['interface']['## name'] = $name; + $this->saveClients($clients); + $server = $this->readConfig(); + foreach ($server['peers'] as $k => $v) { + if ($v['AllowedIPs'] == $clients[$client]['interface']['Address']) { + $server['peers'][$k]['## name'] = $name; + } + } + $this->restartWG($this->createConfig($server)); + } + + public function readClients():array + { + return json_decode(file_get_contents($this->clients), true) ?: []; + } + public function export() { $conf = $this->readConfig(); @@ -146,6 +184,7 @@ class Bot 'start_message' => $this->input['message_id'], 'start_callback' => $this->input['callback_id'], 'callback' => 'importFile', + 'args' => [], ]; } @@ -156,7 +195,7 @@ class Bot if (empty($json) || !is_array($json)) { $this->answer($this->input['callback_id'], 'ошибка', true); } else { - file_put_contents($this->clients, json_encode($json['clients'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $this->saveClients($json['clients']); $this->restartWG($this->createConfig($json['server'])); } } @@ -221,13 +260,14 @@ class Bot 'start_message' => $this->input['message_id'], 'start_callback' => $this->input['callback_id'], 'callback' => 'pac', + 'args' => [], ]; } public function proxy() { $proxy = trim($this->ssh("getent hosts proxy | awk '{ print $1 }'")); - $this->createPeer("$proxy/32"); + $this->createPeer("$proxy/32", 'прокси'); $this->menu(); } @@ -243,7 +283,7 @@ class Bot if (!empty($_SESSION['reply'][$this->input['reply']])) { $this->delete($this->input['chat'], $this->input['reply']); $callback = $_SESSION['reply'][$this->input['reply']]['callback']; - $this->{$callback}($this->input['message']); + $this->{$callback}($this->input['message'], ...$_SESSION['reply'][$this->input['reply']]['args']); switch ($callback) { case 'createPeer': case 'importFile': @@ -255,6 +295,12 @@ class Bot case 'pac': $this->answer($_SESSION['reply'][$this->input['reply']]['start_callback']); break; + case 'renameClient': + $this->delete($this->input['chat'], $this->input['message_id']); + $this->input['message_id'] = $this->input['callback_id'] = $_SESSION['reply'][$this->input['reply']]['start_message']; + $this->menu(); + $this->answer($_SESSION['reply'][$this->input['reply']]['start_message']); + break; } unset($_SESSION['reply'][$this->input['reply']]); } @@ -264,13 +310,14 @@ class Bot { $r = $this->send( $this->input['chat'], - "@{$this->input['username']} перечислите через запятую подсети", + "@{$this->input['username']} перечислите подсети через запятую", $this->input['message_id'], - reply: 'перечислите через запятую подсети', + reply: 'перечислите подсети через запятую', ); $_SESSION['reply'][$r['result']['message_id']] = [ 'start_message' => $this->input['message_id'], 'callback' => 'createPeer', + 'args' => ['подсеть'], ]; } @@ -343,7 +390,7 @@ class Bot public function addPeer() { - $this->createPeer(); + $this->createPeer(name: 'весь трафик'); $this->menu(); } @@ -369,7 +416,7 @@ class Bot public function deletePeer($client) { $conf = $this->readConfig(); - $this->deleteClient($conf['peers'][$client]); + $this->deleteClient($client); unset($conf['peers'][$client]); $this->restartWG($this->createConfig($conf)); $this->menu(); @@ -377,30 +424,26 @@ class Bot public function menu($client = false, $return = false) { - $status = $this->ssh('wg'); - $conf = $this->readConfig(); - $text = "Статус\n\nСеть: {$conf['interface']['Address']}:{$conf['interface']['ListenPort']}\n\n$status\n$text"; + $clients = $this->readClients(); if ($client !== false) { - $clients = json_decode(file_get_contents($this->clients), true); - foreach ($clients as $k => $v) { - if ($v['interface']['Address'] == $conf['peers'][$client]['AllowedIPs']) { - $client_conf = $v; - break; - } - } - $client_ip = explode('/', $conf['peers'][$client]['AllowedIPs'])[0]; - $client_conf_str = $this->createConfig($client_conf); - $text = "$client_ip\n\n$client_conf_str"; + $name = $this->getName($clients[$client]['interface']); + $text = "{$this->createConfig($clients[$client])}\n\n$name"; $data = [ [ [ - 'text' => "скачать {$conf['peers'][$client]['AllowedIPs']}", + 'text' => "переименовать", + 'callback_data' => "/rename $client", + ], + ], + [ + [ + 'text' => "скачать", 'callback_data' => "/download $client", ], ], [ [ - 'text' => "удалить {$conf['peers'][$client]['AllowedIPs']}", + 'text' => "удалить", 'callback_data' => "/delete $client", ], ], @@ -412,49 +455,69 @@ class Bot ], ]; } else { - if (!empty($conf['peers'])) { - foreach ($conf['peers'] as $k => $v) { - $peers[] = [[ - 'text' => "{$v['AllowedIPs']}", - 'callback_data' => "/client $k", - ]]; - } - } - $data = [ - [ - [ - 'text' => "добавить клиента", - 'callback_data' => "/showadd", - ], - [ - 'text' => "PAC скрипт", - 'callback_data' => "/showpac", - ], - ], - [ - [ - 'text' => "экспорт", - 'callback_data' => "/export", - ], - [ - 'text' => "импорт", - 'callback_data' => "/import", - ], - [ - 'text' => "сброс", - 'callback_data' => "/showreset", - ], - ], - ]; - if ($peers) { - $data = array_merge($peers, $data); - } - array_unshift($data, [ + $data[] = [ [ 'text' => "обновить статус", 'callback_data' => "/menu", ], - ]); + ]; + if (!empty($clients)) { + foreach ($clients as $k => $v) { + $data[] = [[ + 'text' => $this->getName($v['interface']), + 'callback_data' => "/client $k", + ]]; + } + } + $data[] = [ + [ + 'text' => "добавить клиента", + 'callback_data' => "/showadd", + ], + [ + 'text' => "PAC скрипт", + 'callback_data' => "/showpac", + ], + ]; + $data[] = [ + [ + 'text' => "экспорт", + 'callback_data' => "/export", + ], + [ + 'text' => "импорт", + 'callback_data' => "/import", + ], + [ + 'text' => "сброс", + 'callback_data' => "/showreset", + ], + ]; + $conf = $this->readConfig(); + $status = $this->readStatus(); + $text[] = 'Server:'; + $text[] = " address: {$conf['interface']['Address']}"; + $text[] = " port: {$status['interface']['listening port']}"; + $text[] = " publickey: {$status['interface']['public key']}"; + $text[] = "\nPeers:"; + foreach ($conf['peers'] as $k => $v) { + foreach ($clients as $cl) { + if ($cl['interface']['Address'] == $v['AllowedIPs']) { + $allowed_ips = $cl['peers'][0]['AllowedIPs']; + } + } + $peer = $this->getStatusPeer($v['PublicKey'], $status['peers']); + $text[] = " {$this->getName($v)}: " . (preg_match('~^(\d+ seconds|[12] minute)~', $peer['latest handshake']) ? 'ONLINE' : 'OFFLINE') . ($peer['transfer'] ? " {$peer['transfer']}": ''); + $text[] = " address: {$peer['allowed ips']}"; + $text[] = " allowed ips: $allowed_ips"; + $text[] = " publickey: {$peer['peer']}"; + if ($peer['latest handshake']) { + $text[] = " endpoint: {$peer['endpoint']}"; + $text[] = " handshake: {$peer['latest handshake']}"; + } + $text[] = ''; + } + $text = '' . implode(PHP_EOL, $text) . ''; } if ($return) { @@ -478,6 +541,15 @@ class Bot } } + public function getStatusPeer(string $publickey, array $peers) + { + foreach ($peers as $k => $v) { + if ($v['peer'] == $publickey) { + return $v; + } + } + } + public function readConfig() { $r = $this->ssh('cat /etc/wireguard/wg0.conf'); @@ -509,6 +581,48 @@ class Bot return $d; } + public function readStatus() + { + $r = $this->ssh('wg'); + $r = explode(PHP_EOL, $r); + $r = array_filter($r); + $i = 0; + foreach ($r as $k => $v) { + if (preg_match('~^(interface|peer):~', $v, $m)) { + $i++; + if ($m[1] == 'interface') { + $data[$i]['type'] = 'interface'; + } else { + $data[$i]['type'] = 'peer'; + } + } + $t = explode(':', $v, 2); + $data[$i][trim($t[0])] = trim($t[1]); + } + foreach ($data as $v) { + $type = $v['type']; + unset($v['type']); + if ($type == 'interface') { + $d['interface'] = $v; + } else { + $d['peers'][] = $v; + } + } + return $d; + } + + public function getName(array $a):string + { + $name = ''; + foreach ($a as $k => $v) { + if (preg_match('~^#.*name$~', $k)) { + $name = $v; + } + } + $name = $name ?: $a['AllowedIPs'] ?: $a['Address']; + return $name; + } + public function createConfig($data) { $conf[] = "[Interface]"; @@ -527,7 +641,7 @@ class Bot return implode(PHP_EOL, $conf); } - public function createPeer($ips_user = false) + public function createPeer($ips_user = false, $name = false) { $conf = $this->readConfig(); $ipnet = explode('/', $conf['interface']['Address']); @@ -552,11 +666,13 @@ class Bot $public_peer_key = trim($this->ssh("echo $private_peer_key | wg pubkey")); $conf['peers'][] = [ + '## name' => $client_ip . ($name ? " ($name)" : ''), 'PublicKey' => $public_peer_key, 'AllowedIPs' => "$client_ip/32", ]; $client_conf = [ 'interface' => [ + '## name' => $client_ip . ($name ? " ($name)" : ''), 'PrivateKey' => $private_peer_key, 'Address' => "$client_ip/32", 'MTU' => 1350, @@ -574,26 +690,21 @@ class Bot $this->restartWG($this->createConfig($conf)); } - public function deleteClient($conf) + public function deleteClient(int $client) { - $clients = json_decode(file_get_contents($this->clients), true); - foreach ($clients as $k => $v) { - if ($v['interface']['Address'] == $conf['AllowedIPs']) { - unset($clients[$k]); - } - } - file_put_contents($this->clients, json_encode($clients, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $clients = $this->readClients(); + unset($clients[$client]); + $this->saveClients($clients); } - public function saveClient($client) + public function saveClient(array $client) { - if (file_exists($this->clients)) { - $conf = json_decode(file_get_contents($this->clients), true) ?: []; - } else { - $conf = []; - } - $conf = array_merge($conf, [$client]); - file_put_contents($this->clients, json_encode($conf, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $this->saveClients(array_merge($this->readClients(), [$client])); + } + + public function saveClients(array $clients) + { + file_put_contents($this->clients, json_encode($clients, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); } public function restartWG($conf_str)