From 520bca6992b6f96940dde0f750454d4183cc3193 Mon Sep 17 00:00:00 2001 From: Evgeny Nerush Date: Tue, 26 Mar 2024 17:00:10 +0300 Subject: [PATCH] add CDN support at the second ip address --- CDN.md | 1 + CDN.ru.md | 32 +++++ README.md | 5 +- README.ru.md | 7 +- ex.sh | 131 +++++++++++++++--- nginx.conf | 85 ++++++++++++ template_config_client_cdn.jsonc | 229 +++++++++++++++++++++++++++++++ template_config_server.jsonc | 24 ++++ template_site4cdn.conf | 35 +++++ 9 files changed, 530 insertions(+), 19 deletions(-) create mode 100644 CDN.md create mode 100644 CDN.ru.md create mode 100644 nginx.conf create mode 100644 template_config_client_cdn.jsonc create mode 100644 template_site4cdn.conf diff --git a/CDN.md b/CDN.md new file mode 100644 index 0000000..1333ed7 --- /dev/null +++ b/CDN.md @@ -0,0 +1 @@ +TODO diff --git a/CDN.ru.md b/CDN.ru.md new file mode 100644 index 0000000..fe15d73 --- /dev/null +++ b/CDN.ru.md @@ -0,0 +1,32 @@ +#### Дополнительный канал связи + +Если вы делитесь доступом к своему серверу с другими людьми, рано или поздно IP-адрес вашего сервера может утечь к РКН, который добавит его +в список блокируемых сайтов, доступ к серверу при этом пропадёт. Если же сервер работает через сеть доставки контента (content delivery +network, CDN), то для доступа используется не IP-адрес, а доменное имя. Для того, чтобы заблокировать сервер в этом случае нужно будет +заблокировать один из серверов доменных имён CDN-провайдера (на котором "резолвится" ваше доменное имя). На этом сервере имён может +резолвиться большое число других сайтов (в том числе российских), и в случае блокировки они тоже перестанут работать. Так что, возможно, РКН +не станет идти на такие риски и не будет блокировать ваш сервер даже в случае обнаружения. + +CDN от Cloudflare позволяет бесплатно транспортировать grpc-трафик, однако вам потребуется купить какое-либо доменное имя. Чтобы понять +общую концепцию работы xray через CDN, прочитайте [вот эту статью](https://habr.com/ru/articles/761798/). + +**До запуска установки xray** скопируйте ваши сертификаты Cloudflare `cert.pem` и `cert.key` в директорию со скриптом `ex.sh`! + +Vless не может быть сконфигурирован с fallback при использовании grpc, соответственно, не может противостоять атаке active-probing. Поэтому +помимо xray нужно установить веб-сервер **nginx**, который будет обеспечивать перенаправление на сайт-заглушку. Easy-xray сгенерирует +"сайт" для nginx, который слушает IPv6 адрес и перенаправляет реальных пользователей на xray-сервер, слушающий на 127.0.0.1:50051. При +настройке easy-xray (при выполнении команды `sudo ./ex.sh install` или `./ex.sh conf`) укажите ваше доменное имя, и следуйте дальнейшим +инструкциям. После установки нужно проверить конфигурацию nginx и запустить его с помощью команд + +``` +sudo nginx -t +sudo systemctl start nginx +``` + +Если при попытке зайти на ваш "сайт" из браузера редирект (fallback) на сайт-заглушку не происходит (error 502, bad gateway), возможно, +политики SELinux запрещают nginx выполнять сетевые соединения. Чтобы это исправить, выполните команду + +``` +sudo setsebool -P httpd_can_network_connect 1 +``` + diff --git a/README.md b/README.md index f9c4934..be9a4f0 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ can First you need a Linux server (VPS) with [jq](https://jqlang.github.io/jq/) and `openssl` installed, they can be found in repositories of almost all popular Linux distributions. Then download whole `easy-xray` folder to the VPS, make the script `ex.sh` executable, and run a desired command with it. Use `./ex.sh help` to see the list of all available commands and `./ex.sh install` to start interactive prompt -that installs and configures XRay. +that installs and configures XRay. Do not use easy-xray for user `root`, use usual user and `sudo` instead. ``` chmod +x ex.sh @@ -40,6 +40,9 @@ sudo ./ex.sh install Now you have `conf` folder with server and client configs and some user configs. Server config is properly installed and XRay is running. Time to share configs or *links* with users! To generate config in the link form, use `./ex.sh link user_config_file.json`. +If your VPS have both IPv4 and IPv6 addressess, easy-xray can be configured such that IPv6 address is used for access with grpc-tls +protocol via CDN (e.g. Cloudflare). See [CDN instruction](CDN.md) for details. + #### Docker Script `ex.sh` is written without Docker in mind, but can be used with Docker. Download `easy-xray` folder (main branch) and build the diff --git a/README.ru.md b/README.ru.md index c8b0eda..2bdf900 100644 --- a/README.ru.md +++ b/README.ru.md @@ -31,7 +31,8 @@ VLESS-Reality) не шифрует уже зашифрованный https-тр установить [jq](https://jqlang.github.io/jq/) и `openssl` - их можно найти в репозиторияx практически всех популярных Linux дистрибутивов. Затем скачайте `easy-xray` (всю директорию целиком), сделайте исполняемым файл `ex.sh` и запустите нужную команду. Используйте `./ex.sh help` для получения списка всех команд и короткой справки по ним. Для установки и настройки xray используйте `./ex.sh install` - эта команда -предложит вам ввести ip-адрес вашего сервера, адрес сайта, под который ваш сервер будет маскироваться, и имена пользователей. +предложит вам ввести ip-адрес вашего сервера, адрес сайта, под который ваш сервер будет маскироваться, и имена пользователей. Не +используйте easy-xray для пользователя root, используйте обычного пользователя и sudo! ``` chmod +x ex.sh @@ -43,6 +44,10 @@ sudo ./ex.sh install будет должным образом установлен, а xray - запущен с ним. Настало время раздать конфиги пользователям! Для многих графических клиентов удобно использовать конфиги в виде ссылок, которые можно сгенерировать командой `./ex.sh link user_config_file.json`. +Если у вашего VPS сервера есть два IP-адреса (IPv4 и IPv6), easy-xray может быть сконфигурирован так, чтобы IPv6-адрес +использовался для доступа через сеть CDN от Cloudflare по протоколу grpc-tls. См. детали в [инструкции](CDN.ru.md). + + #### Docker Скрипт `ex.sh` написан прежде всего для использования в системе с systemd, однако может быть использован и в Docker. Для этого скачайте diff --git a/ex.sh b/ex.sh index 226180d..14765f5 100755 --- a/ex.sh +++ b/ex.sh @@ -137,7 +137,17 @@ conf () { check_command jq "needed for operations with configs" check_command openssl "needed for strong random numbers excluding some types of attacks" # - echo -e "Enter IPv4 or IPv6 address of your xray server, or its domain name:" + echo -e "Enter domain name to use with IPv6 and CDN (e.g. Cloudflare), +or leave blank for simple default configuration:" + read server_name4cdn + # + if [ -v $server_name4cdn ] + then + echo -e "Enter IPv4 or IPv6 address of your xray server, or its domain name:" + else + check_command sed "needed to make nginx's site to use cdn" + echo -e "Enter IPv4 address of your xray server:" + fi read address if [ -v $address ] then @@ -145,11 +155,11 @@ conf () { exit 1 fi id=$(xray uuid) # random uuid for VLESS - echo -e "Generate private and public keys? (Y/n)" + echo -e "Generate xray private and public keys? (Y/n)" read answer if [ ! -v $answer ] && [ $answer = 'n' ] then - echo -e "Enter private and public keys delimited by a space:" + echo -e "Enter xray private and public keys delimited by a space:" read answer private_key=$(echo $answer | cut -d " " -f 1) # get the first field of fields delimited by spaces public_key=$(echo $answer | cut -d " " -f 2) @@ -217,10 +227,33 @@ Better if it is quite popular and not blocked in your country: email="love@xray.com" # unsafe_mkdir conf + # + if [ -v $server_name4cdn ] + then + listen="0.0.0.0" + else + listen=$address # otherwise xray will listen also at ip6 + # grpc service name (location); letters and digits only + echo -e "Enter grpc service name or hit Enter to autogenerate:" + read service_name + if [ -v ${service_name} ] + then + service_name=$(openssl rand -base64 9 | sed 's![^[:alnum:]]!!g') + fi + # config for nginx; `!` in sed allows not to escape special characters such as dot and plus sign + cat ./template_site4cdn.conf \ + | sed "s!server_domain_name!${server_name4cdn}!" \ + | sed "s!www.youtube.com!${fake_site}!" \ + | sed "s!your_service_name!${service_name}!" \ + > ./conf/site4cdn.conf + cp ./conf/site4cdn.conf /etc/nginx/sites-enabled/ + fi + # ## Make server config ## jsonc2json template_config_server.jsonc \ | jq ".inbounds[1].settings.clients[0].id=\"${id}\" | .inbounds[2].settings.clients[0].id=\"${id}\" + | .inbounds[1].listen=\"${listen}\" | .inbounds[1].settings.clients[0].email=\"${email}\" | .inbounds[2].settings.clients[0].email=\"${email}\" | .inbounds[1].streamSettings.realitySettings.dest=\"${fake_site}:443\" @@ -230,7 +263,9 @@ Better if it is quite popular and not blocked in your country: | .inbounds[1].streamSettings.realitySettings.privateKey=\"${private_key}\" | .inbounds[2].streamSettings.realitySettings.privateKey=\"${private_key}\" | .inbounds[1].streamSettings.realitySettings.shortIds=[ \"${short_id}\" ] - | .inbounds[2].streamSettings.realitySettings.shortIds=[ \"${short_id}\" ]" \ + | .inbounds[2].streamSettings.realitySettings.shortIds=[ \"${short_id}\" ] + | .inbounds[3].settings.clients[0].id=\"${id}\" + | .inbounds[3].streamSettings.grpcSettings.serviceName=\"${service_name}\" " \ > ./conf/config_server.json # make the user (not root) the owner of the file [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_server.json @@ -271,6 +306,24 @@ Better if it is quite popular and not blocked in your country: echo -e "${red}config files are not generated${normal}" exit 1 fi + ## Make main client config_cdn ## + if [ ! -v $server_name4cdn ] + then + jsonc2json template_config_client_cdn.jsonc \ + | jq ".outbounds[0].settings.vnext[0].address=\"${server_name4cdn}\" + | .outbounds[0].settings.vnext[0].users[0].id=\"${id}\" + | .outbounds[0].streamSettings.grpcSettings.serviceName=\"${service_name}\"" \ + > ./conf/config_client_cdn.json + # make the user (not root) an owner of a file + [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_client_cdn.json + if [ -f "./conf/config_client_cdn.json" ] + then + echo -e "${green}config_cdn file is generated${normal}" + else + echo -e "${red}config_cdn file is not generated${normal}" + exit 1 + fi + fi } # the main part of `./ex.sh add` command, adds config for given users and updates server config @@ -339,6 +392,11 @@ no new config created fot it${normal}" ok1=$(cat ./conf/config_client.json | jq ".outbounds[0].settings.vnext[0].users[0].id=\"${id}\" | .outbounds[0].settings.vnext[0].users[0].email=\"${username}@example.com\" | .outbounds[0].streamSettings.realitySettings.shortId=\"${short_id}\"" > ./conf/config_client_${username}.json) # then make the user (not root) an owner of a file [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_client_${username}.json + if [ -f "./conf/config_client_cdn.json" ] + then + cat ./conf/config_client_cdn.json | jq ".outbounds[0].settings.vnext[0].users[0].id=\"${id}\"" > ./conf/config_client_${username}_cdn.json + [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_client_${username}_cdn.json + fi fi # update server config client=" @@ -348,9 +406,13 @@ no new config created fot it${normal}" \"flow\": \"xtls-rprx-vision\" } " - # update server config + grpc_client_id=" + { + \"id\": \"${id}\" + } + " cp ./conf/config_server.json ./conf/tmp_server_config.json - ok2=$(cat ./conf/tmp_server_config.json | jq ".inbounds[1].settings.clients += [${client}] | .inbounds[1].streamSettings.realitySettings.shortIds += [\"${short_id}\"]" > ./conf/config_server.json) + ok2=$(cat ./conf/tmp_server_config.json | jq ".inbounds[1].settings.clients += [${client}] | .inbounds[1].streamSettings.realitySettings.shortIds += [\"${short_id}\"] | .inbounds[3].settings.clients += [${grpc_client_id}]" > ./conf/config_server.json) # then make the user (not root) an owner of a file [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_server.json if $ok1 && $ok2 @@ -449,6 +511,16 @@ sudo ./ex.sh install${normal}" echo -e "${red}customgeo.dat not copied to ${dat_dir}${normal}" exit 1 fi + # for cert.pem + mkdir -p /etc/ssl/certs/ + # for cert.key + mkdir -p /etc/ssl/private/ + # for nginx's 'site' + mkdir -p /etc/nginx/sites-enabled/ + # + cp -b ./cert.pem /etc/ssl/certs/ + cp -b ./cert.key /etc/ssl/private/ + cp -b ./nginx.conf /etc/nginx/nginx.conf else echo -e "${red}xray not installed, something goes wrong${normal}" exit 1 @@ -547,14 +619,19 @@ then echo -e "${yellow}no config for user ${username}${normal}" else short_id=$(jq ".outbounds[0].streamSettings.realitySettings.shortId" $config) + id=$(jq ".outbounds[0].settings.vnext[0].users[0].id" $config) cp ./conf/config_server.json ./conf/tmp_server_config.json # update server config - ok1=$(cat ./conf/tmp_server_config.json | jq "del(.inbounds[1].settings.clients[] | select(.email == \"${username}@example.com\")) | del(.inbounds[1].streamSettings.realitySettings.shortIds[] | select(. == ${short_id}))" > ./conf/config_server.json) + ok1=$(cat ./conf/tmp_server_config.json | jq "del(.inbounds[1].settings.clients[] | select(.email == \"${username}@example.com\")) | del(.inbounds[1].streamSettings.realitySettings.shortIds[] | select(. == ${short_id})) | del(.inbounds[3].settings.clients[] | select(.id == ${id}))" > ./conf/config_server.json) # then make the user (not root) an owner of a file [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_server.json if [ $command = "del" ] then ok2=$(rm ./conf/config_client_${username}.json) + if [ -f "./conf/config_client_${username}_cdn.json" ] + then + rm ./conf/config_client_${username}_cdn.json + fi if $ok1 && $ok2 then rm ./conf/tmp_server_config.json @@ -602,17 +679,26 @@ then exit 1 fi check_command jq "needed for operations with configs" - id=$(strip_quotes $(jq ".outbounds[0].settings.vnext[0].users[0].id" $conf_file)) - address=$(strip_quotes $(jq ".outbounds[0].settings.vnext[0].address" $conf_file)) - if [[ $address == *":"* ]] # address contains ':', as IPv6 does + network=$(strip_quotes $(jq ".outbounds[0].streamSettings.network" $conf_file)) + if [ $network = "tcp" ] # tls-vless-reality config then - address="[${address}]" + id=$(strip_quotes $(jq ".outbounds[0].settings.vnext[0].users[0].id" $conf_file)) + address=$(strip_quotes $(jq ".outbounds[0].settings.vnext[0].address" $conf_file)) + if [[ $address == *":"* ]] # address contains ':', as IPv6 does + then + address="[${address}]" + fi + port=$(jq ".outbounds[0].settings.vnext[0].port" $conf_file) + public_key=$(strip_quotes $(jq ".outbounds[0].streamSettings.realitySettings.publicKey" $conf_file)) + server_name=$(strip_quotes $(jq ".outbounds[0].streamSettings.realitySettings.serverName" $conf_file)) + short_id=$(strip_quotes $(jq ".outbounds[0].streamSettings.realitySettings.shortId" $conf_file)) + link="vless://${id}@${address}:${port}?fragment=&security=reality&encryption=none&pbk=${public_key}&fp=chrome&type=tcp&flow=xtls-rprx-vision&sni=${server_name}&sid=${short_id}#easy-xray+%F0%9F%97%BD" + else # grpc config + id=$(strip_quotes $(jq ".outbounds[0].settings.vnext[0].users[0].id" $conf_file)) + address=$(strip_quotes $(jq ".outbounds[0].settings.vnext[0].address" $conf_file)) + service_name=$(strip_quotes $(jq ".outbounds[0].streamSettings.grpcSettings.serviceName" $conf_file)) + link="vless://${id}@${address}:443?security=tls&encryption=none&type=grpc&serviceName=${service_name}#easy-xray+%F0%9F%97%BD+CDN" fi - port=$(jq ".outbounds[0].settings.vnext[0].port" $conf_file) - public_key=$(strip_quotes $(jq ".outbounds[0].streamSettings.realitySettings.publicKey" $conf_file)) - server_name=$(strip_quotes $(jq ".outbounds[0].streamSettings.realitySettings.serverName" $conf_file)) - short_id=$(strip_quotes $(jq ".outbounds[0].streamSettings.realitySettings.shortId" $conf_file)) - link="vless://${id}@${address}:${port}?fragment=&security=reality&encryption=none&pbk=${public_key}&fp=chrome&type=tcp&flow=xtls-rprx-vision&sni=${server_name}&sid=${short_id}#easy-xray+%F0%9F%97%BD" echo -e "${yellow}don't forget to share misc/customgeo4hiddify.txt or misc/customgeo4nekoray.txt as well ${green}here is your link:${normal}" echo $link @@ -709,6 +795,12 @@ then ok1=$(cat ${to}/config_client.json | jq ".outbounds[0].settings.vnext[0].users[0].id=\"${id}\" | .outbounds[0].settings.vnext[0].users[0].email=\"${uname_from_email}@example.com\" | .outbounds[0].streamSettings.realitySettings.shortId=\"${short_id}\"" > ${to}/config_client_${uname_from_email}.json) # then make the user (not root) an owner of a file [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ${to}/config_client_${uname_from_email}.json + # + if [ -f "./conf/config_client_cdn.json" ] + then + cat ./conf/config_client_cdn.json | jq ".outbounds[0].settings.vnext[0].users[0].id=\"${id}\"" > ./conf/config_client_${uname_from_email}_cdn.json + [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ./conf/config_client_${username}_cdn.json + fi # update server config client=" { @@ -717,8 +809,13 @@ then \"flow\": \"xtls-rprx-vision\" } " + grpc_client_id=" + { + \"id\": \"${id}\" + } + " cp ${to}/config_server.json ${to}/tmp_server_config.json - ok2=$(cat ${to}/tmp_server_config.json | jq ".inbounds[1].settings.clients += [${client}] | .inbounds[1].streamSettings.realitySettings.shortIds += [\"${short_id}\"]" > ${to}/config_server.json) + ok2=$(cat ${to}/tmp_server_config.json | jq ".inbounds[1].settings.clients += [${client}] | .inbounds[1].streamSettings.realitySettings.shortIds += [\"${short_id}\"] | .inbounds[3].settings.clients += [${grpc_client_id}]" > ${to}/config_server.json) # then make the user (not root) an owner of a file [[ $SUDO_USER ]] && chown "$SUDO_USER:$SUDO_USER" ${to}/config_server.json if $ok1 && $ok2 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..5ca16d5 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,85 @@ +# For more information on configuration, see: +# * Official English Documentation: http://nginx.org/en/docs/ +# * Official Russian Documentation: http://nginx.org/ru/docs/ + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. +include /usr/share/nginx/modules/*.conf; + +events { + worker_connections 1024; +} + +http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 4096; + + include /etc/nginx/mime.types; + include /etc/nginx/sites-enabled/*; + default_type application/octet-stream; + + # Load modular configuration files from the /etc/nginx/conf.d directory. + # See http://nginx.org/en/docs/ngx_core_module.html#include + # for more information. + include /etc/nginx/conf.d/*.conf; + + #server { + # listen 80; + # listen [::]:80; + # server_name _; + # root /usr/share/nginx/html; + + # # Load configuration files for the default server block. + # include /etc/nginx/default.d/*.conf; + + # error_page 404 /404.html; + # location = /404.html { + # } + + # error_page 500 502 503 504 /50x.html; + # location = /50x.html { + # } + #} + +# Settings for a TLS enabled server. +# +# server { +# listen 443 ssl http2; +# listen [::]:443 ssl http2; +# server_name _; +# root /usr/share/nginx/html; +# +# ssl_certificate "/etc/pki/nginx/server.crt"; +# ssl_certificate_key "/etc/pki/nginx/private/server.key"; +# ssl_session_cache shared:SSL:1m; +# ssl_session_timeout 10m; +# ssl_ciphers PROFILE=SYSTEM; +# ssl_prefer_server_ciphers on; +# +# # Load configuration files for the default server block. +# include /etc/nginx/default.d/*.conf; +# +# error_page 404 /404.html; +# location = /40x.html { +# } +# +# error_page 500 502 503 504 /50x.html; +# location = /50x.html { +# } +# } + +} + diff --git a/template_config_client_cdn.jsonc b/template_config_client_cdn.jsonc new file mode 100644 index 0000000..f85067a --- /dev/null +++ b/template_config_client_cdn.jsonc @@ -0,0 +1,229 @@ +// This config is based on +// https://github.com/XTLS/Xray-examples/blob/main/VLESS-TCP-XTLS-Vision-REALITY/REALITY.ENG.md +{ + "log": { + "access": "none", + "error": "", + "loglevel": "warning", + "dnsLog": false + }, + // Turns on traffic statistics, see https://xtls.github.io/en/config/stats.html#statsobject + // and https://xtls.github.io/en/config/policy.html#policyobject + // and special "api" tag below + "stats": { + }, + "policy": { + "levels": { + // default level + "0": { + "statsUserUplink": true, + "statsUserDownlink": true + } + }, + "system": { + "statsOutboundUplink": true, + "statsOutboundDownlink": true + } + }, + // enables API interface https://xtls.github.io/en/config/api.html#apiobject + "api": { + "tag": "api", + "services": [ "StatsService" ] + }, + // client-side inbound configuration + "inbounds": [ + // gRPC API inbound, used to get statistics + { + "listen": "127.0.0.1", + "port": 8080, + "protocol": "dokodemo-door", + "settings": { + "address": "127.0.0.1" + }, + "tag": "api" + }, + // socks proxy + { + "tag": "socks", + "port": 800, + "listen": "127.0.0.1", + "protocol": "socks", + // used to make transparent proxies, see https://xtls.github.io/en/config/inbound.html#sniffingobject + "sniffing": { + "enabled": true, + "destOverride": [ + "http", + "tls" + ], + "routeOnly": true + }, + // settings of inbound "protocol" (see above) + "settings": { + "auth": "noauth", + "udp": true + } + }, + // http/https proxy + { + "tag": "http", + "port": 801, + "listen": "127.0.0.1", + "protocol": "http", + // used to make transparent proxies, see https://xtls.github.io/en/config/inbound.html#sniffingobject + "sniffing": { + "enabled": true, + "destOverride": [ + "http", + "tls" + ], + "routeOnly": true + }, + // settings of inbound "protocol" (see above) + "settings": { + "auth": "noauth", + "udp": true + } + } + ], + // client-side outbound configuration + "outbounds": [ + // fallback, see `routing` section + { + "tag": "proxy", + "protocol": "vless", + "settings": { + "vnext": [ + { + "address": "server_domain_name", + "port": 443, + "users": [ + { + // should match server side + "id": "client_id", + "encryption": "none" + } + ] + } + ] + }, + "streamSettings": { + "network": "grpc", + "security": "tls", + "grpcSettings": { + // should match server side + "serviceName": "your_service_name", + // for Cloudflare CDN, see this example https://github.com/XTLS/Xray-examples/blob/main/VLESS-GRPC/client.json + // and https://xtls.github.io/en/config/transports/grpc.html#grpcobject + "idle_timeout": 60, + "permit_without_stream": true, + "initial_windows_size": 35536 + }, + "tlsSettings": { + "alpn": ["h2"], + "fingerprint": "chrome" + } + } + }, + { + // this outbound is to guide traffic to local sites not through the server + // but directly from the client; `tag` is just an outbound label + "tag": "direct", + "protocol": "freedom", + "settings": {} + }, + // for that should be blocked + { + "protocol": "blackhole", + "tag": "block" + } + ], + // Forward each inbound connections to corresponding `outboundTag`. If no rules match, + // the traffic is sent out by the first outbound in `outbounds` section. + "routing": { + "domainStrategy": "AsIs", + "rules": [ + { + "type": "field", + "inboundTag": [ + "api" + ], + "outboundTag": "api" + }, + // guide udp traffic to `direct` outbound + { + "type": "field", + "network": "udp", + "outboundTag": "direct" + }, + // block localhost connections through xray + { + "type": "field", + "ip": [ + "geoip:private" + ], + "outboundTag": "block" + }, + // guide bittorent traffic to `direct` outbound + { + "type": "field", + "protocol": [ "bittorrent" ], + "outboundTag": "direct" + }, + // traffic to popular ports of torrent trackers + // and to popular ports of torrent clients + { + "type": "field", + "port": "6969,6881-6889", + "outboundTag": "direct" + }, + // traffic from popular ports of torrent clients + { + "type": "field", + "sourcePort": "6881-6889", + "outboundTag": "direct" + }, + // exceptions for some *.ru sites that shouldn't be blocked or accessed directly + { + "type": "field", + "domain": [ + "ext:customgeo.dat:coherence-extra-exceptions" + ], + "outboundTag": "proxy" + }, + // guide domestic sites traffic to `direct` outbound + { + "type": "field", + "domain": [ + "geosite:cn", + "domain:cn", + // punycode for national Chinese top-level domains .中国, .中國, .公司, .网络 + "domain:xn--fiqs8s", + "domain:xn--fiqz9s", + "domain:xn--55qx5d", + "domain:xn--io0a7i", + "domain:ru", + // punycode for cyrillic Russian top-level domain .рф + "domain:xn--p1ai", + "domain:by", + // punycode for national Belorussian top-level domain .бел + "domain:xn--90ais", + "domain:ir", + // extra domains that are used by domestic sites, see https://github.com/EvgenyNerush/coherence-grabber/tree/main + "ext:customgeo.dat:coherence-extra", + "ext:customgeo.dat:coherence-extra-plus" + ], + "outboundTag": "direct" + }, + { + "type": "field", + "ip": [ + "geoip:cn", + "geoip:ru", + "geoip:by", + "geoip:ir" + ], + "outboundTag": "direct" + } + ] + } +} diff --git a/template_config_server.jsonc b/template_config_server.jsonc index 97279c1..387b385 100644 --- a/template_config_server.jsonc +++ b/template_config_server.jsonc @@ -246,6 +246,30 @@ ], "routeOnly": true } + }, + // this inbound can be used with Nginx configured to listen grpc on IPv6 address; + // vless can't give a reasonable fallback when used with grpc, but nginx can; + // see 'CDN' section in Readme for details + { + "tag": "grpc", + "listen": "127.0.0.1", + "port": 50051, + "protocol": "vless", + "settings": { + "clients": [ + { + "id": "client_id" + } + ], + "decryption": "none" + }, + "streamSettings": { + "network": "grpc", + "grpcSettings": { + // nginx config should contain `location /your_service_name` + "serviceName": "your_service_name" + } + } } ], // server-side outbound configuration diff --git a/template_site4cdn.conf b/template_site4cdn.conf new file mode 100644 index 0000000..e00f5c8 --- /dev/null +++ b/template_site4cdn.conf @@ -0,0 +1,35 @@ +server { + # listen ip6 addresses + listen [::]:443 ssl http2 so_keepalive=on; + + # your server domain name in CDN, for instance myserver.example.com + server_name server_domain_name; + + # fallback: proxy_pass redirects request to another server + location / { + proxy_pass https://www.youtube.com; + } + + + # path to certificates (self-signed or from Cloudflare) + ssl_certificate /etc/ssl/certs/cert.pem; + ssl_certificate_key /etc/ssl/private/cert.key; + + + client_header_timeout 52w; + keepalive_timeout 52w; + # secret location; should match that in the xray server config + location /your_service_name { + if ($content_type !~ "application/grpc") { + # redirect to the root location + return 302 /; + } + client_max_body_size 0; + client_body_buffer_size 512k; + grpc_set_header X-Real-IP $remote_addr; + client_body_timeout 52w; + grpc_read_timeout 52w; + # connect to xray on localhost + grpc_pass grpc://127.0.0.1:50051; + } +}