Compare commits

...

31 Commits

Author SHA1 Message Date
mercury 321b853d42 amnezia reduce button 2026-03-27 01:49:10 +04:00
mercury 8effb72cdc update version 2026-03-10 10:05:12 +04:00
mercury bec8f5488e reset amnezia keys fix 2026-03-08 01:23:12 +04:00
mercury f97b0f9228 amneziawg2.0 2026-03-08 01:12:09 +04:00
mercury 8e8babe1a6 update version 2025-12-16 00:58:02 +04:00
mercury 238c380f23 fix clash template rules behavior 2025-12-16 00:55:44 +04:00
mercury abc2e7c587 setting up dnstt display in menu 2025-12-16 00:38:34 +04:00
mercury 3f99d9fbf0 hide dnstt 2025-12-15 17:06:19 +04:00
mercury 3f1e80c641 update version 2025-12-15 16:48:24 +04:00
mercury 5afe6a4777 lightweight warp image 2025-12-15 00:44:54 +04:00
mercury c2cf0bff98 delete bot command 2025-12-15 00:34:41 +04:00
mercury 60241aaffb new mtproto image 2025-12-15 00:18:08 +04:00
mercury 96ff868b10 improve dnstt menu 2025-12-14 22:49:28 +04:00
mercury f58c29a66b fix reality start 2025-12-14 22:48:58 +04:00
mercury ca922f4612 change address for wg on start 2025-12-14 22:04:22 +04:00
mercury 5a5b1b62b1 domain for shortlink amnezia 2025-12-14 21:55:30 +04:00
mercury 0ef34f818d changing dependencies when starting containers 2025-12-11 00:44:32 +04:00
mercury d1960d6acb change of strategy for server xray domain resolution 2025-12-10 01:34:44 +04:00
mercury 95cba4c8d1 improved issuance of routing rules vless: ip or domains 2025-12-10 01:33:21 +04:00
mercury 18e2dd53ff changing dependencies when starting containers 2025-12-10 00:40:45 +04:00
mercury 3e5b131c16 delete hysteria upstream 2025-12-10 00:40:15 +04:00
mercury e07d1505e4 attempt to save page when changing domain list 2025-12-10 00:01:02 +04:00
mercury c140f292e8 fix port for first amnezia container 2025-12-09 21:57:10 +04:00
mercury 7cc100ace5 dnstt menu improve 2025-12-09 21:31:12 +04:00
mercury 4938ada85d Merge branch 'xhttp' into dev 2025-12-04 02:24:43 +04:00
mercury c5b9886470 hysteria: logging, correct port 2025-12-04 02:23:00 +04:00
mercury 61d8b899e7 improved docker-compose.override.yml rewrite algorithm 2025-12-04 01:08:14 +04:00
mercury 7d81eeeddd improved process logging 2025-12-03 01:25:53 +04:00
mercury 3bdebd95ab backup dnstt keys 2025-12-03 00:16:19 +04:00
mercury eb4641a58d ability to disable naive and openconnect subdomains 2025-12-03 00:10:47 +04:00
mercury 61900daafd custom hysteria port 2025-12-03 00:08:49 +04:00
14 changed files with 559 additions and 321 deletions
+347 -152
View File
@@ -179,6 +179,9 @@ class Bot
case preg_match('~^/setHwidDevices(?: (\w+))?$~', $this->input['callback'], $m): case preg_match('~^/setHwidDevices(?: (\w+))?$~', $this->input['callback'], $m):
$this->setHwidDevices($m[1] ?? null); $this->setHwidDevices($m[1] ?? null);
break; break;
case preg_match('~^/changePort(?: (\w+))?$~', $this->input['callback'], $m):
$this->changePort($m[1] ?? null);
break;
case preg_match('~^/hwidUser (\d+)(?:_(\d+))?$~', $this->input['callback'], $m): case preg_match('~^/hwidUser (\d+)(?:_(\d+))?$~', $this->input['callback'], $m):
$this->hwidUser($m[1], $m[2] ?? 0); $this->hwidUser($m[1], $m[2] ?? 0);
break; break;
@@ -317,8 +320,11 @@ class Bot
case preg_match('~^/logs$~', $this->input['callback'], $m): case preg_match('~^/logs$~', $this->input['callback'], $m):
$this->logs(); $this->logs();
break; break;
case preg_match('~^/dnstt$~', $this->input['callback'], $m): case preg_match('~^/dnstt$~', $this->input['callback'] ?: $this->input['message'], $m):
$this->dnstt(); $this->dnstt(!empty($this->input['callback']));
break;
case preg_match('~^/showdnstt$~', $this->input['callback'], $m):
$this->showdnstt();
break; break;
case preg_match('~^/dnsttDownload$~', $this->input['callback'], $m): case preg_match('~^/dnsttDownload$~', $this->input['callback'], $m):
$this->dnsttDownload(); $this->dnsttDownload();
@@ -455,9 +461,6 @@ class Bot
case preg_match('~^/enterPage$~', $this->input['callback'], $m): case preg_match('~^/enterPage$~', $this->input['callback'], $m):
$this->enterPage(); $this->enterPage();
break; break;
case preg_match('~^/geodb$~', $this->input['callback'], $m):
$this->geodb();
break;
case preg_match('~^/adguardreset$~', $this->input['callback'], $m): case preg_match('~^/adguardreset$~', $this->input['callback'], $m):
$this->adguardreset(); $this->adguardreset();
break; break;
@@ -515,6 +518,12 @@ class Bot
case preg_match('~^/switchAmnezia (-?\d+)$~', $this->input['callback'], $m): case preg_match('~^/switchAmnezia (-?\d+)$~', $this->input['callback'], $m):
$this->switchAmnezia($m[1]); $this->switchAmnezia($m[1]);
break; break;
case preg_match('~^/resetAmnezia (-?\d+)$~', $this->input['callback'], $m):
$this->resetAmnezia($m[1]);
break;
case preg_match('~^/reduceAmnezia (-?\d+)$~', $this->input['callback'], $m):
$this->reduceAmnezia($m[1]);
break;
case preg_match('~^/switchExchange (\d+)$~', $this->input['callback'], $m): case preg_match('~^/switchExchange (\d+)$~', $this->input['callback'], $m):
$this->switchExchange($m[1]); $this->switchExchange($m[1]);
break; break;
@@ -558,8 +567,8 @@ class Bot
case preg_match('~^/addNipdomain$~', $this->input['callback'], $m): case preg_match('~^/addNipdomain$~', $this->input['callback'], $m):
$this->addNipdomain(); $this->addNipdomain();
break; break;
case preg_match('~^/(?P<action>change|delete)(?P<typelist>\w+) (?P<arg>\d+)$~', $this->input['callback'], $m): case preg_match('~^/(?P<action>change|delete)(?P<typelist>\w+) (?P<arg>\d+)(?: (?P<page>\d+))?$~', $this->input['callback'], $m):
$this->listPacChange($m['typelist'], $m['action'], $m['arg']); $this->listPacChange($m['typelist'], $m['action'], $m['arg'], $m['page'] ?: 0);
break; break;
case preg_match('~^/paczapret$~', $this->input['callback'], $m): case preg_match('~^/paczapret$~', $this->input['callback'], $m):
$this->pacZapret(); $this->pacZapret();
@@ -725,11 +734,11 @@ class Bot
public function restartTG() public function restartTG()
{ {
$secret = file_get_contents('/config/mtprotosecret'); $secret = file_get_contents('/config/mtprotosecret');
$fakedomain = file_get_contents('/config/mtprotodomain') ?: 'vk.com'; $fakedomain = file_get_contents('/config/mtprotodomain') ?: 'yandex.ru';
$this->ssh('pkill mtproto-proxy', 'tg'); $this->ssh('pkill mtproto-proxy', 'tg');
if (preg_match('~^\w{32}$~', $secret)) { if (preg_match('~^\w{32}$~', $secret)) {
$p = getenv('TGPORT'); $p = getenv('TGPORT');
$this->ssh("mtproto-proxy --domain $fakedomain -u nobody -H $p --nat-info 10.10.0.8:{$this->ip} -S $secret --aes-pwd /proxy-secret /proxy-multi.conf -M 1 >/dev/null 2>&1 &", 'tg'); $this->ssh("mtproto-proxy --domain $fakedomain -u nobody -H $p --nat-info 10.10.0.8:{$this->ip} -S $secret --aes-pwd /proxy-secret /proxy-multi.conf -M 1", 'tg', false, '/logs/mtproto');
} }
} }
@@ -817,15 +826,15 @@ class Bot
{ {
$s = file_get_contents('/config/mtprotosecret'); $s = file_get_contents('/config/mtprotosecret');
$p = getenv('TGPORT'); $p = getenv('TGPORT');
$d = trim(file_get_contents('/config/mtprotodomain') ?: 'vk.com'); $d = trim(file_get_contents('/config/mtprotodomain') ?: 'yandex.ru');
$d = exec("echo $d | tr -d '\\n' | xxd -ps -c 200"); $d = exec("echo $d | tr -d '\\n' | xxd -ps -c 200");
$ip = $this->getPacConf()['domain'] ?: $this->ip; $ip = $this->getDomain();
return "https://t.me/proxy?server=$ip&port=$p&secret=ee$s$d"; return "https://t.me/proxy?server=$ip&port=$p&secret=ee$s$d";
} }
public function mtproto() public function mtproto()
{ {
$d = file_get_contents('/config/mtprotodomain') ?: 'vk.com'; $d = file_get_contents('/config/mtprotodomain') ?: 'yandex.ru';
$st = $this->ssh('pgrep mtproto-proxy', 'tg') ? 'on' : 'off'; $st = $this->ssh('pgrep mtproto-proxy', 'tg') ? 'on' : 'off';
$text[] = "Menu -> MTProto\n"; $text[] = "Menu -> MTProto\n";
$text[] = "status: $st\n"; $text[] = "status: $st\n";
@@ -1147,7 +1156,7 @@ class Bot
file_put_contents('/config/ocserv.conf', $conf); file_put_contents('/config/ocserv.conf', $conf);
$this->ssh('pkill ocserv', 'oc'); $this->ssh('pkill ocserv', 'oc');
$pac = $this->getPacConf(); $pac = $this->getPacConf();
if (!empty($pac['ocserv'])) { if (!empty($pac['ocserv']) && !empty($this->getHashSubdomain('oc'))) {
$this->ssh('ocserv -c /etc/ocserv/ocserv.conf', 'oc'); $this->ssh('ocserv -c /etc/ocserv/ocserv.conf', 'oc');
} }
} }
@@ -1159,7 +1168,7 @@ class Bot
$c = file_get_contents('/config/Caddyfile'); $c = file_get_contents('/config/Caddyfile');
$t = preg_replace('~^(\t+)?basic_auth[^\n]+~sm', '$1basic_auth ' . ($pac['naive']['user'] ?? '_') . ' ' . ($pac['naive']['pass'] ?? '__'), $c); $t = preg_replace('~^(\t+)?basic_auth[^\n]+~sm', '$1basic_auth ' . ($pac['naive']['user'] ?? '_') . ' ' . ($pac['naive']['pass'] ?? '__'), $c);
file_put_contents('/config/Caddyfile', $t); file_put_contents('/config/Caddyfile', $t);
if (!empty($pac['naive']['pass'])) { if (!empty($pac['naive']['pass']) && !empty($this->getHashSubdomain('np'))) {
$this->ssh('caddy run -c /config/Caddyfile', 'np', false); $this->ssh('caddy run -c /config/Caddyfile', 'np', false);
} }
} }
@@ -1172,7 +1181,7 @@ class Bot
$c['auth']['password'] = $pac['hysteria_pass']; $c['auth']['password'] = $pac['hysteria_pass'];
yaml_emit_file('/config/hysteria.yaml', $c); yaml_emit_file('/config/hysteria.yaml', $c);
if (!empty($pac['hysteria_pass'])) { if (!empty($pac['hysteria_pass'])) {
$this->ssh('hysteria server -c /config/hysteria.yaml', 'hy', false); $this->ssh('hysteria server -c /config/hysteria.yaml', 'hy', false, '/logs/hysteria');
} }
} }
@@ -1187,7 +1196,7 @@ class Bot
public function chOcSubdomain($domain) public function chOcSubdomain($domain)
{ {
$pac = $this->getPacConf(); $pac = $this->getPacConf();
if (empty($domain)) { if ($domain == -1) {
unset($pac["oc_domain"]); unset($pac["oc_domain"]);
} else { } else {
$pac["oc_domain"] = $domain; $pac["oc_domain"] = $domain;
@@ -1201,7 +1210,7 @@ class Bot
public function chNpSubdomain($domain) public function chNpSubdomain($domain)
{ {
$pac = $this->getPacConf(); $pac = $this->getPacConf();
if (!empty($data)) { if ($domain == -1) {
unset($pac['np_domain']); unset($pac['np_domain']);
} else { } else {
$pac['np_domain'] = $domain; $pac['np_domain'] = $domain;
@@ -1233,7 +1242,11 @@ class Bot
public function chhypass($pass) public function chhypass($pass)
{ {
$pac = $this->getPacConf(); $pac = $this->getPacConf();
if (!empty($pass)) {
$pac['hysteria_pass'] = $pass; $pac['hysteria_pass'] = $pass;
} else {
unset($pac['hysteria_pass']);
}
$this->setPacConf($pac); $this->setPacConf($pac);
$this->restartHysteria(); $this->restartHysteria();
$this->menu('hy'); $this->menu('hy');
@@ -1831,6 +1844,10 @@ class Bot
'private' => file_get_contents('/certs/cert_private'), 'private' => file_get_contents('/certs/cert_private'),
'public' => file_get_contents('/certs/cert_public'), 'public' => file_get_contents('/certs/cert_public'),
] : false, ] : false,
'dnstt' => file_exists('/config/dnstt/server.key') ? [
'private' => file_get_contents('/config/dnstt/server.key'),
'public' => file_get_contents('/config/dnstt/server.pub'),
] : false,
'mtproto' => file_get_contents('/config/mtprotosecret'), 'mtproto' => file_get_contents('/config/mtprotosecret'),
'mtprotodomain' => file_get_contents('/config/mtprotodomain'), 'mtprotodomain' => file_get_contents('/config/mtprotodomain'),
'xray' => $this->getXray(), 'xray' => $this->getXray(),
@@ -1982,7 +1999,13 @@ class Bot
if (!empty($json['pac']['domain'])) { if (!empty($json['pac']['domain'])) {
$this->setUpstreamDomainOcserv($json['pac']['domain']); $this->setUpstreamDomainOcserv($json['pac']['domain']);
$this->setUpstreamDomainNaive($json['pac']['domain']); $this->setUpstreamDomainNaive($json['pac']['domain']);
$this->setUpstreamDomainHysteria($json['pac']['domain']); }
// dnstt
if (!empty($json['dnstt'])) {
$out[] = 'update dnstt certificates';
$this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out));
file_put_contents('/config/dnstt/server.key', $json['dnstt']['private']);
file_put_contents('/config/dnstt/server.pub', $json['dnstt']['public']);
} }
// nginx // nginx
$out[] = 'reset nginx'; $out[] = 'reset nginx';
@@ -2053,63 +2076,79 @@ class Bot
$this->restartWG($this->createConfig($server)); $this->restartWG($this->createConfig($server));
} }
public function switchAmnezia($page = 0) public function resetAmnezia($page = 0) {
$this->switchAmnezia($page, 1);
}
public function reduceAmnezia($page = 0) {
$this->switchAmnezia($page, false, 1);
}
public function switchAmnezia($page = 0, $reset = false, $reduce = false)
{ {
$c = $this->getPacConf(); $c = $this->getPacConf();
switch (true) {
case !empty($reset):
$amnezia = 1;
unset($c[$this->getInstanceWG(1) . 'amnezia_keys']);
unset($c[$this->getInstanceWG(1) . 'presharedkey']);
break;
case !empty($reduce):
$amnezia = 1;
unset($c[$this->getInstanceWG(1) . 'amnezia_keys']['S3']);
unset($c[$this->getInstanceWG(1) . 'amnezia_keys']['S4']);
unset($c[$this->getInstanceWG(1) . 'amnezia_keys']['I1']);
break;
default:
$amnezia = $c[$this->getInstanceWG(1) . 'amnezia'] = $c[$this->getInstanceWG(1) . 'amnezia'] ? 0 : 1; $amnezia = $c[$this->getInstanceWG(1) . 'amnezia'] = $c[$this->getInstanceWG(1) . 'amnezia'] ? 0 : 1;
break;
}
$this->setPacConf($c); $this->setPacConf($c);
$pk = $this->presharedKey(); $pk = $this->presharedKey();
$ak = $this->amneziaKeys(); $ak = $this->amneziaKeys();
$clients = $this->readClients(); $clients = $this->readClients();
foreach ($clients as $k => $v) { foreach ($clients as $k => $v) {
if (!empty($amnezia)) {
$clients[$k]['peers'][0]['PresharedKey'] = $pk;
$clients[$k]['interface']['Jc'] = $ak['Jc'];
$clients[$k]['interface']['Jmin'] = $ak['Jmin'];
$clients[$k]['interface']['Jmax'] = $ak['Jmax'];
$clients[$k]['interface']['S1'] = $ak['S1'];
$clients[$k]['interface']['S2'] = $ak['S2'];
$clients[$k]['interface']['H1'] = $ak['H1'];
$clients[$k]['interface']['H2'] = $ak['H2'];
$clients[$k]['interface']['H3'] = $ak['H3'];
$clients[$k]['interface']['H4'] = $ak['H4'];
} else {
unset($clients[$k]['peers'][0]['PresharedKey']); unset($clients[$k]['peers'][0]['PresharedKey']);
unset($clients[$k]['interface']['Jc']); unset($clients[$k]['interface']['Jc']);
unset($clients[$k]['interface']['Jmin']); unset($clients[$k]['interface']['Jmin']);
unset($clients[$k]['interface']['Jmax']); unset($clients[$k]['interface']['Jmax']);
unset($clients[$k]['interface']['S1']); unset($clients[$k]['interface']['S1']);
unset($clients[$k]['interface']['S2']); unset($clients[$k]['interface']['S2']);
unset($clients[$k]['interface']['S3']);
unset($clients[$k]['interface']['S4']);
unset($clients[$k]['interface']['H1']); unset($clients[$k]['interface']['H1']);
unset($clients[$k]['interface']['H2']); unset($clients[$k]['interface']['H2']);
unset($clients[$k]['interface']['H3']); unset($clients[$k]['interface']['H3']);
unset($clients[$k]['interface']['H4']); unset($clients[$k]['interface']['H4']);
unset($clients[$k]['interface']['I1']);
if (!empty($amnezia)) {
$clients[$k]['peers'][0]['PresharedKey'] = $pk;
foreach ($ak as $j => $i) {
$clients[$k]['interface'][$j] = $i;
}
} }
} }
$this->saveClients($clients); $this->saveClients($clients);
$wg = $this->readConfig(); $wg = $this->readConfig();
if (!empty($amnezia)) {
$wg['interface']['Jc'] = $ak['Jc'];
$wg['interface']['Jmin'] = $ak['Jmin'];
$wg['interface']['Jmax'] = $ak['Jmax'];
$wg['interface']['S1'] = $ak['S1'];
$wg['interface']['S2'] = $ak['S2'];
$wg['interface']['H1'] = $ak['H1'];
$wg['interface']['H2'] = $ak['H2'];
$wg['interface']['H3'] = $ak['H3'];
$wg['interface']['H4'] = $ak['H4'];
} else {
unset($wg['interface']['Jc']); unset($wg['interface']['Jc']);
unset($wg['interface']['Jmin']); unset($wg['interface']['Jmin']);
unset($wg['interface']['Jmax']); unset($wg['interface']['Jmax']);
unset($wg['interface']['S1']); unset($wg['interface']['S1']);
unset($wg['interface']['S2']); unset($wg['interface']['S2']);
unset($wg['interface']['S3']);
unset($wg['interface']['S4']);
unset($wg['interface']['H1']); unset($wg['interface']['H1']);
unset($wg['interface']['H2']); unset($wg['interface']['H2']);
unset($wg['interface']['H3']); unset($wg['interface']['H3']);
unset($wg['interface']['H4']); unset($wg['interface']['H4']);
unset($wg['interface']['I1']);
if (!empty($amnezia)) {
foreach ($ak as $j => $i) {
$wg['interface'][$j] = $i;
}
} }
foreach ($wg['peers'] as $k => $v) { foreach ($wg['peers'] as $k => $v) {
@@ -2119,7 +2158,7 @@ class Bot
unset($wg['peers'][$k]['PresharedKey']); unset($wg['peers'][$k]['PresharedKey']);
} }
} }
$this->restartWG($this->createConfig($wg), 1); $this->restartWG($this->createConfig($wg), !$reset && !$reduce);
$this->menu('wg', $page); $this->menu('wg', $page);
} }
@@ -2400,8 +2439,7 @@ class Bot
$adguardClient = $conf['adguardkey'] ? "-d {$conf['adguardkey']}.{$conf['domain']}" : ''; $adguardClient = $conf['adguardkey'] ? "-d {$conf['adguardkey']}.{$conf['domain']}" : '';
$oc = $this->getHashSubdomain('oc'); $oc = $this->getHashSubdomain('oc');
$np = $this->getHashSubdomain('np'); $np = $this->getHashSubdomain('np');
$hy = $this->getHashSubdomain('hy'); exec("certbot certonly --force-renew --preferred-chain 'ISRG Root X1' -n --agree-tos --email mail@{$conf['domain']} -d {$conf['domain']}" . ($oc ? " -d $oc.{$conf['domain']}" : '') . ($np ? " -d $np.{$conf['domain']}" : '') . " $adguardClient --webroot -w /certs/ --logs-dir /logs --max-log-backups 0 2>&1", $out, $code);
exec("certbot certonly --force-renew --preferred-chain 'ISRG Root X1' -n --agree-tos --email mail@{$conf['domain']} -d {$conf['domain']} -d $oc.{$conf['domain']} -d $np.{$conf['domain']} -d $hy.{$conf['domain']} $adguardClient --webroot -w /certs/ --logs-dir /logs --max-log-backups 0 2>&1", $out, $code);
if ($code > 0) { if ($code > 0) {
$this->send($this->input['chat'], "ERROR\n" . implode("\n", $out)); $this->send($this->input['chat'], "ERROR\n" . implode("\n", $out));
break; break;
@@ -2635,6 +2673,21 @@ class Bot
]; ];
} }
public function changePort($container)
{
$r = $this->send(
$this->input['chat'],
"@{$this->input['username']} number port",
$this->input['message_id'],
reply: 'number port',
);
$_SESSION['reply'][$r['result']['message_id']] = [
'start_message' => $this->input['message_id'],
'callback' => 'setPort',
'args' => [$container],
];
}
public function addLinkDomain() public function addLinkDomain()
{ {
$r = $this->send( $r = $this->send(
@@ -2945,42 +2998,42 @@ DNS-over-HTTPS with IP:
$page = (int) floor(array_search($v, array_keys($conf[$type])) / $this->limit); $page = (int) floor(array_search($v, array_keys($conf[$type])) / $this->limit);
} }
$page = $page ?: -2; $page = $page ?: -2;
$this->backXtlsList($type); $this->backXtlsList($type, $page);
} }
public function backXtlsList($type) public function backXtlsList($type, $page = 0)
{ {
switch ($type) { switch ($type) {
case 'includelist': case 'includelist':
$this->pacUpdate($_SESSION['proxylistentry']); $this->pacUpdate($_SESSION['proxylistentry']);
if (!empty($_SESSION['proxylistentry'])) { if (!empty($_SESSION['proxylistentry'])) {
$this->xtlsproxy(); $this->xtlsproxy($page);
} }
break; break;
case 'blocklist': case 'blocklist':
$this->xrayUpdateRules(); $this->xrayUpdateRules();
$this->xtlsblock(); $this->xtlsblock($page);
break; break;
case 'warplist': case 'warplist':
$this->xrayUpdateRules(); $this->xrayUpdateRules();
$this->xtlswarp(); $this->xtlswarp($page);
break; break;
case 'processlist': case 'processlist':
$this->xtlsprocess(); $this->xtlsprocess($page);
break; break;
case 'packagelist': case 'packagelist':
$this->xtlsapp(); $this->xtlsapp($page);
break; break;
case 'subnetlist': case 'subnetlist':
$this->xtlssubnet(); $this->xtlssubnet($page);
break; break;
case 'rulessetlist': case 'rulessetlist':
$this->xtlsrulesset(); $this->xtlsrulesset($page);
break; break;
case 'white': case 'white':
case 'deny': case 'deny':
$this->syncDeny(); $this->syncDeny();
$this->denyList(0, $type == 'white' ? 1 : 0); $this->denyList($page, $type == 'white' ? 1 : 0);
break; break;
} }
} }
@@ -3325,12 +3378,6 @@ DNS-over-HTTPS with IP:
$am = $c[$this->getInstanceWG(1) . 'amnezia']; $am = $c[$this->getInstanceWG(1) . 'amnezia'];
$end = $c[$this->getInstanceWG(1) . 'endpoint']; $end = $c[$this->getInstanceWG(1) . 'endpoint'];
$data = [ $data = [
[
[
'text' => $this->i18n($am ? 'on' : 'off') . " amnezia",
'callback_data' => "/switchAmnezia $page",
],
],
[ [
[ [
'text' => $this->i18n(!$bt ? 'on' : 'off') . " {$this->i18n('torrent')} ", 'text' => $this->i18n(!$bt ? 'on' : 'off') . " {$this->i18n('torrent')} ",
@@ -3368,6 +3415,24 @@ DNS-over-HTTPS with IP:
], ],
], ],
]; ];
if (!empty($am)) {
array_unshift($data, [
[
'text' => "reset obf-keys",
'callback_data' => "/resetAmnezia $page",
],
[
'text' => "reduce to 1",
'callback_data' => "/reduceAmnezia $page",
],
]);
}
array_unshift($data, [
[
'text' => $this->i18n($am ? 'on' : 'off') . " amnezia",
'callback_data' => "/switchAmnezia $page",
],
]);
if ($clients) { if ($clients) {
$data = array_merge($data, $clients); $data = array_merge($data, $clients);
} }
@@ -3775,45 +3840,54 @@ DNS-over-HTTPS with IP:
public function getAmneziaShortLink($client) public function getAmneziaShortLink($client)
{ {
$dns = explode(',', $client['interface']['DNS']); $domain = $this->getDomain();
$c = json_encode([ $pac = $this->getPacConf();
$dnsRaw = $client['interface']['DNS'] ?: $pac[$this->getInstanceWG(1) . 'dns'] ?: $this->dns;
$dns = array_map('trim', explode(',', $dnsRaw));
$wgPort = (int) getenv($this->getInstanceWG() == 'wg1' ? 'WG1PORT' : 'WGPORT');
$amneziaKeys = $this->amneziaKeys();
$protoVer = array_key_exists('I1', $amneziaKeys) ? "2" : "1";
$containerName = $protoVer === "2" ? "amnezia-awg2" : "amnezia-awg";
$c = [
"containers" => [ "containers" => [
[ [
"awg" => [ "awg" => [
"isThirdPartyConfig" => True, "isThirdPartyConfig" => true,
"last_config" => json_encode([ "last_config" => json_encode(array_merge(array_map('strval', $amneziaKeys), [
"H1" => "{$client['interface']['H1']}", "protocol_version" => $protoVer,
"H2" => "{$client['interface']['H2']}", "client_ip" => $client['interface']['Address'],
"H3" => "{$client['interface']['H3']}",
"H4" => "{$client['interface']['H4']}",
"Jc" => "{$client['interface']['Jc']}",
"Jmax" => "{$client['interface']['Jmax']}",
"Jmin" => "{$client['interface']['Jmin']}",
"S1" => "{$client['interface']['S1']}",
"S2" => "{$client['interface']['S2']}",
"client_ip" => explode('/', $client['interface']['Address'])[0],
"client_priv_key" => $client['interface']['PrivateKey'], "client_priv_key" => $client['interface']['PrivateKey'],
"client_pub_key" => "0", "client_pub_key" => "0",
"config" => $this->createConfig($client), "config" => $this->createConfig($client),
"hostName" => $this->ip, "hostName" => $domain,
"port" => (int) getenv('WG1PORT'), "port" => $wgPort,
"psk_key" => $client['peers'][0]['PresharedKey'], "psk_key" => $client['peers'][0]['PresharedKey'],
"server_pub_key" => $client['peers'][0]['PublicKey'] "server_pub_key" => $client['peers'][0]['PublicKey'],
]), "allowed_ips" => array_map('trim', explode(',', $client['peers'][0]['AllowedIPs'])),
"port" => (int) getenv('WG1PORT'), "persistent_keep_alive" => "25"
"transport_proto" => "udp" ])),
"port" => $wgPort,
"transport_proto" => "udp",
"protocol_version" => $protoVer
], ],
"container" => "amnezia-awg" "container" => $containerName
] ]
], ],
"defaultContainer" => "amnezia-awg", "defaultContainer" => $containerName,
"description" => $client['interface']['## name'], "description" => $client['interface']['## name'],
"dns1" => $dns[0], "dns1" => $dns[0] ?? '',
"dns2" => $dns[1] ?: '', "dns2" => $dns[1] ?? '',
"hostName" => $this->ip "hostName" => $domain,
]); "isThirdPartyConfig" => true
exec("echo '$c' | python amnezia.py", $o); ];
return $o[0]; $json = json_encode($c);
$proc = proc_open('python amnezia.py', [0 => ['pipe', 'r'], 1 => ['pipe', 'w']], $pipes);
fwrite($pipes[0], $json);
fclose($pipes[0]);
$result = trim(stream_get_contents($pipes[1]));
fclose($pipes[1]);
proc_close($proc);
return $result;
} }
public function getClient($client, $page) public function getClient($client, $page)
@@ -3821,7 +3895,7 @@ DNS-over-HTTPS with IP:
$clients = $this->readClients(); $clients = $this->readClients();
if ($clients) { if ($clients) {
$name = $this->getName($clients[$client]['interface']); $name = $this->getName($clients[$client]['interface']);
$conf = $this->createConfig($clients[$client]); $conf = htmlspecialchars($this->createConfig($clients[$client]));
if ($this->getWGType() == 'awg') { if ($this->getWGType() == 'awg') {
$sl = $this->getAmneziaShortLink($clients[$client]); $sl = $this->getAmneziaShortLink($clients[$client]);
} }
@@ -4398,11 +4472,11 @@ DNS-over-HTTPS with IP:
$data[] = [ $data[] = [
[ [
'text' => $this->i18n($v ? 'on' : 'off') . ' ' . ($basename ? basename($k) . ' ' : '') . (in_array($type, ['rulessetlist', 'packagelist', 'processlist', 'subnetlist']) ? $k : idn_to_utf8($k)), 'text' => $this->i18n($v ? 'on' : 'off') . ' ' . ($basename ? basename($k) . ' ' : '') . (in_array($type, ['rulessetlist', 'packagelist', 'processlist', 'subnetlist']) ? $k : idn_to_utf8($k)),
'callback_data' => "/change$type " . ($i + $page * $this->limit), 'callback_data' => "/change$type " . ($i + $page * $this->limit) . " $page",
], ],
[ [
'text' => 'delete', 'text' => 'delete',
'callback_data' => "/delete$type " . ($i + $page * $this->limit), 'callback_data' => "/delete$type " . ($i + $page * $this->limit) . " $page",
], ],
]; ];
$i++; $i++;
@@ -4448,7 +4522,7 @@ DNS-over-HTTPS with IP:
return [$data, $text]; return [$data, $text];
} }
public function listPacChange($type, $action, $key) public function listPacChange($type, $action, $key, $page = 0)
{ {
$conf = $this->getPacConf(); $conf = $this->getPacConf();
$i = 0; $i = 0;
@@ -4467,7 +4541,7 @@ DNS-over-HTTPS with IP:
$i++; $i++;
} }
$this->setPacConf($conf); $this->setPacConf($conf);
$this->backXtlsList($type); $this->backXtlsList($type, $page);
} }
public function pacZapret() public function pacZapret()
@@ -4659,7 +4733,7 @@ DNS-over-HTTPS with IP:
if (!file_exists('/config/dnstt/server.key')) { if (!file_exists('/config/dnstt/server.key')) {
$this->ssh("dnstt-server -gen-key -privkey-file /dnstt/server.key -pubkey-file /dnstt/server.pub", 'dnstt'); $this->ssh("dnstt-server -gen-key -privkey-file /dnstt/server.key -pubkey-file /dnstt/server.pub", 'dnstt');
} }
$this->ssh("dnstt-server -udp :53 -privkey-file /dnstt/server.key {$c['dnsttDomain']} 127.0.0.1:22", 'dnstt' , false); $this->ssh("dnstt-server -udp :53 -privkey-file /dnstt/server.key {$c['dnsttDomain']} 127.0.0.1:22", 'dnstt' , false, '/logs/dnstt');
} }
} }
@@ -4668,25 +4742,43 @@ DNS-over-HTTPS with IP:
$this->sendFile($this->input['from'], curl_file_create('/config/dnstt/server.pub')); $this->sendFile($this->input['from'], curl_file_create('/config/dnstt/server.pub'));
} }
public function dnstt() public function showdnstt()
{
$c = $this->getPacConf();
$c['showdnstt'] = empty($c['showdnstt']);
$this->setPacConf($c);
$this->dnstt(1);
}
public function dnstt($update = false)
{ {
$c = $this->getPacConf(); $c = $this->getPacConf();
$pubkey = file_get_contents('/config/dnstt/server.pub'); $pubkey = file_get_contents('/config/dnstt/server.pub');
$text[] = "dnstt"; $text[] = "dnstt";
$data[] = [
[
'text' => $this->i18n('show in menu ') . $this->i18n($c['showdnstt'] ? 'on' : 'off'),
'callback_data' => "/showdnstt",
],
];
if (!empty($c['dnsttDomain']) && !empty($c['dnsttPassword'])) {
$text[] = "<pre>set the NS record for {$c['dnsttDomain']}: tns.{$c['domain']}\nset A record for tns.{$c['domain']}: {$this->ip}</pre>"; $text[] = "<pre>set the NS record for {$c['dnsttDomain']}: tns.{$c['domain']}\nset A record for tns.{$c['domain']}: {$this->ip}</pre>";
$text[] = "domain: <code>{$c['dnsttDomain']}</code>"; $text[] = "account: <code>vpnbot:{$c['dnsttPassword']}</code>";
$text[] = "ssh: <code>vpnbot:{$c['dnsttPassword']}</code>"; $text[] = "server name: <code>{$c['dnsttDomain']}</code>";
$text[] = "pubkey: <code>$pubkey</code>"; $text[] = "public key: <code>$pubkey</code>";
$data[] = [ $data[] = [
[ [
'text' => $this->i18n('download pubkey'), 'text' => $this->i18n('download pubkey'),
'callback_data' => "/dnsttDownload", 'callback_data' => "/dnsttDownload",
], ],
]; ];
} else {
$text[] = "set subdomain and password";
}
$data[] = [ $data[] = [
[ [
'text' => $this->i18n('set domain'), 'text' => $this->i18n('set subdomain'),
'callback_data' => "/dnsttDomain", 'callback_data' => "/dnsttDomain",
], ],
]; ];
@@ -4702,12 +4794,21 @@ DNS-over-HTTPS with IP:
'callback_data' => "/menu", 'callback_data' => "/menu",
], ],
]; ];
if ($update) {
$this->update( $this->update(
$this->input['chat'], $this->input['chat'],
$this->input['message_id'], $this->input['message_id'],
implode("\n", $text), implode("\n", $text),
$data ?: false, $data ?: false,
); );
} else {
$this->send(
$this->input['chat'],
implode("\n", $text),
$this->input['message_id'],
$data ?: false,
);
}
} }
public function menu($type = false, $arg = false, $return = false) public function menu($type = false, $arg = false, $return = false)
@@ -4735,7 +4836,6 @@ DNS-over-HTTPS with IP:
$main[] = ''; $main[] = '';
$oc = $this->getHashSubdomain('oc'); $oc = $this->getHashSubdomain('oc');
$np = $this->getHashSubdomain('np'); $np = $this->getHashSubdomain('np');
$hy = $this->getHashSubdomain('hy');
if (!empty($conf['domain'])) { if (!empty($conf['domain'])) {
$ssl_expiry = $this->expireCert(); $ssl_expiry = $this->expireCert();
$certs = $this->domainsCert() ?: []; $certs = $this->domainsCert() ?: [];
@@ -4743,9 +4843,12 @@ DNS-over-HTTPS with IP:
$main[] = "<blockquote>"; $main[] = "<blockquote>";
$main[] = "Domains:"; $main[] = "Domains:";
$main[] = $conf['domain'] . (in_array($conf['domain'], $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); $main[] = $conf['domain'] . (in_array($conf['domain'], $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
if (!empty($np)) {
$main[] = 'naive ' . "$np.{$conf['domain']}" . (in_array("$np.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); $main[] = 'naive ' . "$np.{$conf['domain']}" . (in_array("$np.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
}
if (!empty($oc)) {
$main[] = 'openconnect ' . "$oc.{$conf['domain']}" . (in_array("$oc.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); $main[] = 'openconnect ' . "$oc.{$conf['domain']}" . (in_array("$oc.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
$main[] = 'hysteria ' . "$hy.{$conf['domain']}" . (in_array("$hy.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); }
if (!empty($conf['adguardkey'])) { if (!empty($conf['adguardkey'])) {
$main[] = "{$conf['adguardkey']}.{$conf['domain']}" . (in_array("{$conf['adguardkey']}.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '') . ' adguard DOT';; $main[] = "{$conf['adguardkey']}.{$conf['domain']}" . (in_array("{$conf['adguardkey']}.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '') . ' adguard DOT';;
} }
@@ -4754,6 +4857,10 @@ DNS-over-HTTPS with IP:
$main[] = $this->i18n('domain explain'); $main[] = $this->i18n('domain explain');
} }
} }
$ports = yaml_parse_file('/docker/compose')['services'];
$hy_port = explode(':', $c['hy']['ports'][0])[0];
$main[] = ''; $main[] = '';
$main[] = '<code>'; $main[] = '<code>';
@@ -4777,7 +4884,7 @@ DNS-over-HTTPS with IP:
$this->i18n('on') . ' 443', $this->i18n('on') . ' 443',
$this->i18n('on') . ' 443', $this->i18n('on') . ' 443',
$this->i18n('on') . ' 443', $this->i18n('on') . ' 443',
$this->i18n('on') . ' 443', $this->i18n($hy_port ? 'on' : 'off') . ($hy_port ? " $hy_port" : 'port unavailable'),
$this->i18n($c['tg'] ? 'on' : 'off') . ' ' . getenv('TGPORT'), $this->i18n($c['tg'] ? 'on' : 'off') . ' ' . getenv('TGPORT'),
$this->i18n($c['ad'] ? 'on' : 'off') . ' 853', $this->i18n($c['ad'] ? 'on' : 'off') . ' 853',
$this->i18n($c['ss'] ? 'on' : 'off') . ' ' . getenv('SSPORT'), $this->i18n($c['ss'] ? 'on' : 'off') . ' ' . getenv('SSPORT'),
@@ -4804,7 +4911,8 @@ DNS-over-HTTPS with IP:
$menu = [ $menu = [
'main' => [ 'main' => [
'text' => implode("\n", $main ?: []), 'text' => implode("\n", $main ?: []),
'data' => [ 'data' => array_merge(
[
[ [
[ [
'text' => $this->i18n($this->getPacConf()['amnezia'] ? 'amnezia' : 'wg_title'), 'text' => $this->i18n($this->getPacConf()['amnezia'] ? 'amnezia' : 'wg_title'),
@@ -4855,16 +4963,22 @@ DNS-over-HTTPS with IP:
'callback_data' => "/pacMenu 0", 'callback_data' => "/pacMenu 0",
], ],
], ],
],
[array_merge(
[ [
[ [
'text' => $this->i18n('Hysteria'), 'text' => $this->i18n('Hysteria'),
'callback_data' => "/menu hy", 'callback_data' => "/menu hy",
], ],
],
$conf['showdnstt'] ? [
[ [
'text' => $this->i18n('DNSTT'), 'text' => $this->i18n('DNSTT'),
'callback_data' => "/dnstt", 'callback_data' => "/dnstt",
], ],
], ] : [],
)],
[
[ [
[ [
'text' => $this->i18n('config'), 'text' => $this->i18n('config'),
@@ -4884,11 +4998,12 @@ DNS-over-HTTPS with IP:
], ],
], ],
], ],
)
], ],
'wg' => $type == 'wg' ? $this->statusWg($arg) : false, 'wg' => $type == 'wg' ? $this->statusWg($arg) : false,
'client' => $type == 'client' ? $this->getClient(...explode('_', $arg)) : false, 'client' => $type == 'client' ? $this->getClient(...explode('_', $arg)) : false,
'addpeer' => $type == 'addpeer' ? $this->addWg(...explode('_', $arg)) : false, 'addpeer' => $type == 'addpeer' ? $this->addWg(...explode('_', $arg)) : false,
'pac' => $type == 'pac' ? $this->pacMenu($arg) : false, 'pac' => $type == 'pac' ? $this->pacMenu((int) $arg) : false,
'adguard' => $type == 'adguard' ? $this->adguardMenu() : false, 'adguard' => $type == 'adguard' ? $this->adguardMenu() : false,
'config' => $type == 'config' ? $this->configMenu() : false, 'config' => $type == 'config' ? $this->configMenu() : false,
'ss' => $type == 'ss' ? $this->menuSS() : false, 'ss' => $type == 'ss' ? $this->menuSS() : false,
@@ -5953,10 +6068,12 @@ DNS-over-HTTPS with IP:
public function hysteriaMenu() public function hysteriaMenu()
{ {
$pac = $this->getPacConf(); $pac = $this->getPacConf();
$f = '/docker/compose';
$c = yaml_parse_file($f)['services'];
$port = explode(':', $c['hy']['ports'][0])[0];
$domain = $this->getDomain(); $domain = $this->getDomain();
$text[] = "Menu -> Hysteria"; $text[] = "Menu -> Hysteria";
$hy = $this->getHashSubdomain('hy'); $text[] = "server: " . ($port? "<code>$domain:$port</code>" : 'port unavailable');
$text[] = "server: <code>$hy.$domain</code>";
$text[] = "passwd: <code>{$pac['hysteria_pass']}</code>"; $text[] = "passwd: <code>{$pac['hysteria_pass']}</code>";
$data[] = [ $data[] = [
[ [
@@ -7908,17 +8025,11 @@ DNS-over-HTTPS with IP:
case 'package': case 'package':
echo yaml_emit(['payload' => array_map(fn($e) => "PROCESS-NAME,$e", $v['list'])]); echo yaml_emit(['payload' => array_map(fn($e) => "PROCESS-NAME,$e", $v['list'])]);
break; break;
case 'pac':
echo yaml_emit(['payload' => array_map(fn($e) => "+.$e", $v['list'])]);
break;
case 'subnet':
echo yaml_emit(['payload' => array_map(fn($e) => $e, $v['list'])]);
break;
default: default:
echo yaml_emit(['payload' => array_map(function($e) { echo yaml_emit(['payload' => array_map(function($e) {
if (preg_match('~^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d{1,2})?$~', $e)) { if (preg_match('~^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d{1,2})?$~', $e, $m)) {
return "IP-CIDR,$e"; return "IP-CIDR,$e" . (empty($m[1]) ? '/32' : '');
} else { } else {
return "DOMAIN-SUFFIX,$e"; return "DOMAIN-SUFFIX,$e";
} }
@@ -8019,10 +8130,10 @@ DNS-over-HTTPS with IP:
foreach ($rules as $k => $v) { foreach ($rules as $k => $v) {
if (array_key_exists('domain_suffix', $v)) { if (array_key_exists('domain_suffix', $v)) {
foreach ($v['domain_suffix'] as $j) { foreach ($v['domain_suffix'] as $j) {
if (!preg_match('~^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d{1,2})?$~', $j)) { if (!preg_match('~^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d{1,2})?$~', $j, $m)) {
$domains[] = $j; $domains[] = $j;
} else { } else {
$ips[] = $j; $ips[] = $j . (empty($m[1]) ? '/32' : '');
} }
} }
unset($rules[$k]['domain_suffix']); unset($rules[$k]['domain_suffix']);
@@ -8104,7 +8215,7 @@ DNS-over-HTTPS with IP:
{ {
$sub = $this->getHashSubdomain('oc'); $sub = $this->getHashSubdomain('oc');
$nginx = file_get_contents('/config/upstream.conf'); $nginx = file_get_contents('/config/upstream.conf');
$t = preg_replace('~#ocserv.+#ocserv~s', $domain ? "#ocserv\n$sub.$domain ocserv;\n#ocserv" : "#ocserv\n#$sub.\$domain ocserv;\n#ocserv", $nginx); $t = preg_replace('~#ocserv.+#ocserv~s', $domain ? "#ocserv\n" . ($sub ? '' : '#' ) . "$sub.$domain ocserv;\n#ocserv" : "#ocserv\n#$sub.\$domain ocserv;\n#ocserv", $nginx);
file_put_contents('/config/upstream.conf', $t); file_put_contents('/config/upstream.conf', $t);
$this->ssh("nginx -s reload 2>&1", 'up'); $this->ssh("nginx -s reload 2>&1", 'up');
} }
@@ -8113,16 +8224,7 @@ DNS-over-HTTPS with IP:
{ {
$sub = $this->getHashSubdomain('np'); $sub = $this->getHashSubdomain('np');
$nginx = file_get_contents('/config/upstream.conf'); $nginx = file_get_contents('/config/upstream.conf');
$t = preg_replace('~#naive.+#naive~s', $domain ? "#naive\n$sub.$domain naive;\n#naive" : "#naive\n#$sub.\$domain naive;\n#naive", $nginx); $t = preg_replace('~#naive.+#naive~s', $domain ? "#naive\n" . ($sub ? '' : '#' ) . "$sub.$domain naive;\n#naive" : "#naive\n#$sub.\$domain naive;\n#naive", $nginx);
file_put_contents('/config/upstream.conf', $t);
$this->ssh("nginx -s reload 2>&1", 'up');
}
public function setUpstreamDomainHysteria($domain)
{
$sub = $this->getHashSubdomain('hy');
$nginx = file_get_contents('/config/upstream.conf');
$t = preg_replace('~#hysteria.+#hysteria~s', $domain ? "#hysteria\n$sub.$domain hysteria;\n#hysteria" : "#hysteria\n#$sub.\$domain hysteria;\n#hysteria", $nginx);
file_put_contents('/config/upstream.conf', $t); file_put_contents('/config/upstream.conf', $t);
$this->ssh("nginx -s reload 2>&1", 'up'); $this->ssh("nginx -s reload 2>&1", 'up');
} }
@@ -8186,7 +8288,7 @@ DNS-over-HTTPS with IP:
public function getHashSubdomain($subdomain) public function getHashSubdomain($subdomain)
{ {
$p = $this->getPacConf(); $p = $this->getPacConf();
if (!empty($p["{$subdomain}_domain"])) { if (isset($p["{$subdomain}_domain"])) {
return $p["{$subdomain}_domain"]; return $p["{$subdomain}_domain"];
} }
$p["{$subdomain}_domain"] = substr(hash('sha256', "$subdomain{$this->key}"), 0, 8); $p["{$subdomain}_domain"] = substr(hash('sha256', "$subdomain{$this->key}"), 0, 8);
@@ -8482,7 +8584,6 @@ DNS-over-HTTPS with IP:
$conf = $this->getPacConf(); $conf = $this->getPacConf();
$oc = $this->getHashSubdomain('oc'); $oc = $this->getHashSubdomain('oc');
$np = $this->getHashSubdomain('np'); $np = $this->getHashSubdomain('np');
$hy = $this->getHashSubdomain('hy');
if (!empty($conf['domain'])) { if (!empty($conf['domain'])) {
$ssl_expiry = $this->expireCert(); $ssl_expiry = $this->expireCert();
$certs = $this->domainsCert() ?: []; $certs = $this->domainsCert() ?: [];
@@ -8492,7 +8593,6 @@ DNS-over-HTTPS with IP:
$text[] = $conf['domain'] . (in_array($conf['domain'], $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); $text[] = $conf['domain'] . (in_array($conf['domain'], $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
$text[] = 'naive ' . "$np.{$conf['domain']}" . (in_array("$np.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); $text[] = 'naive ' . "$np.{$conf['domain']}" . (in_array("$np.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
$text[] = 'openconnect ' . "$oc.{$conf['domain']}" . (in_array("$oc.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : ''); $text[] = 'openconnect ' . "$oc.{$conf['domain']}" . (in_array("$oc.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
$text[] = 'hysteria ' . "$hy.{$conf['domain']}" . (in_array("$hy.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '');
if (!empty($conf['adguardkey'])) { if (!empty($conf['adguardkey'])) {
$text[] = "{$conf['adguardkey']}.{$conf['domain']}" . (in_array("{$conf['adguardkey']}.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '') . ' adguard DOT';; $text[] = "{$conf['adguardkey']}.{$conf['domain']}" . (in_array("{$conf['adguardkey']}.{$conf['domain']}", $certs) ? ' (ssl: ' . date('Y-m-d H:i:s', $ssl_expiry) . ')' : '') . ' adguard DOT';;
} }
@@ -8675,6 +8775,10 @@ DNS-over-HTTPS with IP:
'text' => $this->i18n($c['dnstt'] ? 'on' : 'off') . ' 53 dnstt', 'text' => $this->i18n($c['dnstt'] ? 'on' : 'off') . ' 53 dnstt',
'callback_data' => "/hidePort dnstt", 'callback_data' => "/hidePort dnstt",
]], ]],
[[
'text' => $this->i18n($c['hy'] ? 'on' : 'off') . ' ' . explode(':', $c['hy']['ports'][0])[0] . ' hysteria',
'callback_data' => "/changePort hy",
]],
]; ];
if (!empty($pac['restart'])) { if (!empty($pac['restart'])) {
$data[] = [ $data[] = [
@@ -8709,17 +8813,93 @@ DNS-over-HTTPS with IP:
'dnstt' => '53:53/udp', 'dnstt' => '53:53/udp',
]; ];
$f = '/docker/compose'; $f = '/docker/compose';
$c = yaml_parse_file($f); $content = file_exists($f) ? file_get_contents($f) : '';
// Находим все сервисы с !override для ports
$overrides = [];
if (preg_match_all('/(\w+):\s*\n\s+ports:\s*!override/m', $content, $matches)) {
foreach ($matches[1] as $service) {
$overrides[$service] = true;
}
}
// Парсим YAML
$c = $content ? yaml_parse($content) : [];
// Изменяем структуру
if (!empty($c['services'][$container])) { if (!empty($c['services'][$container])) {
unset($c['services'][$container]); unset($c['services'][$container]);
} else { } else {
$c['services'][$container]['ports'][] = $ports[$container]; $c['services'][$container]['ports'][] = $ports[$container];
} }
// Записываем обратно
if (empty($c['services'])) { if (empty($c['services'])) {
file_put_contents($f, ''); file_put_contents($f, '');
} else { } else {
yaml_emit_file($f, $c); $yaml = yaml_emit($c);
// Восстанавливаем !override для ports тех сервисов где он был
foreach ($overrides as $service => $val) {
// Заменяем "ports:" на "ports: !override" для конкретного сервиса
$yaml = preg_replace(
'/(' . preg_quote($service, '/') . ':\s*\n\s+)ports:/m',
'${1}ports: !override',
$yaml
);
} }
file_put_contents($f, $yaml);
}
$pac = $this->getPacConf();
$pac['restart'] = 1;
$this->setPacConf($pac);
$this->ports();
}
public function setPort($port, $container)
{
$port = (int) $port;
$ports = [
'hy' => '443/udp',
];
$f = '/docker/compose';
$content = file_exists($f) ? file_get_contents($f) : '';
// Находим все сервисы с !override для ports
$overrides = [];
if (preg_match_all('/(\w+):\s*\n\s+ports:\s*!override/m', $content, $matches)) {
foreach ($matches[1] as $service) {
$overrides[$service] = true;
}
}
// Парсим YAML
$c = $content ? yaml_parse($content) : [];
// Изменяем структуру
if (!empty($port) && is_numeric($port) && $port != 443) {
$c['services'][$container]['ports'] = ["$port:$ports[$container]"];
} else {
unset($c['services'][$container]);
}
// Записываем обратно
if (empty($c['services'])) {
file_put_contents($f, '');
} else {
$yaml = yaml_emit($c);
// Восстанавливаем !override для ports тех сервисов где он был
foreach ($overrides as $service => $val) {
// Заменяем "ports:" на "ports: !override" для конкретного сервиса
$yaml = preg_replace(
'/(' . preg_quote($service, '/') . ':\s*\n\s+)ports:/m',
'${1}ports: !override',
$yaml
);
}
file_put_contents($f, $yaml);
}
$pac = $this->getPacConf(); $pac = $this->getPacConf();
$pac['restart'] = 1; $pac['restart'] = 1;
$this->setPacConf($pac); $this->setPacConf($pac);
@@ -8806,7 +8986,7 @@ DNS-over-HTTPS with IP:
$this->update( $this->update(
$this->input['chat'], $this->input['chat'],
$this->input['message_id'], $this->input['message_id'],
implode("\n", $text ?: ['...']), implode("\n", ['...']),
$data ?: false, $data ?: false,
); );
} }
@@ -8974,7 +9154,7 @@ DNS-over-HTTPS with IP:
$x = $this->getXray(); $x = $this->getXray();
$h = $this->getHashBot(); $h = $this->getHashBot();
$p['reality']['domain'] = $p['reality']['domain'] ?: 'web.telegram.org'; $p['reality']['domain'] = $p['reality']['domain'] ?: 'yandex.ru';
$p['reality']['destination'] = $p['reality']['destination'] ?: $p['reality']['domain'] . ':443'; $p['reality']['destination'] = $p['reality']['destination'] ?: $p['reality']['domain'] . ':443';
$p['transport'] = $transport; $p['transport'] = $transport;
@@ -9257,16 +9437,34 @@ DNS-over-HTTPS with IP:
{ {
$c = $this->getPacConf(); $c = $this->getPacConf();
if (empty($c[$this->getInstanceWG(1) . 'amnezia_keys'])) { if (empty($c[$this->getInstanceWG(1) . 'amnezia_keys'])) {
// S1 and S2: 064 bytes; constraint: S1 + 56 ≠ S2
$s1 = random_int(15, 64);
do {
$s2 = random_int(15, 64);
} while ($s1 + 56 === $s2);
// H1H4: distinct 32-bit values (must not overlap)
$h = [];
while (count($h) < 4) {
$v = random_int(1, 4_294_967_295);
if (!in_array($v, $h)) {
$h[] = $v;
}
}
$c[$this->getInstanceWG(1) . 'amnezia_keys'] = [ $c[$this->getInstanceWG(1) . 'amnezia_keys'] = [
'Jc' => rand(3, 10), 'Jc' => random_int(3, 10),
'Jmin' => 50, 'Jmin' => 64,
'Jmax' => 1000, 'Jmax' => 1000,
'S1' => rand(15, 150), 'S1' => $s1,
'S2' => rand(15, 150), 'S2' => $s2,
'H1' => rand(1, 2_147_483_647), 'S3' => random_int(0, 64),
'H2' => rand(1, 2_147_483_647), 'S4' => random_int(0, 32),
'H3' => rand(1, 2_147_483_647), 'H1' => $h[0],
'H4' => rand(1, 2_147_483_647), 'H2' => $h[1],
'H3' => $h[2],
'H4' => $h[3],
'I1' => '<b 0xc000000001><r 100>',
]; ];
$this->setPacConf($c); $this->setPacConf($c);
} }
@@ -9383,12 +9581,8 @@ DNS-over-HTTPS with IP:
public function restartWG($conf_str, $switch = false) public function restartWG($conf_str, $switch = false)
{ {
$this->ssh("echo '$conf_str' > /etc/wireguard/wg0.conf", $this->getInstanceWG()); $this->ssh("echo '$conf_str' > /etc/wireguard/wg0.conf", $this->getInstanceWG());
if (!empty($switch)) { $this->ssh("{$this->getWGType((int) $switch)}-quick down wg0", $this->getInstanceWG());
$this->ssh("{$this->getWGType(1)}-quick down wg0", $this->getInstanceWG());
$this->ssh("{$this->getWGType()}-quick up wg0", $this->getInstanceWG()); $this->ssh("{$this->getWGType()}-quick up wg0", $this->getInstanceWG());
} else {
$this->ssh("{$this->getWGType()} syncconf wg0 <({$this->getWGType()}-quick strip wg0)", $this->getInstanceWG());
}
return true; return true;
} }
@@ -9405,7 +9599,7 @@ DNS-over-HTTPS with IP:
$this->send($this->input['chat'], "disconnect: \n" . var_export($args, true) . "\n", $this->input['message_id']); $this->send($this->input['chat'], "disconnect: \n" . var_export($args, true) . "\n", $this->input['message_id']);
} }
public function ssh($cmd, $service = 'wg', $wait = true) public function ssh($cmd, $service = 'wg', $wait = true, $log = '/dev/null')
{ {
try { try {
$c = ssh2_connect($service, 22); $c = ssh2_connect($service, 22);
@@ -9422,9 +9616,10 @@ DNS-over-HTTPS with IP:
// nohup запускает процесс независимо от SSH-сессии // nohup запускает процесс независимо от SSH-сессии
// & переносит процесс в фон // & переносит процесс в фон
// </dev/null >/dev/null 2>&1 перенаправляет все потоки ввода-вывода // </dev/null >/dev/null 2>&1 перенаправляет все потоки ввода-вывода
$cmd = "nohup $cmd </dev/null >/dev/null 2>&1 &"; $cmd = "nohup sh -c \"$cmd 2>&1 | tee -a $log >&3\" 3>/proc/1/fd/1 </dev/null &";
} }
$s = ssh2_exec($c, $cmd); $s = ssh2_exec($c, $cmd);
if (empty($s)) { if (empty($s)) {
throw new Exception("exec fail: \n$cmd\n" . var_export($s, true)); throw new Exception("exec fail: \n$cmd\n" . var_export($s, true));
+2 -2
View File
@@ -25,8 +25,8 @@ $bot->cloakNginx();
$bot->syncDeny(); $bot->syncDeny();
$bot->cleanDocker(); $bot->cleanDocker();
$bot->dnsttStart(); $bot->dnsttStart();
$bot->restartHysteria();
$c = $bot->getPacConf(); $c = $bot->getPacConf();
$bot->setUpstreamDomain($c['pac']['transport'] != 'Reality' ? 't' : ($c['pac']['reality']['domain'] ?: $c['xray']['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0])); $bot->setUpstreamDomain($c['transport'] != 'Reality' ? 't' : $c['reality']['domain']);
$bot->setUpstreamDomainNaive($c['domain']); $bot->setUpstreamDomainNaive($c['domain']);
$bot->setUpstreamDomainOcserv($c['domain']); $bot->setUpstreamDomainOcserv($c['domain']);
$bot->setUpstreamDomainHysteria($c['domain']);
+3 -3
View File
@@ -99,7 +99,7 @@
"list": "~block~", "list": "~block~",
"action": "REJECT", "action": "REJECT",
"interval": 60, "interval": 60,
"behavior": "domain", "behavior": "classical",
"name": "block" "name": "block"
}, },
{ {
@@ -131,7 +131,7 @@
"list": "~pac~", "list": "~pac~",
"action": "PROXY", "action": "PROXY",
"interval": 60, "interval": 60,
"behavior": "domain", "behavior": "classical",
"name": "pac" "name": "pac"
}, },
{ {
@@ -139,7 +139,7 @@
"list": "~subnet~", "list": "~subnet~",
"action": "PROXY", "action": "PROXY",
"interval": 60, "interval": 60,
"behavior": "ipcidr", "behavior": "classical",
"name": "subnet" "name": "subnet"
}, },
{ {
+1
View File
@@ -122,3 +122,4 @@ AllowTcpForwarding yes
PermitTunnel yes PermitTunnel yes
GatewayPorts yes GatewayPorts yes
X11Forwarding yes X11Forwarding yes
LogLevel QUIET
-8
View File
@@ -32,10 +32,6 @@ stream {
server np:443; server np:443;
} }
upstream hysteria {
server hy:443;
}
map_hash_bucket_size 128; map_hash_bucket_size 128;
map $ssl_preread_server_name $sni_name { map $ssl_preread_server_name $sni_name {
#domain #domain
@@ -49,10 +45,6 @@ stream {
#naive #naive
#np.domain naive; #np.domain naive;
#naive #naive
#hysteria
#hy.domain hysteria;
#hysteria
default other; default other;
} }
+1 -1
View File
@@ -49,7 +49,7 @@
} }
], ],
"routing": { "routing": {
"domainStrategy": "AsIs", "domainStrategy": "IPIfNonMatch",
"rules": [ "rules": [
{ {
"inboundTag": [ "inboundTag": [
+14 -13
View File
@@ -40,22 +40,14 @@ services:
hostname: upstream hostname: upstream
container_name: upstream-${VER} container_name: upstream-${VER}
depends_on: depends_on:
php:
condition: service_healthy
ng: ng:
condition: service_healthy condition: service_healthy
ad:
condition: service_started
ss:
condition: service_started
xr: xr:
condition: service_started condition: service_started
oc: oc:
condition: service_started condition: service_started
np: np:
condition: service_started condition: service_started
hy:
condition: service_started
env_file: env_file:
- path: ./.env - path: ./.env
required: true # default required: true # default
@@ -106,6 +98,12 @@ services:
depends_on: depends_on:
php: php:
condition: service_healthy condition: service_healthy
ad:
condition: service_started
ss:
condition: service_started
xr:
condition: service_started
healthcheck: healthcheck:
test: ["CMD", "nginx", "-t"] test: ["CMD", "nginx", "-t"]
interval: 10s interval: 10s
@@ -216,7 +214,7 @@ services:
- dnstt - dnstt
- hy - hy
wg: wg:
image: mercurykd/vpnbot-wg:1.1 image: mercurykd/vpnbot-wg:1.2
build: build:
dockerfile: dockerfile/wireguard.dockerfile dockerfile: dockerfile/wireguard.dockerfile
args: args:
@@ -254,7 +252,7 @@ services:
ipv4_address: 10.10.0.4 ipv4_address: 10.10.0.4
logging: *default-logging logging: *default-logging
wg1: wg1:
image: mercurykd/vpnbot-wg:1.1 image: mercurykd/vpnbot-wg:1.2
build: build:
dockerfile: dockerfile/wireguard.dockerfile dockerfile: dockerfile/wireguard.dockerfile
args: args:
@@ -327,15 +325,16 @@ services:
entrypoint: ["/bin/sh", "/start_ad.sh"] entrypoint: ["/bin/sh", "/start_ad.sh"]
logging: *default-logging logging: *default-logging
tg: tg:
image: mercurykd/vpnbot-tg:1.2 image: mercurykd/vpnbot-tg:1.3
build: build:
dockerfile: dockerfile/telegram.dockerfile dockerfile: dockerfile/telegram.dockerfile
volumes: volumes:
- ./config/.profile:/root/.ashrc:ro - ./config/.profile:/root/.bashrc:ro
- ./ssh:/ssh - ./ssh:/ssh
- ./config/sshd_config:/etc/ssh/sshd_config - ./config/sshd_config:/etc/ssh/sshd_config
- ./scripts/start_tg.sh:/start_tg.sh - ./scripts/start_tg.sh:/start_tg.sh
- ./config/mtprotosecret:/mtprotosecret - ./config/mtprotosecret:/mtprotosecret
- ./logs/:/logs/
hostname: telegram hostname: telegram
container_name: mtproto-${VER} container_name: mtproto-${VER}
depends_on: depends_on:
@@ -450,7 +449,7 @@ services:
ipv4_address: 10.10.0.12 ipv4_address: 10.10.0.12
logging: *default-logging logging: *default-logging
wp: wp:
image: mercurykd/vpnbot-wp:1.4 image: mercurykd/vpnbot-wp:1.5
build: build:
dockerfile: dockerfile/warp.dockerfile dockerfile: dockerfile/warp.dockerfile
args: args:
@@ -550,6 +549,7 @@ services:
volumes: volumes:
- ./config/.profile:/root/.ashrc:ro - ./config/.profile:/root/.ashrc:ro
- ./ssh:/ssh - ./ssh:/ssh
- ./logs:/logs
- ./config/sshd_config:/etc/ssh/sshd_config - ./config/sshd_config:/etc/ssh/sshd_config
- ./config/dnstt:/dnstt - ./config/dnstt:/dnstt
- ./scripts/start_dnstt.sh:/start_dnstt.sh - ./scripts/start_dnstt.sh:/start_dnstt.sh
@@ -580,6 +580,7 @@ services:
- ./ssh:/ssh - ./ssh:/ssh
- ./config/sshd_config:/etc/ssh/sshd_config - ./config/sshd_config:/etc/ssh/sshd_config
- ./certs:/certs - ./certs:/certs
- ./logs:/logs
- ./config:/config - ./config:/config
- ./scripts/start_hysteria.sh:/start_hysteria.sh - ./scripts/start_hysteria.sh:/start_hysteria.sh
hostname: hysteria hostname: hysteria
+33 -13
View File
@@ -1,13 +1,33 @@
FROM alpine:3.6 # Build stage
RUN apk add --no-cache --virtual .build-deps alpine-sdk linux-headers openssl-dev \ FROM ubuntu:22.04 AS builder
&& git clone --single-branch --depth 1 https://github.com/TelegramMessenger/MTProxy.git /mtproxy/sources \
&& mkdir /mtproxy/patches && wget -P /mtproxy/patches https://raw.githubusercontent.com/alexdoesh/mtproxy/master/patches/randr_compat.patch \ RUN apt-get update && apt-get install -y --no-install-recommends \
&& cd /mtproxy/sources && patch -p0 -i /mtproxy/patches/randr_compat.patch \ git \
&& make \ build-essential \
&& mkdir /root/.ssh \ libssl-dev \
&& cp /mtproxy/sources/objs/bin/mtproto-proxy /usr/bin \ zlib1g-dev \
&& rm -rf /mtproxy \ ca-certificates \
&& apk del .build-deps\ && rm -rf /var/lib/apt/lists/*
&& apk add --no-cache --update curl openssh \
&& ln -s /usr/lib/libcrypto.so.41 /usr/lib/libcrypto.so.1.0.0 WORKDIR /src
ENV ENV="/root/.ashrc" RUN git clone --single-branch --depth 1 https://github.com/GetPageSpeed/MTProxy . \
&& make -j$(nproc) \
&& strip objs/bin/mtproto-proxy
# Runtime stage - minimal Ubuntu
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y --no-install-recommends \
libssl3 \
zlib1g \
curl \
openssh-client \
openssh-server \
ca-certificates \
vim-common \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /root/.ssh /var/run/sshd
COPY --from=builder /src/objs/bin/mtproto-proxy /usr/local/bin/mtproto-proxy
ENV PATH="/usr/local/bin:$PATH"
+14 -4
View File
@@ -1,6 +1,16 @@
FROM ubuntu:22.04 FROM ubuntu:22.04
RUN apt update && apt install -y curl gpg socat jq lsb-release openssh-server \
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
gpg \
socat \
jq \
lsb-release \
openssh-server \
ca-certificates \
&& curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg \ && curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/cloudflare-client.list \ && echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/cloudflare-client.list \
&& apt update && apt install -y cloudflare-warp \ && apt-get update && apt-get install -y --no-install-recommends cloudflare-warp \
&& mkdir /root/.ssh && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& mkdir -p /root/.ssh /var/run/sshd
+5 -9
View File
@@ -47,15 +47,11 @@ oc: # консоль сервиса
docker compose exec oc /bin/sh docker compose exec oc /bin/sh
service: # консоль сервиса service: # консоль сервиса
docker compose exec service /bin/sh docker compose exec service /bin/sh
clean: delete:
docker image prune make d
docker builder prune docker system prune -f -a
cleanf: docker volume prune -f -a
docker image prune -f > /dev/null rm -rf /root/vpnbot
docker builder prune -f > /dev/null
cleanall:
docker image prune -a -f
docker builder prune -a -f
push: push:
docker compose push docker compose push
s: s:
-1
View File
@@ -1,5 +1,4 @@
cat /ssh/key.pub > /root/.ssh/authorized_keys cat /ssh/key.pub > /root/.ssh/authorized_keys
ssh-keygen -A ssh-keygen -A
exec /usr/sbin/sshd -D -e "$@" & exec /usr/sbin/sshd -D -e "$@" &
hysteria server -c /config/hysteria.yaml &
tail -f /dev/null tail -f /dev/null
-1
View File
@@ -1,4 +1,3 @@
echo 'root:dummy_passwd'|chpasswd
cat /ssh/key.pub > /root/.ssh/authorized_keys cat /ssh/key.pub > /root/.ssh/authorized_keys
ssh-keygen -A ssh-keygen -A
exec /usr/sbin/sshd -D -e "$@" & exec /usr/sbin/sshd -D -e "$@" &
+4 -2
View File
@@ -14,7 +14,8 @@ then
echo "ListenPort = $WG1PORT" >> /etc/wireguard/wg0.conf echo "ListenPort = $WG1PORT" >> /etc/wireguard/wg0.conf
else else
sed "s/ListenPort = [0-9]\+/ListenPort = $WG1PORT/" /etc/wireguard/wg0.conf > change_port sed "s/ListenPort = [0-9]\+/ListenPort = $WG1PORT/" /etc/wireguard/wg0.conf > change_port
cat change_port > /etc/wireguard/wg0.conf sed "s|Address = [0-9\.\/ ]\+|Address = $ADDRESS|" change_port > change_address
cat change_address > /etc/wireguard/wg0.conf
fi fi
else else
if [ $(cat /etc/wireguard/wg0.conf | wc -c) -eq 0 ] if [ $(cat /etc/wireguard/wg0.conf | wc -c) -eq 0 ]
@@ -26,7 +27,8 @@ else
echo "ListenPort = $WGPORT" >> /etc/wireguard/wg0.conf echo "ListenPort = $WGPORT" >> /etc/wireguard/wg0.conf
else else
sed "s/ListenPort = [0-9]\+/ListenPort = $WGPORT/" /etc/wireguard/wg0.conf > change_port sed "s/ListenPort = [0-9]\+/ListenPort = $WGPORT/" /etc/wireguard/wg0.conf > change_port
cat change_port > /etc/wireguard/wg0.conf sed "s|Address = [0-9\.\/ ]\+|Address = $ADDRESS|" change_port > change_address
cat change_address > /etc/wireguard/wg0.conf
fi fi
fi fi
iptables -t nat -A POSTROUTING --destination 10.10.0.5 -j ACCEPT iptables -t nat -A POSTROUTING --destination 10.10.0.5 -j ACCEPT
+23
View File
@@ -1,3 +1,26 @@
27.03.2026 v2.30
- возможность понижать амнезию до 1
10.03.2026 v2.29
- amnezia 2.0
16.12.2025 v2.28.1
- фикс шаблона mihomo
15.12.2025 v2.28
- новый MTProto
- hysteria
- dnstt (вместо iodine)
- xhttp-vless by legiz
- возможность отключать поддомены для naive и openconnect (через установку поддомена в 0)
- возможность отключать naive и openconnect (через установку пароля в 0)
- возможность указывать айпишники в списках block, warp
- быстрая команда для добавления бота в крон: make cron
- увиличены тайминги пула рулсетов
- увиличен тайминг сбора статы с xray-ядра
- улучшено логирование: ssh не срет в контейнер, а основной процесс пишет в /logs
- улучшены кроны autobackup, autoscan, autoreset
- фикс старта upstream
- фикс шорт-ссылки для амнезии
- фикс папки override для своих заглушек
- фикс сброса страницы в списках route
09.11.2025 v2.27 09.11.2025 v2.27
- фикс reality - фикс reality
08.11.2025 v2.26 08.11.2025 v2.26