reality
continuous-integration/drone/push Build is passing Details

This commit is contained in:
quackerd 2023-08-21 09:05:44 -04:00
parent 58b769abe7
commit 050cecc84f
17 changed files with 176 additions and 472 deletions

7
.env
View File

@ -1,4 +1,5 @@
PORT=443
FQDN=example.com
USERS=exampleuser1,exampleuser2@xtls-rprx-direct
LOGDIR=./logs
TARGET_URL=example.com
TARGET_PORT=443
USERS=exampleuser1,exampleuser2
LOG_LEVEL=warn

View File

@ -1,34 +1,24 @@
FROM alpine:latest
ENV VER_XRAY 1.7.5
ENV VER_SO 2.5.20
ENV VER_NG 1.7.38
ENV VER_XRAY 1.8.3
# install packages
RUN set -xe && apk add --no-cache unzip wget nginx certbot openssl python3 py3-jinja2 supervisor apache2-utils bash
RUN set -xe && apk add --no-cache unzip wget openssl python3 py3-jinja2 supervisor apache2-utils bash
# download packages
RUN set -xe && \
mkdir -p /downloads /downloads/others /downloads/android && \
wget -P /downloads/others https://github.com/XTLS/Xray-core/releases/download/v$VER_XRAY/Xray-windows-64.zip && \
wget -P /downloads/others https://github.com/XTLS/Xray-core/releases/download/v$VER_XRAY/Xray-linux-64.zip && \
wget -P /downloads/others https://github.com/FelisCatus/SwitchyOmega/releases/download/v$VER_SO/SwitchyOmega_Chromium.crx && \
wget -P /downloads/android https://github.com/2dust/v2rayNG/releases/download/$VER_NG/v2rayNG_"$VER_NG"_arm64-v8a.apk && \
wget -P /downloads/others https://github.com/XTLS/Xray-core/releases/download/v$VER_XRAY/Xray-macos-64.zip && \
wget -P /downloads/others https://github.com/XTLS/Xray-core/releases/download/v$VER_XRAY/Xray-macos-arm64-v8a.zip
mkdir -p /downloads && \
wget -P /downloads https://github.com/XTLS/Xray-core/releases/download/v$VER_XRAY/Xray-linux-64.zip && \
unzip /downloads/Xray-linux-64.zip -d /opt/xray && \
rm -rf /downloads
COPY ./opt /opt/
# xray
RUN set -xe && unzip /downloads/others/Xray-linux-64.zip -d /opt/xray
# nginx
RUN set -xe && addgroup www && \
adduser -H -D -S -s /bin/false www -G www && \
chown -R www:www /opt/nginx
# RUN set -xe && addgroup www && \
# adduser -H -D -S -s /bin/false www -G www && \
# chown -R www:www /opt/nginx
# remove packages
RUN set -xe && apk del unzip wget
EXPOSE 80
VOLUME /etc/letsencrypt
CMD ["sh", "/opt/init.sh"]

View File

@ -2,23 +2,20 @@ networks:
d2ray_br:
external: false
volumes:
d2ray_certs:
services:
d2ray:
image: quackerd/d2ray
container_name: d2ray
ports:
- ${PORT}:${PORT}
- 80:80
environment:
- PORT=${PORT}
- FQDN=${FQDN}
- TARGET_URL=${TARGET_URL}
- TARGET_PORT=${TARGET_PORT}
- USERS=${USERS}
- LOG_LEVEL=${LOG_LEVEL}
restart: unless-stopped
networks:
- d2ray_br
volumes:
- d2ray_certs:/etc/letsencrypt
- ${LOGDIR}:/etc/d2ray
- ./config:/etc/d2ray

View File

@ -1 +0,0 @@
0 0 * * * certbot renew --post-hook "supervisorctl xray restart"

View File

@ -1,10 +1,68 @@
import os
import getopt
import sys
import subprocess
import jinja2
import random
import string
import pathlib
CONFIG_DIR = pathlib.Path("/etc/d2ray")
PRIVKEY = CONFIG_DIR.joinpath("certs/private_key")
PUBKEY = CONFIG_DIR.joinpath("certs/public_key")
LOG_DIR = CONFIG_DIR.joinpath("logs")
XRAY_BIN = pathlib.Path("/opt/xray/xray")
class d2args:
port : int
target_port : int
target_url : str
log_level : str
users : list[str]
def __init__(self) -> None:
self.port = 443
self.target_port = 443
self.target_url = "localhost"
self.log_level = "warn"
self.users = [''.join(random.choices(string.ascii_uppercase + string.digits, k=24))]
def from_env(self) -> None:
env = os.getenv("PORT")
if env != None:
self.port = int(env)
env = os.getenv("TARGET_PORT")
if env != None:
self.target_port = int(env)
env = os.getenv("TARGET_URL")
if env != None:
self.target_url = env
env = os.getenv("LOG_LEVEL")
if env != None:
self.log_level = env
env = os.getenv("USERS")
if env != None:
self.users = env.split(",")
def __str__(self):
ret = (f"Port: {self.port}\n"
f"Target Port: {self.target_port}\n"
f"Target URL: {self.target_url}\n"
f"Log Level: {self.log_level}\n"
f"Users: {', '.join(self.users)}"
)
return ret
def get_users_json(self) -> str:
ret : str= ""
for i in range(len(users)):
if (i > 0):
ret = ret + ","
ret = ret + "{\"id\": \"" + users[i][0] + "\",\"flow\": \"" + users[i][1] + "\"}"
return ret
def process_directory(path : str, vars : dict[str, str], delete_template : bool = True) -> None:
for f in os.listdir(path):
@ -20,103 +78,76 @@ def process_directory(path : str, vars : dict[str, str], delete_template : bool
if delete_template:
subprocess.check_call(f"rm {full_path}", shell=True)
def build_target_fqdns(url : str) -> str:
prefix = "www."
fqdns = [url, f"{prefix}{url}"]
if url.startswith(prefix) and len(url) > len(prefix):
fqdns.append(url[len(prefix):])
return ', '.join(['"' + item + '"' for item in fqdns])
def parse_user_flow(s : str) -> list[tuple[str,str]]:
users = []
userconfs : list[str] = s.split(",")
for userconf in userconfs:
if len(userconf) == 0:
continue
ele = userconf.split("@")
username = ele[0]
if (len(ele) > 1):
flow = ele[1]
else:
flow = "xtls-rprx-vision"
users.append((username, flow))
return users
def build_users_json(users: list[str]) -> str:
return ', '.join(["{\"id\": \"" + item + "\", \"flow\": \"xtls-rprx-vision\"}" for item in users])
def build_jinja_dict(args : d2args, skey : str) -> dict[str, str]:
jinja_dict : dict[str,str] = dict()
jinja_dict["PORT"] = str(args.port)
def build_users_json(users: list[tuple[str, str]]) -> str:
ret : str= ""
for i in range(len(users)):
if (i > 0):
ret = ret + ","
ret = ret + "{\"id\": \"" + users[i][0] + "\",\"flow\": \"" + users[i][1] + "\"}"
return ret
jinja_dict["TARGET_URL"] = args.target_url
jinja_dict["TARGET_PORT"] = str(args.target_port)
jinja_dict["TARGET_FQDNS"] = build_target_fqdns(args.target_url)
NGINX_LOCATION_TEMPLATE = '''
location /{{ USER }} {
root /opt/nginx;
autoindex on;
}
'''
jinja_dict["LOG_DIR"] = str(LOG_DIR)
jinja_dict["LOG_LEVEL"] = args.log_level
def build_nginx_locations(users: list[tuple[str, str]]) -> str:
ret = ""
for user in users:
ret += jinja2.Environment().from_string(NGINX_LOCATION_TEMPLATE).render(USER = user[0]) + "\n"
return ret
jinja_dict["USERS"] = build_users_json(args.users)
jinja_dict["PRIVATE_KEY"] = skey
return jinja_dict
def parse_xray_x25519_output(stdout : str) -> tuple[str, str]:
skey = None
pkey = None
lines = stdout.split("\n")
if len(lines) < 2:
raise Exception(f"Unknown Xray output format:\n\"{stdout}\"\n")
priv_key_hdr = "Private key: "
pub_key_hdr = "Public key: "
for line in lines:
if line.startswith(priv_key_hdr):
skey = line[len(priv_key_hdr):]
elif line.startswith(pub_key_hdr):
pkey = line[len(pub_key_hdr):]
if (skey == None) or (pkey == None):
raise Exception(f"Unable to extract private or public key from Xray output:\n\"{stdout}\"\n")
return (skey.strip(), pkey.strip())
try:
opts, _ = getopt.getopt(sys.argv[1:], "u:p:f:")
except getopt.GetoptError as err:
# print help information and exit:
print(err, flush=True) # will print something like "option -a not recognized"
exit(1)
def main():
args = d2args()
args.from_env()
port : int = 443
users : list[str] = [''.join(random.choices(string.ascii_uppercase + string.digits, k=24))]
fqdn : str = "example.com"
print("====== init.py ======", flush=True)
print(f"Checking server private key...", flush=True)
if not PRIVKEY.exists():
print(f"Server private key not found at {PRIVKEY}. Generating...")
skey, _ = parse_xray_x25519_output(subprocess.check_output(f"{XRAY_BIN} x25519", shell = True).decode())
with open(PRIVKEY, "w") as f:
f.write(skey)
with open(PRIVKEY, "r") as f:
skey = f.read().strip()
for o, a in opts:
if o == "-u":
users = parse_user_flow(a)
elif o == "-p":
port = int(a)
elif o == "-f":
fqdn = a
else:
print(f"Unknown option {o}, ignoring...", flush=True)
exit(1)
print(f"Deriving public key...", flush=True)
_, pkey = parse_xray_x25519_output(subprocess.check_output(f"{XRAY_BIN} x25519 -i {skey}", shell = True).decode())
print("====== init.py ======", flush=True)
print("Configuration:\n" + \
f" port = {port}\n" + \
f" fqdn = {fqdn}\n" + \
f" users = {str(users)}", flush=True)
with open(PUBKEY, "w") as f:
f.write(pkey)
print(f"Checking certs for {fqdn}...", flush=True)
if (os.path.exists(f"/etc/letsencrypt/live/{fqdn}")):
print("Found existing certs, trying to renew...", flush=True)
subprocess.check_call(f"certbot renew", shell=True)
else:
print("Unable to locate certs, generating...", flush=True)
subprocess.check_call(f"certbot certonly -n --standalone -m dummy@dummy.com --agree-tos --no-eff-email -d {fqdn}", shell=True)
print(f"\nConfigurations:\n{str(args)}\nPublic key: {pkey}\n", flush=True)
jinja_dict : dict[str,str] = dict()
jinja_dict["XRAY_USERS"] = build_users_json(users)
jinja_dict["PORT"] = str(port)
jinja_dict["FQDN"] = str(fqdn)
jinja_dict["NGINX_LOCATIONS"] = build_nginx_locations(users)
template = build_jinja_dict(args, skey)
print(f"Processing Xray config files...", flush=True)
process_directory("/opt/xray", jinja_dict)
print(f"Processing config files...", flush=True)
process_directory("/opt/xray", template)
print(f"Processing Nginx config files...", flush=True)
process_directory("/opt/nginx", jinja_dict)
for u in users:
print(f"Preparing directory for user {u[0]}...", flush=True)
user_dir = f"/opt/nginx/{u[0]}"
subprocess.check_call(f"mkdir -p {user_dir}", shell=True)
subprocess.check_call(f"cp -r /opt/user/* {user_dir}/", shell=True)
jinja_dict["USER"] = u[0]
jinja_dict["FLOW"] = u[1]
process_directory(user_dir, jinja_dict)
subprocess.check_call(f"ln -sf /downloads/others {user_dir}/others/downloads", shell=True)
subprocess.check_call(f"ln -sf /downloads/android {user_dir}/android/downloads", shell=True)
exit(0)
main()

View File

@ -1,13 +1,13 @@
#!/bin/sh
# create log directories
mkdir -p /etc/d2ray/logs/cron
# create directories
mkdir -p /etc/d2ray/logs/xray
mkdir -p /etc/d2ray/logs/nginx
mkdir -p /etc/d2ray/logs/supervisor
mkdir -p /etc/d2ray/logs/supervisord
mkdir -p /etc/d2ray/certs
python3 /opt/init.py -p $PORT -u $USERS -f $FQDN
python3 /opt/init.py
retval=$?
if [ $retval -ne 0 ]; then
exit $retval
fi
exec /usr/bin/supervisord -c /opt/supervisord.conf

View File

@ -1,45 +0,0 @@
user www www;
worker_processes auto;
daemon off;
pid /tmp/nginx.pid;
worker_rlimit_nofile 8192;
events {
worker_connections 4096; ## Default: 1024
}
http {
geo $external {
default 1;
127.0.0.1/32 0;
}
##
# WebSocket proxying
##
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
charset utf-8;
access_log /etc/d2ray/logs/nginx/access.log;
error_log /etc/d2ray/logs/nginx/error.log;
include /etc/nginx/mime.types;
server {
listen 80 default_server;
server_name _;
if ($external) {
return 301 https://$host:{{ PORT }}$request_uri;
}
location / {
root /opt/nginx/webroot;
index index.html;
}
{{ NGINX_LOCATIONS }}
}
}

View File

@ -1,49 +0,0 @@
<header>Welcome to a webpage written entirely in Vanilla <strike>JS</strike> HTML.</header>
<!-- Heading -->
<h1>THE ROCKET BLOG FROM THE FUTURE 🚀</h1>
<p>My thoughts and ideas on rockets and the science behind them. <a href="https://about.me/vaibhav_khulbe" target="_blank">Learn more</a>.</p>
<br>
<!-- Article -->
<img src="https://images.pexels.com/photos/796206/pexels-photo-796206.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Space shuttle"/>
<kbd>FEATURED</kbd>
<h4><a href="#">Lorem ipsum dolor sit, amet consectetur adipisicing elit</a></h4>
<p>Optio, beatae! Aut quis id voluptate ullam repellendus. Et sit, ipsa, non consequuntur magnam quaerat temporibus at officiis ab, expedita molestiae liber...</p>
<label for="Download?">Download: </label>
<input type="text" id="text_download" placeholder="">
<button onclick="redirect()"><b>DOWNLOAD</b></button>
<script>
function redirect()
{
var url=document.getElementById("text_download").value
if (url.length > 0) {
window.location.href = "./" + url
}
}
</script>
<br><br>
<!-- Article break -->
<details>
<summary>What was the secret behind this mission?</summary>
<img src="https://media.giphy.com/media/NdKVEei95yvIY/giphy.gif" alt="Secret GIF">
<p>Okay, go watch The Office.</p>
</details>
<br><br>
<!-- Article -->
<kbd>SPACE</kbd> <kbd>LUNA</kbd> <kbd>PRESSURE</kbd>
<h4><a href="#">Ad aspernatur, nemo unde neque laboriosam sequi?</a></h4>
<p>Nullam in lorem nec mi euismod pretium in eu erat. Nunc lacus tellus, sodales molestie sem id, tempor elementum turpis. Auris dapibus mi vitae libero luctus iaculis non non turpis. Mauris molestie ultrices...</p>
<a href="#"><button><b>READ MORE</b></button></a>
<br><br>
<!-- Article break -->
<blockquote>
“The Earth is the cradle of humanity, but mankind cannot stay in the cradle forever.” <i> - Konstantin Tsiolkovsky</i>
</blockquote>
<br><br>
<!-- Footer -->
<center>( ̄︶ ̄)↗</center> 

View File

@ -1,5 +1,7 @@
[unix_http_server]
file=/var/run/supervisord.sock
username = dummy1234
password = dummy1234
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
@ -7,27 +9,29 @@ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisord]
nodaemon=true
loglevel=warn
logfile=/etc/d2ray/logs/supervisor/supervisord.log
logfile=/etc/d2ray/logs/supervisord/supervisord.log
logfile_maxbytes=0
[supervisorctl]
serverurl=unix:///var/run/supervisord.sock
username = dummy1234
password = dummy1234
[program:nginx]
command=nginx -c /opt/nginx/nginx.conf
autostart=true
autorestart=false
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
# [program:nginx]
# command=nginx -c /opt/nginx/nginx.conf
# autostart=true
# autorestart=false
# stdout_logfile=/dev/fd/1
# stdout_logfile_maxbytes=0
# redirect_stderr=true
[program:cron]
command=crond -f -L /etc/d2ray/logs/cron/crond.log -c /opt/crontabs
autostart=true
autorestart=false
stdout_logfile=/etc/d2ray/logs/cron/crond.log
stdout_logfile_maxbytes=0
redirect_stderr=true
# [program:cron]
# command=crond -f -L /etc/d2ray/logs/cron/crond.log -c /opt/crontabs
# autostart=true
# autorestart=false
# stdout_logfile=/etc/d2ray/logs/cron/crond.log
# stdout_logfile_maxbytes=0
# redirect_stderr=true
[program:xray]
command=/opt/xray/xray -c /opt/xray/d2ray.json

View File

@ -1,21 +0,0 @@
1. 下载当前网页的apk文件并且安装到手机
2. 打开V2rayNG app
3. 右上角箭头->新加配置文件->手动输入[VLess]
别名(remarks):随便起
地址(address){{ FQDN }}
端口(port){{ PORT }}
用户ID(id){{ USER }}
流控(flow){{ FLOW }}
传输协议(network)tcp
伪装类型(type)none
传输层安全(tls)tls
uTLS: firefox
alpn: h2,http/1.1
跳过证书验证(allowInsecure)false
以上没有提到的选项统统默认值
4. 右上角对勾保存
5. 选择刚创建的链接,点击右下角"V"开启或关闭VPN.

View File

@ -1,16 +0,0 @@
1. 去AppStore下载shadowrocket软件需要非中国区
2. 打开shadowrocket app
3. 右上角+号
类型VLESS
地址:{{ FQDN }}
端口:{{ PORT }}
UUID{{ USER }}
TLS 开启
XTLS Direct: 开启
快速打开: 开启
以上没有提到的选项统统默认值
4. 右上角完成保存
5. 返回主页面选择刚创建的链接然后开启链接即开启vpn

View File

@ -1,119 +0,0 @@
{
"dns": {
"servers": [
"223.5.5.5",
"114.114.114.114",
{
"address": "8.8.8.8",
"port": 53,
"domains": [
"geosite:geolocation-!cn"
]
},
{
"address": "1.1.1.1",
"port": 53,
"domains": [
"geosite:geolocation-!cn"
]
}
]
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"outboundTag": "direct",
"ip": [
"223.5.5.5",
"114.114.114.114"
]
},
{
"type": "field",
"outboundTag": "proxy",
"ip": [
"8.8.8.8",
"1.1.1.1"
]
},
{
"type": "field",
"outboundTag": "direct",
"ip": [
"geoip:cn",
"geoip:private"
]
},
{
"type": "field",
"outboundTag": "direct",
"domain": ["geosite:cn"]
},
{
"type": "field",
"outboundTag": "proxy",
"network": "udp,tcp"
}
]
},
"inbounds": [
{
"port": 1080,
"listen": "127.0.0.1",
"protocol": "socks",
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
},
"settings": {
"auth": "noauth",
"udp": false
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "{{ FQDN }}",
"port": {{ PORT }},
"users": [
{
"id": "{{ USER }}",
"encryption": "none",
"flow": "{{ FLOW }}"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "{{ FQDN }}",
"allowInsecure": false,
"fingerprint": "random",
"alpn": ["h2","http/1.1"]
}
}
},
{
"protocol": "freedom",
"settings": {},
"tag": "direct"
},
{
"protocol": "blackhole",
"settings": {},
"tag": "block"
}
]
}

View File

@ -1,4 +0,0 @@
#!/bin/sh
cd $(dirname $0)
chmod +x xray
./xray -c config.json

View File

@ -1,3 +0,0 @@
@echo off
.\xray -c config.json

View File

@ -1,55 +0,0 @@
分两步:
1. 配置VPN程序此程序负责与VPN服务器交流
2. 配置浏览器让浏览器把所有网站请求交给VPN程序处理
**** 1. 配置 VPN程序 ****
1. 请在downloads目录里下载与系统相适应的程序和配置文件
- Windows 64 bits (Intel CPU): Xray-windows-64.zip + config.json + windows.bat
- MacOS 64 bits (Intel CPU): Xray-macos-64.zip + config.json + macos.sh
- MacOS ARM (M Series CPU): Xray-macos-arm64-v8a.zip + config.json + macos.sh
2. 如果仅需要浏览器联网,请下载 Google Chrome 浏览器和 downloads 目录里的 SwitchyOmega_Chromium.crx
如果需要全局代理(例如除浏览器外其他应用程序),请先完成 Google Chrome 的配置然后从Google下载 Proxifier 后请看 "配置全局代理" 部分
3. 解压缩下载的zip文件将 config.json 和 windows.bat 或 macos.sh 拷贝到解压缩后的目录下
4. 双击 windows.bat 或 macos.sh 即可运行VPN程序MacOS系统有可能需要右键 macos.sh => 用应用程序打开(Open With) => 其他(Other) => 在新打开的窗口下方选择框里选"所有应用程序"(All Applications) => 找到 终端"Terminal" 并勾选 永久以程序打开(Always Open With) => 确定
**** 2. 配置 Google Chrome ****
1. 安装下载的SwitchyOmega插件:
- 打开 Google Chrome
- 打开网址 "chrome://extensions"
- 打开右上角 开发者模式(Developer mode)
- 在新出现的工具栏选择第一项 "Load unpacked"(加载插件)
- 在新出现的对话框选择下载的 SwitchyOmega_Chromium.crx
2. 配置SwitchyOmega插件
- 打开SwitchyOmega插件配置菜单(浏览器右上角点击SwitchyOmega图标然后点最后一项配置没有的话在插件下拉里面找找到可以Pin到浏览器上).
- 在左边菜单新建一个profile, 类型选择第一个"Proxy代理 Profile",名字取"VPN", 点击 “创建”
- Protocol(协议)选择"SOCKS5", 服务器(Server)填写127.0.0.1, 端口(Port)写1080点击左边菜单"应用" (Apply changes)
**** 3. 配置全局代理 (Optional) ****
!! 此部分仅适用需要全局代理的用户在配置全局代理前请先配置好Google Chrome !!
1. 在配置好的 Google Chrome 上访问 https://www.proxifier.com/,下载并安装 ("Download Proxifier 31-day Free Trial")
2. 打开Proxifier。点击左上角菜单 "Profile" -> "Proxy Servers" -> "Add"
- Address 填写 127.0.0.1
- Port 填写 1080
- Protocol 选 SOCKS Version 5
然后点OK
3. 窗口 "Do you want Proxifier to use this proxy by default?" -> Yes然后点OK
4. 重新启动 Proxifier 即可启动全局代理,第一次运行 Proxifier 会询问是否将 Xray.exe 加入 Exclusion选Yes即可。
**** 4. 注意事项 ****
1. 启动VPN时请启动VPN程序并且 在SwitchyOmega插件菜单选择 "VPN" 或者打开Proxifier
2. 关闭VPN时请 当不需要VPN时只需在SwitchyOmega插件选择"Direct"直连。
3. 不需要用VPN时请关闭VPN降低被ban的概率。
4. 请每过一段时间(~ 1-3 months从本站下载最新的VPN软件并重新配置VPN软件的bug修复和新的协议会降低被ban的概率。

View File

@ -1,3 +0,0 @@
Android用户请看 android 文件夹
iOS用户请看 ios 文件夹
其他系统用户请下载 d2ray.zip

View File

@ -1,8 +1,8 @@
{
"log": {
"loglevel": "warn",
"access": "/etc/d2ray/logs/xray/access.log",
"error": "/etc/d2ray/logs/xray/error.log"
"loglevel": "{{LOG_LEVEL}}",
"access": "{{LOG_DIR}}/access.log",
"error": "{{LOG_DIR}}/error.log"
},
"inbounds": [
{
@ -10,25 +10,23 @@
"protocol": "vless",
"settings": {
"clients": [
{{ XRAY_USERS }}
{{ USERS }}
],
"decryption": "none",
"fallbacks": [
{
"dest": "localhost:80"
}
]
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"alpn": ["http/1.1", "h2"],
"certificates": [
{
"certificateFile": "/etc/letsencrypt/live/{{ FQDN }}/fullchain.pem",
"keyFile": "/etc/letsencrypt/live/{{ FQDN }}/privkey.pem"
}
"security": "reality",
"realitySettings": {
"show": false,
"dest": "{{ TARGET_URL }}:{{ TARGET_PORT }}",
"xver": 0,
"serverNames": [
{{ TARGET_FQDNS }}
],
"privateKey": "{{ PRIVATE_KEY }}",
"shortIds": [
""
]
}
},
@ -43,8 +41,7 @@
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
"protocol": "freedom"
}
]
}