new format

This commit is contained in:
quackerd 2021-01-16 17:53:44 -05:00
parent 542334d914
commit 2e94156ffe
11 changed files with 243 additions and 187 deletions

View File

@ -1,4 +1,4 @@
MIT License Copyright (c) <year> <copyright holders> MIT License Copyright (c) 2021 quackerd
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -68,7 +68,7 @@
"inbounds": [ "inbounds": [
{ {
"port": 1080, "port": {{ port }},
"listen": "127.0.0.1", "listen": "127.0.0.1",
"protocol": "socks", "protocol": "socks",
"sniffing": { "sniffing": {
@ -88,12 +88,12 @@
"settings": { "settings": {
"vnext": [ "vnext": [
{ {
"address": "{{ server_name }}", "address": "{{ subdomain }}.{{ domain }}",
"port": 443, "port": 443,
"users": [ "users": [
{ {
"id": "{{ uuid }}", "id": "{{ id }}",
"alterId": 64, "alterId": {{ alterid }},
"security": "none" "security": "none"
} }
] ]
@ -104,7 +104,7 @@
"network": "ws", "network": "ws",
"security": "tls", "security": "tls",
"wsSettings": { "wsSettings": {
"path": "/{{ path }}" "path": "{{ path }}"
} }
} }
}, },
@ -119,4 +119,4 @@
"tag": "block" "tag": "block"
} }
] ]
} }

4
client_obj.in Normal file
View File

@ -0,0 +1,4 @@
{
"id": "{{ id }}",
"alterId": {{ alterid }}
}

32
config.yml Normal file
View File

@ -0,0 +1,32 @@
server:
# the domain name
domain: domain.tld
# the subdomain name. leave empty for naked domain.
subdomain: example
# email. your email for the registered SSL cert. leave empty for a dummy email.
email:
# the user/group to run the docker-compose stack.
# defaults to the current user
# or you can manually set, e.g.:
# uid: 1000
# gid: 1000
uid:
gid:
# the path for websocket. Do NOT include the leading slash. default: auto-generated.
path:
# whether or not to enable watchtower to auto update docker containers. default: false
watchtower: False
clients:
- name: example_user1
# default id: auto-generated uuid
id:
# default alterid: 64
alterid:
# port for local socks5. default: 1080
port:
- name: example_user2
# or you can set them manually
id: 5058b990-6be5-438d-9c02-4b06b5d927d0
alterid: 32
port: 6666

176
configure.py Normal file
View File

@ -0,0 +1,176 @@
import getopt
import sys
import uuid
import pwd
import jinja2
import random
import os
import string
import yaml
OUTPUT_DIR = "build"
CLIENT_OUTPUT_DIR = "clients"
DEFAULT_EMAIL = "dummy@dummy.org"
CONF_FILE = "config.yml"
SERVER_IN = "server.in"
SERVER_PATH = "v2ray"
SERVER_FN = "config.json"
NGINX_IN = "nginx.in"
NGINX_PATH = "nginx/nginx/site-confs"
NGINX_FN = "default"
DEFAULT_ALTERID = 64
CLIENT_OBJ_IN = "client_obj.in"
CLIENT_CONF_IN = "client_conf.in"
DOCKER_IN = "docker-compose.in"
WATCHTOWER_IN = "watchtower.in"
DEFAULT_WATCHTOWER_ENABLE = False
DEFAULT_CLIENT_PORT = 1080
def yaml_key_exists_else(mapping : [], name : str, other_val = None, nullable = True):
if (name in mapping) and (mapping[name] != None):
return mapping[name]
else:
if not nullable:
raise Exception("Key " + name + " must not be null.")
else:
return other_val
class Client:
def __init__(self, conf_obj):
self.name = str(yaml_key_exists_else(conf_obj, 'name', nullable=False))
self.id = str(yaml_key_exists_else(conf_obj, 'id', other_val=uuid.uuid4()))
self.alterid = int(yaml_key_exists_else(conf_obj,'alterid', other_val=DEFAULT_ALTERID))
self.port = int(yaml_key_exists_else(conf_obj, 'port', other_val=DEFAULT_CLIENT_PORT))
def print(self, ident):
pre = ""
for i in range(ident):
pre += " "
print(pre + "{")
print(pre + " id: " + self.id)
print(pre + " alterid: " + str(self.alterid))
print(pre + " port: " + str(self.port))
print(pre + "}")
class Config:
@staticmethod
def __randomString(stringLength=16):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
def print(self):
print("Server configuration:")
print(" domain: " + self.domain)
print(" email: " + self.email)
print(" subdomain: " + self.subdomain)
print(" uid: " + str(self.uid))
print(" gid: " + str(self.gid))
print(" path: " + self.path)
print(" clients:")
for i in range(len(self.clients)):
self.clients[i].print(8)
def __init__(self, f):
conf = yaml.safe_load(f)
conf_srv = conf['server']
self.domain = str(yaml_key_exists_else(conf_srv, 'domain', nullable=False))
self.email = str(yaml_key_exists_else(conf_srv, 'email', other_val=DEFAULT_EMAIL))
self.subdomain = str(yaml_key_exists_else(conf_srv, 'subdomain', other_val=""))
self.subdomain_only = len(self.subdomain) == 0
self.uid = int(yaml_key_exists_else(conf_srv, 'uid', other_val=os.getuid()))
self.gid = int(yaml_key_exists_else(conf_srv, 'gid', other_val=os.getgid()))
self.path = str(yaml_key_exists_else(conf_srv, 'path', other_val=self.__randomString()))
self.watchtower = bool(yaml_key_exists_else(conf_srv, 'watchtower', other_val=DEFAULT_WATCHTOWER_ENABLE))
self.clients = []
conf_clients = conf['clients']
for i in range(len(conf_clients)):
self.clients.append(Client(conf_clients[i]))
def main():
with open(CONF_FILE, 'r') as f:
conf = Config(f)
conf.print()
template_dict = {}
template_dict['uid'] = conf.uid
template_dict['gid'] = conf.gid
template_dict['subdomain'] = conf.subdomain
template_dict['subdomain_only'] = conf.subdomain_only
template_dict['domain'] = conf.domain
template_dict['email'] = conf.email
template_dict['path'] = conf.path
if conf.watchtower:
with open(WATCHTOWER_IN, "r") as f:
template_dict['watchtower'] = f.read()
else:
template_dict['watchtower'] = ""
clients = ""
with open(CLIENT_OBJ_IN, "r") as f:
client_obj = f.read()
for i in range(len(conf.clients)):
if i > 0:
clients += ",\n"
clients += jinja2.Template(client_obj).render(id = conf.clients[i].id,
alterid = conf.clients[i].alterid)
template_dict['clients'] = clients
print("Generating files...")
# create output dir
os.makedirs(OUTPUT_DIR, exist_ok=True)
# generate docker-compose.yml
path = os.path.join(OUTPUT_DIR, "docker-compose.yml")
with open(DOCKER_IN, "r") as f:
template = jinja2.Template(f.read())
with open(path, "w") as f:
f.write(template.render(template_dict))
# generate NGINX conf
with open(NGINX_IN, "r") as f:
template = jinja2.Template(f.read())
path = os.path.join(OUTPUT_DIR, NGINX_PATH)
os.makedirs(path, exist_ok=True)
path = os.path.join(path, NGINX_FN)
with open(path, "w") as f:
f.write(template.render(template_dict))
# generate v2ray conf
with open(SERVER_IN, "r") as f:
template = jinja2.Template(f.read())
path = os.path.join(OUTPUT_DIR, SERVER_PATH)
os.makedirs(path, exist_ok=True)
path = os.path.join(path, SERVER_FN)
with open(path, "w") as f:
f.write(template.render(template_dict))
# generate client confs
path = os.path.join(OUTPUT_DIR, CLIENT_OUTPUT_DIR)
os.makedirs(path, exist_ok=True)
with open(CLIENT_CONF_IN, "r") as f:
client_conf_temp = jinja2.Template(f.read())
for i in range(len(conf.clients)):
template_dict['id'] = conf.clients[i].id
template_dict['port'] = conf.clients[i].port
template_dict['alterid'] = conf.clients[i].alterid
epath = os.path.join(path, conf.clients[i].name + "_" + conf.clients[i].id)
os.makedirs(epath, exist_ok=True)
with open(os.path.join(epath, SERVER_FN), "w") as f:
f.write(client_conf_temp.render(template_dict))
# chown
os.chown(OUTPUT_DIR, conf.uid, conf.gid)
for dirpath, dirnames, filenames in os.walk(OUTPUT_DIR):
os.chown(dirpath, conf.uid, conf.gid)
for fname in filenames:
os.chown(os.path.join(dirpath, fname), conf.uid, conf.gid)
print("Please find the generated files in the build directory. To start the stack, run docker-compose up -d in the build directory.")
main()

View File

@ -1,18 +1,18 @@
version: '3.4' version: '3.0'
networks: networks:
lan: br-v2ray:
external: false external: false
services: services:
nginx: nginx:
container_name: nginx container_name: v2ray_nginx
image: linuxserver/letsencrypt image: linuxserver/letsencrypt
restart: always restart: unless-stopped
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
networks: networks:
- lan - br-v2ray
environment: environment:
- PUID={{ uid }} - PUID={{ uid }}
- PGID={{ gid }} - PGID={{ gid }}
@ -20,9 +20,9 @@ services:
- URL={{ domain }} - URL={{ domain }}
- SUBDOMAINS={{ subdomain }} - SUBDOMAINS={{ subdomain }}
- VALIDATION=http - VALIDATION=http
- EMAIL={{ email }} - EMAIL=dummy@dummy.com
- DHLEVEL=2048 - DHLEVEL=2048
- ONLY_SUBDOMAINS={{ only_sub }} - ONLY_SUBDOMAINS={{ subdomain_only }}
- STAGING=false - STAGING=false
ports: ports:
- 80:80 - 80:80
@ -31,11 +31,13 @@ services:
- ./nginx:/config - ./nginx:/config
v2ray: v2ray:
container_name: v2ray container_name: v2ray_v2ray
image: teddysun/v2ray image: teddysun/v2ray
restart: always restart: unless-stopped
networks: networks:
- lan - br-v2ray
command: ["v2ray","-config=/etc/v2ray/config.json"] command: ["v2ray","-config=/etc/v2ray/config.json"]
volumes: volumes:
- ./v2ray:/etc/v2ray - ./v2ray:/etc/v2ray
{{ watchtower }}

View File

@ -1,5 +0,0 @@
d example.com
s v2ray
e email@domain.tld
u 00000000-0000-0000-0000-000000000000
p somepath

View File

@ -17,10 +17,10 @@ server {
# all ssl related config moved to ssl.conf # all ssl related config moved to ssl.conf
include /config/nginx/ssl.conf; include /config/nginx/ssl.conf;
server_name {{server_name}}; server_name {{ subdomain }}.{{ domain }};
location /{{ path }} { location /{{ path }} {
proxy_redirect off; proxy_redirect off;
proxy_pass http://v2ray:8080; proxy_pass http://v2ray_v2ray:8080;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";

View File

@ -6,10 +6,7 @@
"protocol": "vmess", "protocol": "vmess",
"settings": { "settings": {
"clients": [ "clients": [
{ {{ clients }}
"id": "{{ uuid }}",
"alterId": 64
}
] ]
}, },
"streamSettings": { "streamSettings": {
@ -26,4 +23,4 @@
"settings": {} "settings": {}
} }
] ]
} }

158
setup.py
View File

@ -1,158 +0,0 @@
import getopt
import sys
import uuid
import pwd
import jinja2
import random
import os
import string
def randomString(stringLength=16):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
def read_conf(f):
ret = {}
f = open(f, "r")
lines = f.readlines()
for line in lines:
line = line.strip()
eline = line.split(' ')
if len(eline) >= 2:
ret[eline[0]] = eline[1]
return ret
def usage():
print("Usage: python setup.py [options]\n\n\
options:\n\
-h : show usage.\n\
-d domain : your domain - mydomain.tld.\n\
[-u uuid] : the uuid of the user. Optional.\n\
[-p path] : the path of the websocket. Optional.\n\
[-s subdomain] : your subdomain. Optional.\n\
[-e email] : your email. Optional.\n\
[-c conf] : load config from file. Optional.\n\n")
def main():
email = None
subdomain = None
domain = None
conf_file = None
uid = os.getuid()
gid = os.getgid()
v_uuid = None
v_path = None
try:
opts , _ = getopt.getopt(sys.argv[1:], "hd:s:e:c:u:p:")
except getopt.GetoptError as err:
print(str(err))
usage()
sys.exit(1)
for o, a in opts:
if o == "-h":
usage()
sys.exit(0)
elif o == "-d":
if domain != None:
print("Can specify maximum ONE domain.")
sys.exit(1)
else:
domain = a
elif o == "-s":
if subdomain != None:
print("Can specify maximum ONE subdomain.")
sys.exit(1)
else:
subdomain = a
elif o == "-e":
if email != None:
print("Can specify maximum ONE email.")
else:
email = a
elif o == "-c":
conf_file = a
elif o == "-u":
v_uuid = a
elif o == "-s":
v_path = a
# overwrite stuff with conf file
conf = read_conf(conf_file)
if "d" in conf:
domain = conf["d"]
if "s" in conf:
subdomain = conf["s"]
if "e" in conf:
email = conf["e"]
if "u" in conf:
v_uuid = conf["u"]
if "p" in conf:
v_path = conf["p"]
# generate settings if not specified
if v_uuid == None:
v_uuid = uuid.uuid4()
if v_path == None:
v_path = randomString()
if domain == None:
print("Must specify a domain.")
sys.exit(1)
server_name = None
if subdomain == None:
server_name = domain
else:
server_name = subdomain + "." + domain
# process docker-compose
with open("docker-compose.yml", "r") as file:
template = jinja2.Template(file.read())
output = template.render(uid = uid, gid = gid, domain = domain, \
subdomain = (subdomain if subdomain != None else ""), \
only_sub = ("true" if subdomain != None else "false"), \
email = ("dummy@dummy.com" if email == None else email))
with open("docker-compose.yml", "w") as file:
file.write(output)
# process v2ray/config
with open("v2ray/config.json", "r") as file:
template = jinja2.Template(file.read())
output = template.render(uuid = v_uuid, path = v_path)
with open("v2ray/config.json", "w") as file:
file.write(output)
# process nginx/nginx/site-confs/default
with open("nginx/nginx/site-confs/default", "r") as file:
template = jinja2.Template(file.read())
output = template.render(server_name = server_name, path = v_path)
with open("nginx/nginx/site-confs/default", "w") as file:
file.write(output)
# process client.json
with open("client.json", "r") as file:
template = jinja2.Template(file.read())
output = template.render(uuid = v_uuid, path = v_path, server_name = server_name)
with open("client.json", "w") as file:
file.write(output)
print("Processed all files. The detailed client config is written to client.conf.\n" + \
" Summary:\n" + \
" Server Address: " + server_name + "\n" \
" Path: " + v_path + "\n" \
" UUID: " + str(v_uuid) + "\n" \
"Please run docker-compose up -d to start the service.")
main()

8
watchtower.in Normal file
View File

@ -0,0 +1,8 @@
watchtower:
container_name: v2ray_watchtower
image: containrrr/watchtower
restart: unless-stopped
networks:
- br-v2ray
volumes:
- /var/run/docker.sock:/var/run/docker.sock