iodine -> dnstt

This commit is contained in:
mercury
2025-11-30 20:46:31 +04:00
parent 9f196ca58e
commit 95c6a023d0
10 changed files with 125 additions and 77 deletions
+86 -46
View File
@@ -317,20 +317,23 @@ class Bot
case preg_match('~^/logs$~', $this->input['callback'], $m):
$this->logs();
break;
case preg_match('~^/iodine$~', $this->input['callback'], $m):
$this->iodine();
case preg_match('~^/dnstt$~', $this->input['callback'], $m):
$this->dnstt();
break;
case preg_match('~^/iodineDomain$~', $this->input['callback'], $m):
$this->iodineDomain();
case preg_match('~^/dnsttDownload$~', $this->input['callback'], $m):
$this->dnsttDownload();
break;
case preg_match('~^/iodinePassword$~', $this->input['callback'], $m):
$this->iodinePassword();
case preg_match('~^/dnsttDomain$~', $this->input['callback'], $m):
$this->dnsttDomain();
break;
case preg_match('~^/setIodineDomain (\w+)$~', $this->input['callback'], $m):
$this->setIodineDomain($m[1]);
case preg_match('~^/dnsttPassword$~', $this->input['callback'], $m):
$this->dnsttPassword();
break;
case preg_match('~^/setIodinePassword (\w+)$~', $this->input['callback'], $m):
$this->setIodinePassword($m[1]);
case preg_match('~^/setdnsttDomain (\w+)$~', $this->input['callback'], $m):
$this->setdnsttDomain($m[1]);
break;
case preg_match('~^/setdnsttPassword (\w+)$~', $this->input['callback'], $m):
$this->setdnsttPassword($m[1]);
break;
case preg_match('~^/getLog (?P<arg>\d+(?:_(?:-)?\d+)?)$~', $this->input['callback'], $m):
$this->getLog(...explode('_', $m['arg']));
@@ -1138,7 +1141,7 @@ class Bot
$t = preg_replace('~^(\t+)?basic_auth[^\n]+~sm', '$1basic_auth ' . ($pac['naive']['user'] ?? '_') . ' ' . ($pac['naive']['pass'] ?? '__'), $c);
file_put_contents('/config/Caddyfile', $t);
if (!empty($pac['naive']['pass'])) {
$this->ssh('caddy run -c /config/Caddyfile > /dev/null 2>&1 &', 'np', false);
$this->ssh('caddy run -c /config/Caddyfile', 'np', false);
}
}
@@ -2829,7 +2832,7 @@ DNS-over-HTTPS with IP:
public function startAd()
{
return $this->ssh('/opt/adguardhome/AdGuardHome --no-check-update --pidfile /opt/adguardhome/pid -c /config/AdGuardHome.yaml -h 0.0.0.0 -w /opt/adguardhome/work > /dev/null 2>&1 &', 'ad', false);
return $this->ssh('/opt/adguardhome/AdGuardHome --no-check-update --pidfile /opt/adguardhome/pid -c /config/AdGuardHome.yaml -h 0.0.0.0 -w /opt/adguardhome/work', 'ad', false);
}
public function stopAd()
@@ -4540,7 +4543,7 @@ DNS-over-HTTPS with IP:
return implode("\n", $result);
}
public function iodineDomain()
public function dnsttDomain()
{
$r = $this->send(
$this->input['chat'],
@@ -4550,12 +4553,12 @@ DNS-over-HTTPS with IP:
);
$_SESSION['reply'][$r['result']['message_id']] = [
'start_message' => $this->input['message_id'],
'callback' => 'setIodineDomain',
'callback' => 'setdnsttDomain',
'args' => [],
];
}
public function iodinePassword()
public function dnsttPassword()
{
$r = $this->send(
$this->input['chat'],
@@ -4565,63 +4568,82 @@ DNS-over-HTTPS with IP:
);
$_SESSION['reply'][$r['result']['message_id']] = [
'start_message' => $this->input['message_id'],
'callback' => 'setIodinePassword',
'callback' => 'setdnsttPassword',
'args' => [],
];
}
public function setIodinePassword($text)
public function setdnsttPassword($text)
{
$c = $this->getPacConf();
if ($text) {
$c['iodinePassword'] = $text;
$c['dnsttPassword'] = $text;
} else {
unset($c['iodinePassword']);
unset($c['dnsttPassword']);
}
$this->setPacConf($c);
$this->iodineRestart();
$this->iodine();
$this->dnsttStart();
$this->dnstt();
}
public function setIodineDomain($text)
public function setdnsttDomain($text)
{
$c = $this->getPacConf();
if ($text) {
$c['iodineDomain'] = $text;
$c['dnsttDomain'] = $text;
} else {
unset($c['iodineDomain']);
unset($c['dnsttDomain']);
}
$this->setPacConf($c);
$this->iodineRestart();
$this->iodine();
$this->dnsttStart();
$this->dnstt();
}
public function iodineRestart()
public function dnsttStart()
{
$c = $this->getPacConf();
$this->ssh('pkill iodine', 'io');
if (!empty($c['iodineDomain']) && !empty($c['iodinePassword'])) {
$this->ssh("iodined -c -P {$c['iodinePassword']} 10.0.0.1 {$c['iodineDomain']}", 'io');
$this->ssh('pkill dnstt', 'dnstt');
if (!empty($c['dnsttDomain']) && !empty($c['dnsttPassword'])) {
$this->ssh("adduser -D -s /bin/sh vpnbot", 'dnstt');
$this->ssh("echo 'vpnbot:{$c['dnsttPassword']}' | chpasswd", 'dnstt');
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 -udp :53 -privkey-file /dnstt/server.key {$c['dnsttDomain']} 127.0.0.1:22", 'dnstt' , false);
}
}
public function iodine()
public function dnsttDownload()
{
$this->sendFile($this->input['from'], curl_file_create('/config/dnstt/server.pub'));
}
public function dnstt()
{
$c = $this->getPacConf();
$text[] = 'Iodine';
$text[] = "domain: {$c['iodineDomain']}";
$text[] = "password: {$c['iodinePassword']}";
$pubkey = file_get_contents('/config/dnstt/server.pub');
$text[] = "dnstt";
$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[] = "ssh: <code>vpnbot:{$c['dnsttPassword']}</code>";
$text[] = "pubkey: <code>$pubkey</code>";
$data[] = [
[
'text' => $this->i18n('download pubkey'),
'callback_data' => "/dnsttDownload",
],
];
$data[] = [
[
'text' => $this->i18n('set domain'),
'callback_data' => "/iodineDomain",
'callback_data' => "/dnsttDomain",
],
];
$data[] = [
[
'text' => $this->i18n('set password'),
'callback_data' => "/iodinePassword",
'callback_data' => "/dnsttPassword",
],
];
$data[] = [
@@ -4693,7 +4715,7 @@ DNS-over-HTTPS with IP:
$this->i18n($this->ssh('pgrep mtproto-proxy', 'tg') ? 'on' : 'off') . ' ' . $this->i18n('mtproto'),
$this->i18n(exec("JSON=1 timeout 2 dnslookup google.com ad") ? 'on' : 'off') . ' ' . $this->i18n('ad_title'),
$this->i18n($this->ssh('pgrep ssserver', 'ss') ? 'on' : 'off') . ' ' . $this->i18n('sh_title'),
$this->i18n($this->ssh('pgrep iodine', 'io') ? 'on' : 'off') . ' ' . $this->i18n('Iodine'),
$this->i18n($this->ssh('pgrep dnstt', 'dnstt') ? 'on' : 'off') . ' ' . $this->i18n('dnstt'),
$this->i18n($this->warpStatus()) . ' ' . $this->i18n('warp'),
],
[
@@ -4705,7 +4727,7 @@ DNS-over-HTTPS with IP:
$this->i18n($c['tg'] ? 'on' : 'off') . ' ' . getenv('TGPORT'),
$this->i18n($c['ad'] ? 'on' : 'off') . ' 853',
$this->i18n($c['ss'] ? 'on' : 'off') . ' ' . getenv('SSPORT'),
$this->i18n($c['io'] ? 'on' : 'off') . ' 53',
$this->i18n($c['dnstt'] ? 'on' : 'off') . ' 53',
'',
],
]);
@@ -4781,8 +4803,8 @@ DNS-over-HTTPS with IP:
],
[
[
'text' => $this->i18n('Iodine'),
'callback_data' => "/iodine",
'text' => $this->i18n('dnstt'),
'callback_data' => "/dnstt",
],
],
[
@@ -8380,8 +8402,8 @@ DNS-over-HTTPS with IP:
'callback_data' => "/hidePort ss",
]],
[[
'text' => $this->i18n($c['io'] ? 'on' : 'off') . ' 53 Iodine',
'callback_data' => "/hidePort io",
'text' => $this->i18n($c['dnstt'] ? 'on' : 'off') . ' 53 dnstt',
'callback_data' => "/hidePort dnstt",
]],
];
if (!empty($pac['restart'])) {
@@ -8414,7 +8436,7 @@ DNS-over-HTTPS with IP:
'tg' => getenv('TGPORT') . ':' . getenv('TGPORT'),
'ad' => '853:853',
'ss' => '8388:8388',
'io' => '53:53/udp',
'dnstt' => '53:53/udp',
];
$f = '/docker/compose';
$c = yaml_parse_file($f);
@@ -9091,15 +9113,33 @@ DNS-over-HTTPS with IP:
if (empty($a)) {
throw new Exception("auth fail: \n$cmd\n" . var_export($a, true));
}
// Оборачиваем команду для выполнения в фоновом режиме
if (!$wait) {
// nohup запускает процесс независимо от SSH-сессии
// & переносит процесс в фон
// </dev/null >/dev/null 2>&1 перенаправляет все потоки ввода-вывода
$cmd = "nohup $cmd </dev/null >/dev/null 2>&1 &";
}
$s = ssh2_exec($c, $cmd);
if (empty($s)) {
throw new Exception("exec fail: \n$cmd\n" . var_export($s, true));
}
stream_set_blocking($s, $wait);
$data = "";
while ($buf = fread($s, 4096)) {
$data .= $buf;
if ($wait) {
// Только для синхронных команд читаем вывод
stream_set_blocking($s, true);
while ($buf = fread($s, 4096)) {
$data .= $buf;
}
} else {
// Для фоновых команд просто даем время запуститься
stream_set_blocking($s, false);
usleep(100000); // 100ms для запуска процесса
}
fclose($s);
ssh2_disconnect($c);
} catch (Exception | Error $e) {
+1 -1
View File
@@ -24,4 +24,4 @@ $bot->adguardSync();
$bot->cloakNginx();
$bot->syncDeny();
$bot->cleanDocker();
$bot->iodineRestart();
$bot->dnsttStart();
+8 -4
View File
@@ -83,9 +83,9 @@ AuthorizedKeysFile .ssh/authorized_keys
#AllowAgentForwarding yes
# Feel free to re-enable these if your use case requires them.
AllowTcpForwarding no
GatewayPorts no
X11Forwarding no
# AllowTcpForwarding no
# GatewayPorts no
# X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
@@ -117,4 +117,8 @@ Subsystem sftp internal-sftp
# ForceCommand cvs server
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedKeyTypes +ssh-rsa
PasswordAuthentication no
PasswordAuthentication yes
AllowTcpForwarding yes
PermitTunnel yes
GatewayPorts yes
X11Forwarding yes
+11 -11
View File
@@ -211,6 +211,7 @@ services:
- np
- proxy
- ss
- dnstt
wg:
image: mercurykd/vpnbot-wg:1.1
build:
@@ -537,21 +538,20 @@ services:
default:
ipv4_address: 10.10.0.6
logging: *default-logging
io:
image: mercurykd/vpnbot-io:1.0
dnstt:
image: mercurykd/vpnbot-dnstt:1.0
build:
dockerfile: dockerfile/iodine.dockerfile
dockerfile: dockerfile/dnstt.dockerfile
args:
image: ${IMAGE}
volumes:
- ./config/.profile:/root/.ashrc:ro
- ./ssh:/ssh
- ./config/sshd_config:/etc/ssh/sshd_config
- ./scripts/start_io.sh:/start_io.sh
hostname: iodine
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
container_name: iodine-${VER}
- ./config/dnstt:/dnstt
- ./scripts/start_dnstt.sh:/start_dnstt.sh
hostname: dnstt
container_name: dnstt-${VER}
depends_on:
php:
condition: service_healthy
@@ -561,7 +561,7 @@ services:
- path: ./override.env
required: false
stop_grace_period: 1s
command: ["/bin/sh", "/start_io.sh"]
command: ["/bin/sh", "/start_dnstt.sh"]
networks:
default:
ipv4_address: 10.10.0.16
+13
View File
@@ -0,0 +1,13 @@
ARG image
FROM golang:alpine AS go
RUN apk add --no-cache git \
&& git clone https://www.bamsoftware.com/git/dnstt.git \
&& cd /go/dnstt/dnstt-server \
&& go build -ldflags="-s -w" -trimpath \
&& rm -rf /go/dnstt/.git \
&& apk del git
FROM $image
COPY --from=go /go/dnstt/dnstt-server/dnstt-server /usr/local/bin/
RUN apk add --no-cache openssh iptables \
&& mkdir -p /root/.ssh
ENV ENV="/root/.ashrc"
-4
View File
@@ -1,4 +0,0 @@
FROM alpine:3.22
RUN apk add iodine openssh iptables\
&& mkdir /root/.ssh
ENV ENV="/root/.ashrc"
+2 -2
View File
@@ -37,8 +37,8 @@ proxy: # консоль сервиса
docker compose exec proxy /bin/sh
tg: # консоль сервиса
docker compose exec tg /bin/sh
io: # консоль сервиса
docker compose exec io /bin/sh
dnstt: # консоль сервиса
docker compose exec dnstt /bin/sh
xr: # консоль сервиса
docker compose exec xr /bin/sh
oc: # консоль сервиса
+4
View File
@@ -0,0 +1,4 @@
cat /ssh/key.pub > /root/.ssh/authorized_keys
ssh-keygen -A
exec /usr/sbin/sshd -D -e "$@" &
tail -f /dev/null
-7
View File
@@ -1,7 +0,0 @@
cat /ssh/key.pub > /root/.ssh/authorized_keys
ssh-keygen -A
exec /usr/sbin/sshd -D -e "$@" &
iptables -A FORWARD -i dns0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o dns0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
tail -f /dev/null
-2
View File
@@ -1,6 +1,4 @@
cat /ssh/key.pub > /root/.ssh/authorized_keys
echo 'HostKeyAlgorithms +ssh-rsa' >> /etc/ssh/sshd_config
echo 'PubkeyAcceptedKeyTypes +ssh-rsa' >> /etc/ssh/sshd_config
service ssh start
off=$(cat /config/pac.json | jq -r .warpoff)
key=$(cat /config/pac.json | jq -r .warp)