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