diff --git a/app/bot.php b/app/bot.php
index f829139..f89316e 100644
--- a/app/bot.php
+++ b/app/bot.php
@@ -263,6 +263,12 @@ class Bot
case preg_match('~^/domain$~', $this->input['callback'], $m):
$this->domain();
break;
+ case preg_match('~^/xray$~', $this->input['callback'], $m):
+ $this->xray();
+ break;
+ case preg_match('~^/generateSecretXray$~', $this->input['callback'], $m):
+ $this->generateSecretXray();
+ break;
case preg_match('~^/include (\d+)$~', $this->input['callback'], $m):
$this->include($m[1]);
break;
@@ -342,6 +348,13 @@ class Bot
}
}
+ public function restartXray($c)
+ {
+ $this->ssh('pkill xray', 'xr');
+ file_put_contents('/config/xray.json', json_encode($c, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
+ $this->ssh('xray run -config /xray.json > /dev/null 2>&1 &', 'xr');
+ }
+
public function mtproto()
{
$s = file_get_contents('/config/mtprotosecret');
@@ -727,6 +740,7 @@ class Bot
'public' => file_get_contents('/certs/cert_public'),
] : false,
'mtproto' => file_get_contents('/config/mtprotosecret'),
+ 'xray' => file_get_contents('/config/xray.json'),
];
return json_encode($conf, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -811,6 +825,12 @@ class Bot
$this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out));
$this->restartTG($json['mtproto']);
}
+ // xray
+ if (!empty($json['xray'])) {
+ $out[] = 'update xray';
+ $this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out));
+ $this->restartXray($json['xray']);
+ }
// nginx
$out[] = 'reset nginx';
$this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out));
@@ -2445,7 +2465,11 @@ DNS-over-HTTPS with IP:
[
'text' => $this->i18n('mtproto'),
'callback_data' => "/mtproto",
- ]
+ ],
+ [
+ 'text' => $this->i18n('xray'),
+ 'callback_data' => "/xray",
+ ],
],
[
[
@@ -2503,6 +2527,57 @@ DNS-over-HTTPS with IP:
}
}
+ public function xray()
+ {
+ $c = json_decode(file_get_contents('/config/xray.json'), true);
+ $pac = $this->getPacConf();
+ $st = $this->ssh('pgrep xray', 'xr') ? 'on' : 'off';
+ $text[] = "Menu -> " . $this->i18n('xray') . "\n";
+ $text[] = "uuid: {$c['inbounds'][0]['settings']['clients'][0]['id']}";
+ $text[] = "shortId: {$c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0]}";
+ $text[] = "pubkey: {$pac['xray']}";
+ $text[] = "\nstatus: $st";
+
+ $data[] = [
+ [
+ 'text' => $this->i18n('generateSecret'),
+ 'callback_data' => "/generateSecretXray",
+ ],
+ ];
+ $data[] = [
+ [
+ 'text' => $this->i18n('back'),
+ 'callback_data' => "/menu",
+ ],
+ ];
+ $this->update(
+ $this->input['chat'],
+ $this->input['message_id'],
+ implode("\n", $text ?: ['...']),
+ $data ?: false,
+ );
+ }
+
+ public function generateSecretXray()
+ {
+ $c = json_decode(file_get_contents('/config/xray.json'), true);
+ $uuid = trim($this->ssh('xray uuid', 'xr'));
+ $shortId = trim($this->ssh('openssl rand -hex 8', 'xr'));
+ $keys = $this->ssh('xray x25519', 'xr');
+ preg_match('~^Private key:\s([^\s]+)~m', $keys, $m);
+ $private = trim($m[1]);
+ preg_match('~^Public key:\s([^\s]+)~m', $keys, $m);
+ $public = trim($m[1]);
+ $c['inbounds'][0]['settings']['clients'][0]['id'] = $uuid;
+ $c['inbounds'][0]['streamSettings']['realitySettings']['privateKey'] = $private;
+ $c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0] = $shortId;
+ $pac = $this->getPacConf();
+ $pac['xray'] = $public;
+ $this->setPacConf($pac);
+ $this->restartXray($c);
+ $this->xray();
+ }
+
public function addWg($page)
{
$text = "Menu -> Wireguard -> Add peer\n\n";
diff --git a/app/i18n.php b/app/i18n.php
index 9ffcf9f..cf7af9a 100644
--- a/app/i18n.php
+++ b/app/i18n.php
@@ -253,4 +253,8 @@ $i = [
'en' => 'clear',
'ru' => 'очистить',
],
+ 'xray' => [
+ 'en' => 'XTLS-Reality',
+ 'ru' => 'XTLS-Reality',
+ ],
];
diff --git a/config/nginx.conf b/config/nginx.conf
index 7298c76..ffb778e 100644
--- a/config/nginx.conf
+++ b/config/nginx.conf
@@ -1,6 +1,8 @@
user nginx;
worker_processes auto;
+load_module /usr/lib/nginx/modules/ngx_stream_module.so;
+
error_log /logs/nginx_error;
pid /var/run/nginx.pid;
@@ -8,6 +10,32 @@ events {
worker_connections 1024;
}
+stream {
+ upstream other {
+ server ng:8443;
+ }
+
+ upstream reality {
+ server xr:443;
+ }
+
+ map $ssl_preread_server_name $sni_name {
+ www.microsoft.com reality;
+ default other;
+ }
+
+ server {
+ listen 443 reuseport;
+ proxy_pass $sni_name;
+ ssl_preread on;
+ }
+
+ server {
+ listen 443 udp;
+ proxy_pass xr:443;
+ }
+}
+
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
@@ -17,7 +45,7 @@ http {
server {
listen 80 default_server;
- listen 443 ssl http2 default_server;
+ listen 8443 ssl http2 default_server;
ssl_certificate /certs/self_public;
ssl_certificate_key /certs/self_private;
@@ -65,7 +93,7 @@ http {
# server_name ;
#-domain
#-ssl
- # listen 443 ssl http2;
+ # listen 8443 ssl http2;
# ssl_certificate /certs/cert_public;
# ssl_certificate_key /certs/cert_private;
#-ssl
diff --git a/config/nginx_default.conf b/config/nginx_default.conf
index 7298c76..ffb778e 100644
--- a/config/nginx_default.conf
+++ b/config/nginx_default.conf
@@ -1,6 +1,8 @@
user nginx;
worker_processes auto;
+load_module /usr/lib/nginx/modules/ngx_stream_module.so;
+
error_log /logs/nginx_error;
pid /var/run/nginx.pid;
@@ -8,6 +10,32 @@ events {
worker_connections 1024;
}
+stream {
+ upstream other {
+ server ng:8443;
+ }
+
+ upstream reality {
+ server xr:443;
+ }
+
+ map $ssl_preread_server_name $sni_name {
+ www.microsoft.com reality;
+ default other;
+ }
+
+ server {
+ listen 443 reuseport;
+ proxy_pass $sni_name;
+ ssl_preread on;
+ }
+
+ server {
+ listen 443 udp;
+ proxy_pass xr:443;
+ }
+}
+
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
@@ -17,7 +45,7 @@ http {
server {
listen 80 default_server;
- listen 443 ssl http2 default_server;
+ listen 8443 ssl http2 default_server;
ssl_certificate /certs/self_public;
ssl_certificate_key /certs/self_private;
@@ -65,7 +93,7 @@ http {
# server_name ;
#-domain
#-ssl
- # listen 443 ssl http2;
+ # listen 8443 ssl http2;
# ssl_certificate /certs/cert_public;
# ssl_certificate_key /certs/cert_private;
#-ssl
diff --git a/config/xray.json b/config/xray.json
new file mode 100644
index 0000000..cf06dac
--- /dev/null
+++ b/config/xray.json
@@ -0,0 +1,60 @@
+{
+ "inbounds": [
+ {
+ "port": 443,
+ "protocol": "vless",
+ "settings": {
+ "clients": [
+ {
+ "email": "user1@myserver",
+ "flow": "xtls-rprx-vision",
+ "id": ""
+ }
+ ],
+ "decryption": "none"
+ },
+ "sniffing": {
+ "destOverride": [
+ "http",
+ "tls"
+ ],
+ "enabled": true
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "realitySettings": {
+ "dest": "www.microsoft.com:443",
+ "maxClientVer": "",
+ "maxTimeDiff": 0,
+ "minClientVer": "",
+ "privateKey": "",
+ "serverNames": [
+ "www.microsoft.com"
+ ],
+ "shortIds": [],
+ "show": false,
+ "xver": 0
+ },
+ "security": "reality"
+ },
+ "tag": "vless_tls"
+ }
+ ],
+ "log": {
+ "loglevel": "info"
+ },
+ "outbounds": [
+ {
+ "protocol": "freedom",
+ "tag": "direct"
+ },
+ {
+ "protocol": "blackhole",
+ "tag": "block"
+ }
+ ],
+ "routing": {
+ "domainStrategy": "AsIs",
+ "rules": []
+ }
+}
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index f7c0d3b..e8c1baf 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -64,6 +64,7 @@ services:
- ./config/ssserver.json:/config/ssserver.json
- ./config/sslocal.json:/config/sslocal.json
- ./config/mtprotosecret:/config/mtprotosecret
+ - ./config/xray.json:/config/xray.json
- ./certs/:/certs/
- type: volume
target: /config/adguard
@@ -226,3 +227,24 @@ services:
default:
ipv4_address: 10.10.0.8
logging: *default-logging
+ xr:
+ build:
+ dockerfile: dockerfile/xray.dockerfile
+ volumes:
+ - ./config/.profile:/root/.ashrc:ro
+ - ./ssh:/ssh
+ - ./config/sshd_config:/etc/ssh/sshd_config
+ - ./config/xray.json:/xray.json
+ - ./scripts/start_xray.sh:/start_xray.sh
+ hostname: xray
+ depends_on:
+ php:
+ condition: service_healthy
+ environment:
+ TZ: ${TZ}
+ stop_grace_period: 1s
+ command: ["/bin/sh", "/start_xray.sh"]
+ networks:
+ default:
+ ipv4_address: 10.10.0.9
+ logging: *default-logging
diff --git a/dockerfile/nginx.dockerfile b/dockerfile/nginx.dockerfile
index bccbb11..4026fd3 100644
--- a/dockerfile/nginx.dockerfile
+++ b/dockerfile/nginx.dockerfile
@@ -1,5 +1,5 @@
from alpine:latest
-run apk add nginx openssh \
+run apk add nginx-mod-stream openssh \
&& mkdir /root/.ssh \
&& mkdir /var/cache/nginx
env ENV="/root/.ashrc"
diff --git a/dockerfile/xray.dockerfile b/dockerfile/xray.dockerfile
new file mode 100644
index 0000000..d4a5c23
--- /dev/null
+++ b/dockerfile/xray.dockerfile
@@ -0,0 +1,11 @@
+from alpine:latest
+run apk add openssh openssl jq \
+ && mkdir /root/.ssh \
+ && wget https://github.com/XTLS/Xray-core/releases/download/v1.8.3/Xray-linux-64.zip \
+ && unzip Xray-linux-64.zip \
+ && mv xray /usr/bin/ \
+ && rm Xray-linux-64.zip \
+ && rm geoip.dat \
+ && rm geosite.dat \
+ && chmod +x /usr/bin/xray
+env ENV="/root/.ashrc"
\ No newline at end of file
diff --git a/makefile b/makefile
index 30b9830..96613b0 100644
--- a/makefile
+++ b/makefile
@@ -25,6 +25,8 @@ proxy: # консоль сервиса
docker compose exec proxy /bin/sh
tg: # консоль сервиса
docker compose exec tg /bin/sh
+xr: # консоль сервиса
+ docker compose exec xr /bin/sh
clean:
docker image prune
docker builder prune
diff --git a/scripts/start_xray.sh b/scripts/start_xray.sh
new file mode 100644
index 0000000..254b066
--- /dev/null
+++ b/scripts/start_xray.sh
@@ -0,0 +1,8 @@
+cat /ssh/key.pub > /root/.ssh/authorized_keys
+ssh-keygen -A
+exec /usr/sbin/sshd -D -e "$@" &
+if [ $(cat /xray.json | jq -r '.inbounds[0].settings.clients[0].id' | wc -c) -gt 1 ]
+then
+ xray run -config /xray.json &
+fi
+tail -f /dev/null