', methods=['GET'])
147 | def get_configuration(config_id):
148 | if config_id == 'main-ip':
149 | return config.SERVER_MAIN_IP, 200
150 | elif config_id == 'panel-secret-key':
151 | return config.get_panel_secret_key(), 200
152 | elif config_id == 'proxy-connect-uuid':
153 | return config.get_proxy_connect_uuid(), 200
154 | elif config_id == 'panel-domain':
155 | return config.get_panel_domain(), 200
156 |
157 | return "", 404
158 |
159 |
160 | @blueprint.route('/' + config.get_proxy_connect_uuid() + '/route', methods=['POST'])
161 | def add_route():
162 | if not validate_user_panel_secret():
163 | return "", 404
164 |
165 | ip = request.form.get('ip')
166 | if ip == None or ip == "":
167 | return "", 404
168 |
169 | if not re.match(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', ip):
170 | return "", 404
171 |
172 | version = request.form.get('version')
173 | if version == None or version == "":
174 | version = "1000"
175 | if not version.isdigit():
176 | version = "1000"
177 | version = int(version)
178 |
179 | proxy_type = request.form.get('proxyType', 'https')
180 | cpu_usage = request.form.get('cpuUsage', 'Unknown')
181 | ram_usage = request.form.get('ramUsage', 'Unknown')
182 | fake_traffic_enabled = request.form.get('fakeTrafficEnabled', 'False')
183 | fake_traffic_avg_gb_per_day = request.form.get('fakeTrafficAvgGbPerDay', '0')
184 |
185 | try:
186 | fake_traffic_enabled = fake_traffic_enabled.lower() == 'true'
187 | fake_traffic_avg_gb_per_day = float(fake_traffic_avg_gb_per_day)
188 | except:
189 | fake_traffic_enabled = False
190 | fake_traffic_avg_gb_per_day = 0
191 |
192 | utils.online_route_ping(ip, version, proxy_type, cpu_usage, ram_usage, fake_traffic_enabled, fake_traffic_avg_gb_per_day)
193 |
194 | ssh_key = request.form.get('sshKey')
195 | # add the public key to authorized_keys file of user "libertea"
196 | if ssh_key != None and ssh_key != "":
197 | sysops.add_ssh_key(ssh_key)
198 |
199 | traffic_data = request.form.get('trafficData')
200 | try:
201 | traffic_data = json.loads(traffic_data)
202 | if traffic_data != None:
203 | utils.online_route_update_traffic(ip, traffic_data)
204 | except:
205 | print("Error parsing bytes data:", traffic_data)
206 | pass
207 |
208 | return "", 200
209 |
--------------------------------------------------------------------------------
/panel/panel/certbot.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | import random
4 | from . import sysops
5 | from . import config
6 | from datetime import datetime, timedelta
7 |
8 | def save_cert(domain, reload_haproxy=True):
9 | fullchain_file = '/etc/letsencrypt/live/' + domain + '/fullchain.pem'
10 | privkey_file = '/etc/letsencrypt/live/' + domain + '/privkey.pem'
11 | cert_file = '/etc/ssl/ha-certs/' + domain + '.pem'
12 |
13 | prev_file_hash = None
14 | if os.path.isfile(cert_file):
15 | with open(cert_file, 'r') as f:
16 | prev_file_hash = hash(f.read())
17 |
18 | with open(cert_file, 'w') as f:
19 | f.write(open(fullchain_file).read())
20 | f.write(open(privkey_file).read())
21 |
22 | new_file_hash = None
23 | with open(cert_file, 'r') as f:
24 | new_file_hash = hash(f.read())
25 |
26 | if prev_file_hash == new_file_hash:
27 | print(' - Certificate file unchanged')
28 | return False
29 |
30 | if reload_haproxy:
31 | print(' - Reloading HAProxy')
32 | sysops.haproxy_reload()
33 | return True
34 |
35 | def cert_exists(domain):
36 | cert_file = '/etc/ssl/ha-certs/' + domain + '.pem'
37 | return os.path.isfile(cert_file)
38 |
39 | def generate_certificate(domain, retry=True, reload_haproxy=True):
40 | client = config.get_mongo_client()
41 | db = client[config.MONGODB_DB_NAME]
42 | domain_certificates = db.domain_certificates
43 | domain_entry = domain_certificates.find_one({'_id': domain})
44 | if domain_entry is not None:
45 | try:
46 | if domain_entry['updated_at'] > datetime.now() - timedelta(days=1):
47 | if not cert_exists(domain):
48 | print('Certificate for ' + domain + ' does not exist. Regenerating.')
49 | else:
50 | print('Certificate for ' + domain + ' is still valid. Skipping.')
51 | return 'skipped'
52 | if domain_entry['skip_until'] > datetime.now():
53 | print('Certificate for ' + domain + ' is skipped due to multiple failures. Skipping.')
54 | return 'skipped_multiple_failures'
55 | except Exception as e:
56 | pass
57 |
58 | # generate certificate
59 | email_address = 'info@' + domain
60 | print('*** Generating certificate for ' + domain)
61 |
62 | result = sysops.run_command('certbot certonly --standalone -d ' + domain + ' --agree-tos --email ' +
63 | email_address + ' --non-interactive' + ' --http-01-port 9999')
64 | if result == 256 and retry:
65 | # try again after 10 seconds
66 | print(' - Certificate generation failed (256). Retrying in 10 seconds.')
67 | time.sleep(10)
68 | result = sysops.run_command('certbot certonly --standalone -d ' + domain + ' --agree-tos --email ' +
69 | email_address + ' --non-interactive' + ' --http-01-port 9999')
70 |
71 | if result == 0:
72 | print(' - Certificate generated successfully')
73 | result = save_cert(domain, reload_haproxy=reload_haproxy)
74 |
75 | print(' - Finalizing')
76 | domain_certificates.update_one({'_id': domain}, {'$set': {
77 | '_id': domain,
78 | 'updated_at': datetime.now(),
79 | 'failure_count': 0,
80 | 'skip_until': datetime.now()
81 | }}, upsert=True)
82 |
83 | if result:
84 | return 'success'
85 | return 'unchanged'
86 | else:
87 | print(' - Certificate generation for ' + domain + ' failed: ' + str(result))
88 |
89 | fullchain_file = '/etc/letsencrypt/live/' + domain + '/fullchain.pem'
90 | privkey_file = '/etc/letsencrypt/live/' + domain + '/privkey.pem'
91 |
92 | result = False
93 | try:
94 | if os.path.isfile(fullchain_file) and os.path.isfile(privkey_file):
95 | print(' - Saving certificate')
96 | result = save_cert(domain, reload_haproxy=False)
97 | except Exception as e:
98 | print(" - Error saving certificate:", e)
99 |
100 | domain_certificates.update_one({'_id': domain}, {'$set': {
101 | '_id': domain,
102 | 'failure_count': domain_entry['failure_count'] + 1 if domain_entry is not None else 1,
103 | 'updated_at': datetime.now() - timedelta(days=100),
104 | 'skip_until': datetime.now() + timedelta(hours=3) if domain_entry is not None and domain_entry['failure_count'] > 5 else datetime.now() + timedelta(minutes=5),
105 | }}, upsert=True)
106 |
107 | if result:
108 | return 'failed_but_changed'
109 | return 'failed'
110 |
111 |
--------------------------------------------------------------------------------
/panel/panel/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import random
4 | import socket
5 | import requests
6 | from pytz import timezone
7 | from datetime import datetime, timedelta
8 | from pymongo import MongoClient
9 |
10 | print("Initializing...")
11 |
12 | # Force ipv4 for requests
13 | old_getaddrinfo = socket.getaddrinfo
14 | def new_getaddrinfo(*args, **kwargs):
15 | responses = old_getaddrinfo(*args, **kwargs)
16 | return [response
17 | for response in responses
18 | if response[0] == socket.AF_INET]
19 | socket.getaddrinfo = new_getaddrinfo
20 |
21 | def get_libertea_branch():
22 | return os.environ.get('LIBERTEA_BRANCH_NAME')
23 |
24 | LIBERTEA_VERSION = 1043
25 | LIBERTEA_PROXY_VERSION = 1007
26 | VERSION_ENDPOINT = "https://raw.githubusercontent.com/VZiChoushaDui/Libertea/" + get_libertea_branch() + "/version.txt"
27 |
28 | HAPROXY_CONTAINER_NAME = 'libertea-haproxy'
29 |
30 | # MONGODB_HOST = "libertea-mongodb:27017"
31 | MONGODB_HOST = "localhost:27017"
32 | MONGODB_USER = "root"
33 | MONGODB_DB_NAME = "panel"
34 |
35 | JWT_VALID_TIME = timedelta(hours=24)
36 |
37 | ROUTE_IP_LISTS = [
38 | {
39 | "id": "cn",
40 | "name": "China",
41 | },
42 | {
43 | "id": "ru",
44 | "name": "Russia",
45 | },
46 | {
47 | "id": "cu",
48 | "name": "Cuba",
49 | },
50 | {
51 | "id": "th",
52 | "name": "Thailand",
53 | },
54 | {
55 | "id": "tm",
56 | "name": "Turkmenistan",
57 | },
58 | {
59 | "id": "ir",
60 | "name": "Iran",
61 | },
62 | {
63 | "id": "sy",
64 | "name": "Syria",
65 | },
66 | {
67 | "id": "sa",
68 | "name": "Saudi Arabia",
69 | },
70 | {
71 | "id": "tr",
72 | "name": "Turkey",
73 | }
74 | ]
75 | ROUTE_IP_LISTS = sorted(ROUTE_IP_LISTS, key=lambda k: k['name'])
76 |
77 | def get_ip_api_url():
78 | return random.choice([
79 | 'https://api.ipify.org',
80 | 'https://ifconfig.io/ip',
81 | 'https://icanhazip.com',
82 | 'https://ident.me',
83 | 'https://ipecho.net/plain',
84 | 'https://myexternalip.com/raw',
85 | 'https://wtfismyip.com/text',
86 | 'https://checkip.amazonaws.com',
87 | ])
88 |
89 | SERVER_MAIN_IP = None
90 | for i in range(5):
91 | try:
92 | ip = requests.get(get_ip_api_url(), timeout=3).content.decode('utf8').strip()
93 | if not re.match(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', ip):
94 | print("Failed to get server ip. Result was: " + str(ip))
95 | continue
96 |
97 | SERVER_MAIN_IP = ip
98 | break
99 | except Exception as e:
100 | print("Failed to get server ip: " + str(e))
101 |
102 | if SERVER_MAIN_IP is None:
103 | raise Exception("couldn't fetch SERVER_MAIN_IP")
104 |
105 | if not re.match(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', SERVER_MAIN_IP):
106 | raise Exception("couldn't fetch SERVER_MAIN_IP. Result was: " + str(SERVER_MAIN_IP))
107 |
108 | # print("SERVER_MAIN_IP: " + SERVER_MAIN_IP)
109 |
110 | def get_mongodb_password():
111 | return os.environ.get('PANEL_MONGODB_PASSWORD')
112 |
113 | def get_panel_secret_key():
114 | return os.environ.get('PANEL_SECRET_KEY')
115 |
116 | def get_admin_uuid():
117 | return os.environ.get('PANEL_ADMIN_UUID')
118 |
119 | def get_proxy_connect_uuid():
120 | return os.environ.get('PANEL_PROXY_CONNECT_UUID')
121 |
122 | def get_proxy_configuration_uuid():
123 | return os.environ.get('PANEL_PROXY_CONFIGURATION_UUID')
124 |
125 | def get_panel_domain():
126 | return os.environ.get('PANEL_DOMAIN')
127 |
128 | def get_mongodb_connection_string():
129 | connstr = "mongodb://" + MONGODB_USER + ":" + get_mongodb_password() + "@" + MONGODB_HOST
130 | # print("connstr:", connstr)
131 | return connstr
132 |
133 | ___mongoClient = None
134 | ___mongoClientPid = None
135 |
136 | def get_mongo_client():
137 | global ___mongoClient
138 | global ___mongoClientPid
139 |
140 | my_pid = os.getpid()
141 | if ___mongoClient is None or ___mongoClientPid != my_pid:
142 | print(f" -- creating mongo client on pid {my_pid}")
143 | ___mongoClient = MongoClient(get_mongodb_connection_string(), serverSelectionTimeoutMS=5000)
144 | ___mongoClientPid = my_pid
145 |
146 | try:
147 | ___mongoClient.server_info()
148 | except Exception as e:
149 | print(f" -- reconnecting mongo client on pid {my_pid}")
150 | ___mongoClient = MongoClient(get_mongodb_connection_string(), serverSelectionTimeoutMS=5000)
151 | ___mongoClientPid = my_pid
152 |
153 | return ___mongoClient
154 |
155 | def get_hostcontroller_api_key():
156 | return os.environ.get('HOSTCONTROLLER_API_KEY')
157 |
158 | def get_bootstrap_script_url():
159 | return "https://raw.githubusercontent.com/VZiChoushaDui/Libertea/" + get_libertea_branch() + "/bootstrap.sh"
160 |
161 | def get_root_dir():
162 | env_root_dir = os.environ.get('LIBERTEA_ROOT_DIR')
163 | if env_root_dir is not None and env_root_dir != "":
164 | path = env_root_dir
165 | if path[-1] != '/':
166 | path += '/'
167 | return path
168 | return "/root/libertea/"
169 |
170 |
171 | def get_regional_domain_suffixes(countries):
172 | suffixes = ['local', 'lan']
173 | if 'cn' in countries: # china
174 | suffixes.append('cn')
175 | elif 'cu' in countries: # cuba
176 | suffixes.append('cu')
177 | elif 'ir' in countries: # iran
178 | suffixes.append('ir')
179 | elif 'ru' in countries: # russia
180 | suffixes.append('ru')
181 | elif 'sa' in countries: # saudi arabia
182 | suffixes.append('sa')
183 | elif 'sy' in countries: # syria
184 | suffixes.append('sy')
185 | elif 'th' in countries: # thailand
186 | suffixes.append('th')
187 | elif 'tm' in countries: # turkmenistan
188 | suffixes.append('tm')
189 | elif 'tr' in countries:
190 | suffixes.append('tr')
191 | return suffixes
192 |
193 | def get_timezone(country):
194 | if country == 'cn':
195 | return 'Asia/Shanghai'
196 | elif country == 'ru':
197 | return 'Europe/Moscow'
198 | elif country == 'cu':
199 | return 'America/Havana'
200 | elif country == 'th':
201 | return 'Asia/Bangkok'
202 | elif country == 'tm':
203 | return 'Asia/Ashgabat'
204 | elif country == 'ir':
205 | return 'Asia/Tehran'
206 | elif country == 'sy':
207 | return 'Asia/Damascus'
208 | elif country == 'sa':
209 | return 'Asia/Riyadh'
210 | elif country == 'tr':
211 | return 'Europe/Istanbul'
212 | return 'UTC'
213 |
214 | def current_time_in_timezone(country):
215 | return datetime.now(timezone(get_timezone(country)))
216 |
217 | SIGNAL_INVALIDATE_CACHE = 18
--------------------------------------------------------------------------------
/panel/panel/jwt.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VZiChoushaDui/Libertea/8eda816ae8f39ea0fc9be7f683108a9bd7ae7674/panel/panel/jwt.py
--------------------------------------------------------------------------------
/panel/panel/subscription_conf_generator.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import json
4 | import base64
5 | from . import utils
6 | from . import config
7 | from . import settings
8 | from . import clash_conf_generator
9 | from pymongo import MongoClient
10 | from flask import render_template
11 | from datetime import datetime, timedelta
12 |
13 | def fix_path_for_grpc_clients(path):
14 | path = path.replace('/', '___')
15 | if path.startswith('___'):
16 | path = '/' + path[3:]
17 | return path
18 |
19 | def generate_conf(user_id, connect_url, vless=True, trojan=True, shadowsocks=True, vmess=True, enabled_tiers=None, websocket=True, grpc=True):
20 | if not utils.has_active_endpoints():
21 | raise Exception('No active domains found')
22 |
23 | client = config.get_mongo_client()
24 | db = client[config.MONGODB_DB_NAME]
25 |
26 | provider_urls = []
27 | providers = clash_conf_generator.get_providers(connect_url, db, is_for_subscription=True)
28 | if enabled_tiers is not None:
29 | providers = [p for p in providers if p['tier'] in enabled_tiers]
30 | try:
31 | providers = sorted(providers, key=lambda k: int(k['tier']))
32 | except:
33 | pass
34 |
35 | for provider in providers:
36 | # generate url for each provider
37 | if provider['type'] == 'trojan-ws' and trojan and websocket:
38 | provider_urls.append('trojan://' + provider['password'] + '@' + provider['server'] + ':' + str(provider['port']) + provider['path'] +
39 | '?sni=' + provider['sni'] + '&type=ws&host=' + provider['host'] + '&path=' + provider['path'] +
40 | '&alpn=http/1.1&allowInsecure=' + (provider['skip_cert_verify']) + '&fp=chrome' +
41 | '#' + config.get_panel_domain() + ' ' + provider['name'])
42 | elif provider['type'] == 'vless-ws' and vless and websocket:
43 | provider_urls.append('vless://' + provider['password'] + '@' + provider['server'] + ':' + str(provider['port']) + provider['path'] +
44 | '?sni=' + provider['sni'] + '&type=ws&host=' + provider['host'] + '&path=' + provider['path'] +
45 | '&alpn=http/1.1&allowInsecure=' + (provider['skip_cert_verify']) + '&security=tls&encryption=none&fp=chrome'
46 | '#' + config.get_panel_domain() + ' ' + provider['name'])
47 | elif provider['type'] == 'ss-v2ray' and shadowsocks and websocket:
48 | # add xray plugin to url
49 | provider_urls.append('ss://' + provider['password'] + '@' + provider['server'] + ':' + str(provider['port']) + provider['path'] +
50 | '?plugin=v2ray-plugin%3Btls%3Bhost%3D' + provider['host'] + '%3Bpath%3D' + provider['path'] + '%3Bmux%3D4' +
51 | '#' + config.get_panel_domain() + ' ' + provider['name'])
52 | elif provider['type'] == 'vless-grpc' and vless and grpc:
53 | provider_urls.append('vless://' + provider['password'] + '@' + provider['server'] + ':' + str(provider['port']) +
54 | '?sni=' + provider['sni'] + '&type=grpc&host=' + provider['host'] +
55 | '&alpn=http/1.1&allowInsecure=' + (provider['skip_cert_verify']) + '&serviceName=' + fix_path_for_grpc_clients(provider['path'])[1:] +
56 | '&security=tls&encryption=none&fp=chrome#' + config.get_panel_domain() + ' ' + provider['name'])
57 | elif provider['type'] == 'trojan-grpc' and trojan and grpc:
58 | provider_urls.append('trojan://' + provider['password'] + '@' + provider['server'] + ':' + str(provider['port']) +
59 | '?sni=' + provider['sni'] + '&type=grpc&host=' + provider['host'] + '&path=' +
60 | '&alpn=http/1.1&allowInsecure=' + (provider['skip_cert_verify']) + '&serviceName=' + fix_path_for_grpc_clients(provider['path'])[1:] +
61 | '&fp=chrome#' + config.get_panel_domain() + ' ' + provider['name'])
62 | elif provider['type'] == 'vmess-ws' and vmess and websocket:
63 | # vmess is a base64 encoded json
64 | vmess_json = {
65 | 'v': '2',
66 | 'ps': provider['name'],
67 | 'add': provider['host'],
68 | 'port': str(provider['port']),
69 | 'id': provider['password'],
70 | 'aid': '2',
71 | 'net': 'ws',
72 | 'type': 'none',
73 | 'host': provider['host'],
74 | 'path': provider['path'],
75 | 'tls': 'tls',
76 | 'sni': provider['sni'],
77 | 'allowInsecure': provider['skip_cert_verify'],
78 | }
79 | provider_urls.append('vmess://' + base64.b64encode(json.dumps(vmess_json).encode('utf-8')).decode('utf-8'))
80 | elif provider['type'] == 'vmess-grpc' and vmess and grpc:
81 | vmess_json = {
82 | 'v': '2',
83 | 'ps': provider['name'],
84 | 'add': provider['host'],
85 | 'port': str(provider['port']),
86 | 'id': provider['password'],
87 | 'aid': '2',
88 | 'net': 'grpc',
89 | 'type': 'none',
90 | 'host': provider['host'],
91 | 'path': fix_path_for_grpc_clients(provider['path']),
92 | 'tls': 'tls',
93 | 'sni': provider['sni'],
94 | 'allowInsecure': provider['skip_cert_verify'],
95 | 'serviceName': fix_path_for_grpc_clients(provider['path'])[1:],
96 | }
97 | provider_urls.append('vmess://' + base64.b64encode(json.dumps(vmess_json).encode('utf-8')).decode('utf-8'))
98 |
99 |
100 | return provider_urls
101 |
102 |
103 |
104 | def generate_conf_json(user_id, connect_url, enabled_tiers=None):
105 | if not utils.has_active_endpoints():
106 | raise Exception('No active domains found')
107 |
108 | client = config.get_mongo_client()
109 | db = client[config.MONGODB_DB_NAME]
110 |
111 | providers = clash_conf_generator.get_providers(connect_url, db, is_for_subscription=True)
112 | provider_confs = []
113 | if enabled_tiers is not None:
114 | providers = [p for p in providers if p['tier'] in enabled_tiers]
115 | try:
116 | providers = sorted(providers, key=lambda k: int(k['tier']))
117 | except:
118 | pass
119 |
120 | for provider in providers:
121 | if provider['type'] == 'ss-v2ray':
122 | ss_json_v2ray = {
123 | "server": provider['server'],
124 | "server_port": provider['port'],
125 | "password": provider['password'],
126 | "method": "chacha20-ietf-poly1305",
127 | "plugin": "v2ray-plugin",
128 | "plugin_opts": "tls;host=" + provider['host'] + ";path=" + fix_path_for_grpc_clients(provider['path']) + ";mux=4",
129 | "remarks": config.get_panel_domain() + ' ' + provider['name'],
130 | }
131 | provider_confs.append(ss_json_v2ray)
132 |
133 | return provider_confs
134 |
--------------------------------------------------------------------------------
/panel/panel/sysops.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | import pymongo
4 | import threading
5 | from . import config
6 | from . import settings
7 |
8 | def run_command(command):
9 | # go to root directory
10 | os.chdir(config.get_root_dir())
11 | # run the command, and return the exit code
12 | result = os.system(command)
13 |
14 | print("Ran command '" + command + "' and got exit code " + str(result))
15 | return result
16 |
17 | def ___haproxy_reload_internal(sleep_secs):
18 | print("**** Reloading haproxy container ****")
19 | if run_command('sleep ' + str(sleep_secs) + ' && docker kill -s HUP ' + config.HAPROXY_CONTAINER_NAME) == 0:
20 | return True
21 |
22 | return False
23 |
24 | def haproxy_reload():
25 | th = threading.Thread(target=___haproxy_reload_internal, args=(2,))
26 | th.start()
27 | return True
28 |
29 | def regenerate_camouflage_cert():
30 | if settings.get_camouflage_domain_without_protocol() is None:
31 | return False
32 |
33 | if run_command('./haproxy/cert-camouflage.sh ' + settings.get_camouflage_domain_without_protocol()) == 0:
34 | haproxy_reload()
35 | return True
36 |
37 | haproxy_reload()
38 | return False
39 |
40 | def haproxy_renew_certs():
41 | if run_command('./haproxy/certbot.sh') == 0:
42 | return True
43 |
44 | return False
45 |
46 | def haproxy_ensure_folder():
47 | folder = config.get_root_dir() + 'data/haproxy-lists'
48 | if not os.path.exists(folder):
49 | try:
50 | os.makedirs(folder)
51 | except:
52 | pass
53 |
54 | def haproxy_update_users_list():
55 | count = 0
56 | haproxy_ensure_folder()
57 | with open(config.get_root_dir() + 'data/haproxy-lists/valid-user-endpoints.lst', 'w') as f:
58 | client = config.get_mongo_client()
59 | db = client[config.MONGODB_DB_NAME]
60 | users = db.users
61 | for user in users.find():
62 | # write each user to file
63 | f.write('/' + user['connect_url'] + '/' + '\n')
64 | f.write('/' + user['connect_url'] + '___' + '\n')
65 | count += 1
66 |
67 | print("Wrote " + str(count) + " users to haproxy-lists/valid-user-endpoints.lst")
68 |
69 | count = 0
70 | with open(config.get_root_dir() + 'data/haproxy-lists/valid-panel-endpoints.lst', 'w') as f:
71 | client = config.get_mongo_client()
72 | db = client[config.MONGODB_DB_NAME]
73 | users = db.users
74 | for user in users.find():
75 | # write each user to file
76 | f.write('/' + user['_id'] + '/' + '\n')
77 | count += 1
78 | f.write('/' + config.get_proxy_connect_uuid() + '/' + '\n')
79 | f.write('/' + config.get_proxy_configuration_uuid() + '/' + '\n')
80 | count += 1
81 |
82 | print("Wrote " + str(count) + " users to haproxy-lists/valid-panel-endpoints.lst")
83 | return haproxy_reload()
84 |
85 | def haproxy_update_domains_list():
86 | count = 0
87 | haproxy_ensure_folder()
88 | with open(config.get_root_dir() + 'data/haproxy-lists/domains.lst', 'w') as f:
89 | client = config.get_mongo_client()
90 | db = client[config.MONGODB_DB_NAME]
91 | domains = db.domains
92 | for domain in domains.find():
93 | # write each domain to file
94 | f.write(domain['_id'] + '\n')
95 | count += 1
96 |
97 | print("Wrote " + str(count) + " domains to haproxy-lists/domains.lst")
98 | return haproxy_reload()
99 |
100 | def haproxy_update_camouflage_list():
101 | haproxy_ensure_folder()
102 | camouflage_domain = settings.get_camouflage_domain()
103 | with open(config.get_root_dir() + 'data/haproxy-lists/camouflage-hosts.lst', 'w') as f:
104 | camouflage_domain_name = camouflage_domain
105 | if camouflage_domain_name:
106 | if camouflage_domain_name.startswith('https://'):
107 | camouflage_domain_name = camouflage_domain_name[8:]
108 | elif camouflage_domain_name.startswith('http://'):
109 | camouflage_domain_name = camouflage_domain_name[7:]
110 |
111 | if '/' in camouflage_domain_name:
112 | camouflage_domain_name = camouflage_domain_name.split('/')[0]
113 | if ':' in camouflage_domain_name:
114 | camouflage_domain_name = camouflage_domain_name.split(':')[0]
115 |
116 | f.write(camouflage_domain_name + '\n')
117 | print("Wrote " + camouflage_domain_name + " to haproxy-lists/camouflage-hosts.lst")
118 |
119 | camouflage_port = 443
120 | try:
121 | if camouflage_domain:
122 | camouflage_domain_name = camouflage_domain.split('/')[2]
123 | if ':' in camouflage_domain_name:
124 | camouflage_port = int(camouflage_domain_name.split(':')[1])
125 | elif camouflage_domain.startswith('http://'):
126 | camouflage_port = 80
127 | else:
128 | camouflage_port = 443
129 | except:
130 | print("Error parsing port from " + camouflage_domain)
131 |
132 | with open(config.get_root_dir() + 'data/haproxy-lists/camouflage-port.lst', 'w') as f:
133 | f.write(str(camouflage_port) + '\n')
134 |
135 | print("Wrote " + str(camouflage_port) + " to haproxy-lists/camouflage-port.lst")
136 |
137 | return haproxy_reload()
138 |
139 | def add_ssh_key(ssh_key):
140 | ssh_keys_dir = '/home/libertea/.ssh'
141 | ssh_key_file = ssh_keys_dir + '/authorized_keys'
142 |
143 | if not os.path.exists(ssh_keys_dir):
144 | os.makedirs(ssh_keys_dir)
145 |
146 | if not os.path.exists(ssh_key_file):
147 | with open(ssh_key_file, 'w') as f:
148 | f.write(ssh_key + '\n')
149 | return True
150 |
151 | # check if the key already exists
152 | with open(ssh_key_file, 'r') as f:
153 | for line in f.readlines():
154 | if ssh_key in line:
155 | return True
156 |
157 | # append the key to the file
158 | with open(ssh_key_file, 'a') as f:
159 | f.write(ssh_key + '\n')
160 |
161 | return True
162 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/new_domain.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.jinja" %}
2 | {% block content %}
3 | Add new domain
4 |
21 |
22 |
33 |
34 | {% endblock %}
35 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/new_secondary_proxy.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.jinja" %}
2 | {% block content %}
3 |
12 | Add new secondary proxy
13 |
14 |
15 |
19 |
25 |
58 |
59 | To begin, copy and paste the following command into the SSH shell of your secondary server:
60 |
61 |
62 |
63 |
64 | Copy code to clipboard
65 |
66 |
67 |
68 |
69 |
70 | Waiting for your server to connect...
71 |
72 |
73 |
74 |
104 |
105 |
106 |
107 |
178 |
179 | {% endblock %}
180 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/proxies.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 | Secondary proxies allow users to connect to your Libertea via direct IP routes, bypassing the CDN for
7 | more reliable connections for when the ISPs are blocking the CDN.
8 |
9 |
10 | The IPs for secondary proxies might get blocked after a few months, in which case you can simply delete
11 | the old secondary proxy server and add a new one, without disrupting your service and main server.
12 |
13 |
14 |
15 |
20 |
21 |
22 | To add a secondary proxy to your VPN, buy a new server, and copy and paste the following command into the SSH shell:
23 |
24 |
25 |
26 |
27 | Copy code to clipboard
28 |
29 |
30 |
31 |
67 |
68 |
82 | {% endblock %}
83 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/security.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.jinja" %}
2 | {% block content %}
3 |
4 |
22 |
23 | {% if health_warnings.no_camouflage.status %}
24 |
25 |
Camouflage website
26 |
Your domains will show the content from camouflage website so that it looks like a normal website and the chance of being blocked is much lower.
27 |
Set up camouflage website
28 |
29 | {% endif %}
30 |
31 | {% if health_warnings.update_available.status %}
32 |
33 |
Update available
34 |
There is a new version of Libertea available.
35 |
Update now
36 |
37 | {% endif %}
38 |
39 | {% if health_warnings.proxy_update_available.status %}
40 |
41 |
Proxy update available
42 |
One or more of your Secondary Proxies needs to be updated.
43 |
More info
44 |
45 | {% endif %}
46 |
47 | {% if health_warnings.no_direct_country.status %}
48 |
49 |
Direct route for your country
50 |
You have not set up your country in Libertea. By doing so, you will be able to access domestic websites faster, and also reduce the chance of being blocked.
51 |
Set up your country
52 |
53 | {% endif %}
54 |
55 | {% if health_warnings.same_domain_for_panel_and_vpn.status %}
56 |
57 |
Same domain for Panel and VPN
58 |
You have set the same domain {{ panel_domain }} for Panel and VPN. This is not recommended because in case of a block, your clients will not be able to receive updates for the new routes.
59 |
We recommend using separate domains for Panel and VPN.
60 |
Change your domains
61 |
62 | {% endif %}
63 |
64 | {% if health_warnings.no_secondary_route.status %}
65 |
66 |
Secondary proxies
67 |
Consider adding a secondary proxy for better availability across various networks.
68 |
Add a secondary proxy
69 |
70 | {% endif %}
71 |
72 | {% if health_warnings.secondary_route_without_domain.status %}
73 |
74 |
Domains for Secondary proxies
75 |
Some of your Secondary proxies does not have a connected domain. It's recommended to connect a domain or subdomain to each of your secondary proxies.
76 |
Go to Routes
77 |
78 | {% endif %}
79 |
80 | {% if no_warnings %}
81 |
82 |
Everything looks good on your Libertea server. 🎉
83 |
Go back to Dashboard
84 |
85 | {% endif %}
86 |
87 | {% endblock %}
88 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/users.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.jinja" %}
2 | {% block content %}
3 |
24 |
25 | {% if no_domain_warning %}
26 |
27 | Warning: You don't have any domains or secondary proxies set for your VPN. User's won't be able to connect to your VPN until you add a domain.
28 |
29 | {% endif %}
30 |
63 |
64 |
113 |
114 | {% endblock %}
115 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/base.jinja:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Libertea
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
231 |
232 |
233 |
234 | {% block content %}
235 | {% endblock %}
236 |
237 |
238 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/camouflage.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 | One more thing: choose a Camouflage website for your Libertea.
8 |
9 |
10 | The home page of your domains will show the contents of the website you choose here. This will make it harder to block your domains.
11 |
12 |
46 |
47 | {% endblock %}
48 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/country.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
8 |
9 |
10 |
11 |
12 | Which country are you in?
13 |
14 |
15 | Libertea will configure itself to work best in your country.
16 |
17 |
18 | Websites from your country will be routed directly, and websites from other countries will be routed through Libertea.
19 |
20 |
21 | This will make your browsing experience faster, and will make it harder to block Libertea.
22 |
23 |
45 |
46 | {% endblock %}
47 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/finished.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 | Libertea is now ready to use.
8 |
9 |
10 | You can now connect to Libertea and enjoy the Internet without censorship.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
Finish
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/routes-custom.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
22 |
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/routes.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 | You have configured {{ panel_domain }} as your main domain.
8 |
9 |
To keep things simple, you can use the same domain to connect to VPN as well.
10 |
You can also use a different domain for VPN connection, and keep this one for receiving updates for your VPN, in case this domain gets blocked.
11 |
12 |
Use {{ panel_domain }}
13 |
Set another domain
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/routes2.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 | Later on, you can add more domains to your Libertea, to make it harder to block.
8 |
9 |
10 | Alternatively, you can add secondary servers so you can connect to Libertea through them.
11 |
12 |
13 | You'll be able to manage all that in Routes tab of Libertea.
14 |
15 |
16 |
17 |
18 |
19 |
Back
20 |
Next
21 |
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/panel/panel/templates/admin/welcome/welcome.jinja:
--------------------------------------------------------------------------------
1 | {% extends "admin/welcome/base.jinja" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
7 |
Welcome to Libertea!
8 |
9 |
10 |
New user?
11 |
You can try your Libertea right away and enjoy the Internet without censorship.
12 |
15 |
16 |
Ready for more?
17 | {% if setup_finished %}
18 |
You have finished setting up your Libertea. You can view and manage your Libertea in the dashboard.
19 |
22 | {% else %}
23 |
Finish setting up your Libertea and configure it to work best for you.
24 |
28 | {% endif %}
29 |
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/panel/panel/templates/cdn-other-udp.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.entry_type == "CDNProxy-Other" %}
5 | {% if provider.type == "trojan-ws" %}
6 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
7 | {% include ("providers/" + provider.type + ".yaml") %}
8 | {% endwith %}
9 | {% endif %}
10 | {% endif %}
11 | {% endif %}
12 | {% endfor %}
13 |
--------------------------------------------------------------------------------
/panel/panel/templates/cdn-other.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.entry_type == "CDNProxy-Other" %}
5 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
6 | {% include ("providers/" + provider.type + ".yaml") %}
7 | {% endwith %}
8 | {% endif %}
9 | {% endif %}
10 | {% endfor %}
11 |
--------------------------------------------------------------------------------
/panel/panel/templates/cloudflare-udp.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.entry_type == "CDNProxy-Cloudflare" %}
5 | {% if provider.type == "trojan-ws" %}
6 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
7 | {% include ("providers/" + provider.type + ".yaml") %}
8 | {% endwith %}
9 | {% endif %}
10 | {% endif %}
11 | {% endif %}
12 | {% endfor %}
13 |
--------------------------------------------------------------------------------
/panel/panel/templates/cloudflare.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.entry_type == "CDNProxy-Cloudflare" %}
5 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
6 | {% include ("providers/" + provider.type + ".yaml") %}
7 | {% endwith %}
8 | {% endif %}
9 | {% endif %}
10 | {% endfor %}
11 |
--------------------------------------------------------------------------------
/panel/panel/templates/direct-udp.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.entry_type == "SecondaryProxy" %}
5 | {% if provider.type == "trojan-ws" %}
6 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
7 | {% include ("providers/" + provider.type + ".yaml") %}
8 | {% endwith %}
9 | {% endif %}
10 | {% endif %}
11 | {% endif %}
12 | {% endfor %}
13 |
--------------------------------------------------------------------------------
/panel/panel/templates/direct.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.entry_type == "SecondaryProxy" %}
5 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
6 | {% include ("providers/" + provider.type + ".yaml") %}
7 | {% endwith %}
8 | {% endif %}
9 | {% endif %}
10 | {% endfor %}
11 |
--------------------------------------------------------------------------------
/panel/panel/templates/groups/fallback.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: fallback
3 | proxies:
4 | {% for provider in providers %}{% if provider.tier == tier.index %}{% if not provider.meta_only or meta %}{% if provider.type == 'servergroup' %}
5 | - {{ provider.name }}
6 | {% endif %}{% endif %}{% endif %}{% endfor %}
7 | url: 'http://www.google.com/generate_204'
8 | interval: {{ 60 + (tier.index | int - 1) * 11 }}
9 | lazy: false
10 |
--------------------------------------------------------------------------------
/panel/panel/templates/groups/healthcheckserver.yaml:
--------------------------------------------------------------------------------
1 | - name: "healthcheck-{{ name }}"
2 | type: fallback
3 | proxies:
4 | - {{ name }}
5 | url: 'https://{{panel_domain}}/{{user_id}}/health?protocol={{ type }}&domain={% if entry_type == "SecondaryProxy" %}{{ server }}{% else %}{{ host }}{% endif %}&domain_dns={{ server }}'
6 | interval: 290
7 | lazy: false
8 |
--------------------------------------------------------------------------------
/panel/panel/templates/groups/loadbalance.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: load-balance
3 | strategy: round-robin
4 | proxies:
5 | {% for provider in providers %}{% if provider.tier == tier.index %}{% if not provider.meta_only or meta %}{% if provider.type == 'servergroup' %}
6 | - {{ provider.name }}
7 | {% endif %}{% endif %}{% endif %}{% endfor %}
8 | url: 'http://www.google.com/generate_204'
9 | interval: {{ 60 + (tier.index | int - 1) * 11 }}
10 | lazy: false
11 |
--------------------------------------------------------------------------------
/panel/panel/templates/groups/servergroupfallback.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: fallback
3 | proxies:
4 | {% for provider in providers %}{% if provider.group_name == name %}{% if not provider.meta_only or meta %}{% if provider.type != 'servergroup' %}
5 | - {{ provider.name }}
6 | {% endif %}{% endif %}{% endif %}{% endfor %}
7 | {% if health_check %}
8 | url: 'https://{{panel_domain}}/{{user_id}}/health?protocol=all-fallback&domain={% if entry_type == "SecondaryProxy" %}{{ server }}{% else %}{{ host }}{% endif %}&domain_dns={{ server }}'
9 | interval: 290
10 | {% else %}
11 | url: 'http://www.google.com/generate_204'
12 | interval: {{ 290 + group_id * 7 }}
13 | {% endif %}
14 | lazy: false
15 |
--------------------------------------------------------------------------------
/panel/panel/templates/groups/urltest.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: url-test
3 | tolerance: 4000
4 | proxies:
5 | {% for provider in providers %}{% if provider.tier == tier.index %}{% if not provider.meta_only or meta %}{% if provider.type == 'servergroup' %}
6 | - {{ provider.name }}
7 | {% endif %}{% endif %}{% endif %}{% endfor %}
8 | url: 'http://www.google.com/generate_204'
9 | interval: {{ 60 + (tier.index | int - 1) * 11 }}
10 | lazy: false
11 |
--------------------------------------------------------------------------------
/panel/panel/templates/main-singlefile.yaml:
--------------------------------------------------------------------------------
1 |
2 | mode: rule
3 |
4 | dns:
5 | enable: true
6 | listen: 0.0.0.0:53
7 | enhanced-mode: fake-ip
8 | fake-ip-range: 198.18.0.1/16
9 | nameserver:
10 | - 4.2.2.4
11 | - 8.8.8.8
12 |
13 | tun:
14 | enable: true
15 | stack: gvisor # or gvisor
16 | dns-hijack:
17 | - 198.18.0.2:53
18 | auto-route: true
19 | auto-detect-interface: true
20 |
21 | global-client-fingerprint: chrome
22 |
23 | proxies:
24 | {% for provider in providers %}
25 | {% if not provider.meta_only or meta %}
26 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
27 | {% include ("providers/" + provider.type + ".yaml") %}
28 | {% endwith %}
29 | {% endif %}
30 | {% endfor %}
31 |
32 | proxy-groups:
33 | - name: "main"
34 | type: select
35 | proxies:
36 | {% if custom_info_entries is none or custom_info_entries | length == 0 %}
37 | - auto-group
38 | {% endif %}
39 | {% if custom_info_entries is not none %}
40 | {% for info_entry in custom_info_entries %}
41 | - "{{ info_entry }}"
42 | {% endfor %}
43 | {% endif %}
44 | {% if manual_tier_select_clash %}
45 | {% for tier in tiers %}{% if tier.exists %}
46 | - tier{{tier.index}}-group
47 | {% endif %}{% endfor %}
48 | {% endif %}
49 |
50 | - name: "auto-group"
51 | type: fallback
52 | proxies:
53 | {% for tier in tiers %}{% if tier.exists %}
54 | - tier{{tier.index}}-group
55 | {% endif %}{% endfor %}
56 | url: 'https://www.google.com/generate_204'
57 | interval: 90
58 | global-default: true
59 | lazy: false
60 |
61 | {% for tier in tiers %}
62 | {% if tier.exists %}
63 | {% with name="tier" + (tier.index) + "-group", proxies=providers, tier=tier, udp_only=False, panel_domain=panel_domain, user_id=user_id, udp_only=False %}
64 | {% if tier.type == 'urltest' %}{% include "groups/urltest.yaml" %}{% endif %}
65 | {% if tier.type == 'fallback' %}{% include "groups/fallback.yaml" %}{% endif %}
66 | {% if tier.type == 'loadbalance' %}{% include "groups/loadbalance.yaml" %}{% endif %}
67 | {% endwith %}
68 | {% endif %}
69 | {% endfor %}
70 |
71 | {% for group in groups %}
72 | {% with name=group.name, proxies=providers, panel_domain=panel_domain, user_id=user_id, group_id=group.index, health_check=health_check, server = group.server, host = group.host, sni = group.sni, entry_type = group.entry_type %}
73 | {% include "groups/servergroupfallback.yaml" %}
74 | {% endwith %}
75 | {% endfor %}
76 |
77 |
78 | {% if custom_info_entries is not none %}
79 | {% for info_entry in custom_info_entries %}
80 | - name: "{{ info_entry }}"
81 | type: select
82 | proxies:
83 | - auto-group
84 | {% endfor %}
85 | {% endif %}
86 |
87 |
88 | mixed-port: 1096
89 | redir-port: 7892
90 | external-controller: 127.0.0.1:9090
91 |
92 | script:
93 | shortcuts:
94 | udpnetwork: network == 'udp'
95 |
96 | rules:
97 | - IP-CIDR,127.0.0.1/8,DIRECT
98 | - IP-CIDR,172.16.0.0/12,DIRECT
99 | - IP-CIDR,192.168.0.0/16,DIRECT
100 |
101 | {% for domain_suffix in domain_direct_suffixes %}
102 | - DOMAIN-SUFFIX,{{ domain_suffix }},DIRECT
103 | {% endfor %}
104 |
105 | {% for country in ips_direct_countries %}
106 | {% include "rules/" + country + "-direct.yaml" %}
107 | {% endfor %}
108 |
109 | - MATCH,main
110 |
--------------------------------------------------------------------------------
/panel/panel/templates/main.yaml:
--------------------------------------------------------------------------------
1 |
2 | mode: rule
3 |
4 | dns:
5 | enable: true
6 | listen: 0.0.0.0:53
7 | enhanced-mode: fake-ip
8 | fake-ip-range: 198.18.0.1/16
9 | nameserver:
10 | - 4.2.2.4
11 | - 8.8.8.8
12 |
13 | tun:
14 | enable: true
15 | stack: gvisor # or gvisor
16 | dns-hijack:
17 | - 198.18.0.2:53
18 | auto-route: true
19 | auto-detect-interface: true
20 |
21 | global-client-fingerprint: chrome
22 |
23 | proxy-providers:
24 | {% for tier in tiers %}
25 | {% if tier.exists %}
26 | {% for domain in domains %}
27 | tier{{tier.index}}-{{domain}}:
28 | type: http
29 | url: https://{{ domain }}/{{ user_id }}/tier{{tier.index}}.yaml
30 | path: ./{{ domain }}-files/tier{{tier.index}}.yaml
31 | interval: 3600
32 |
33 | {% if udp_exists %}
34 | tier{{tier.index}}-udp-{{domain}}:
35 | type: http
36 | url: https://{{ domain }}/{{ user_id }}/tier{{tier.index}}-udp.yaml
37 | path: ./{{ domain }}-files/tier{{tier.index}}-udp.yaml
38 | interval: 3600
39 | {% endif %}
40 | {% endfor %}
41 | {% endif %}
42 | {% endfor %}
43 |
44 | rule-providers:
45 | {% for domain in domains %}
46 | excludedips-{{ domain }}:
47 | type: http
48 | behavior: classical
49 | interval: 3600
50 | path: ./{{ domain }}-files/excludedips.yaml
51 | url: https://{{ domain }}/{{ user_id }}/rules.yaml
52 | {% endfor %}
53 |
54 |
55 | proxy-groups:
56 | - name: "main"
57 | type: select
58 | proxies:
59 | - auto-group
60 | {% if manual_tier_select_clash %}
61 | {% for tier in tiers %}{% if tier.exists %}
62 | - tier{{tier.index}}-group
63 | {% endif %}{% endfor %}
64 | {% endif %}
65 |
66 | - name: "auto-group"
67 | type: fallback
68 | proxies:
69 | {% for tier in tiers %}{% if tier.exists %}
70 | - tier{{tier.index}}-group
71 | {% endif %}{% endfor %}
72 | url: 'https://www.google.com/generate_204'
73 | interval: 5
74 | global-default: true
75 | lazy: false
76 |
77 | {% if udp_exists %}
78 | - name: "main-udp"
79 | type: fallback
80 | proxies:
81 | {% for tier in tiers %}{% if tier.exists %}
82 | - tier{{tier.index}}-group-udp
83 | {% endif %}{% endfor %}
84 | url: 'https://www.google.com/generate_204'
85 | interval: 5
86 | lazy: false
87 | {% endif %}
88 |
89 | {% for tier in tiers %}
90 | {% if tier.exists %}
91 | - name: "tier{{tier.index}}-group"
92 | use:
93 | {% for domain in domains %}
94 | - tier{{tier.index}}-{{ domain }}
95 | {% endfor %}
96 | {% if tier.type == 'urltest' %}
97 | type: url-test
98 | tolerance: 4000
99 | url: 'https://www.google.com/generate_204'
100 | interval: 30
101 | lazy: false
102 | {% endif %}
103 | {% if tier.type == 'loadbalance' %}
104 | url: 'https://www.google.com/generate_204'
105 | interval: 30
106 | strategy: round-robin
107 | lazy: false
108 | {% endif %}
109 | {% if tier.type == 'fallback' %}
110 | url: 'https://www.google.com/generate_204'
111 | interval: 30
112 | lazy: false
113 | {% endif %}
114 |
115 | {% if udp_exists %}
116 | - name: "tier{{tier.index}}-group-udp"
117 | use:
118 | {% for domain in domains %}
119 | - tier{{tier.index}}-udp-{{ domain }}
120 | {% endfor %}
121 | {% if tier.type == 'urltest' %}
122 | type: url-test
123 | tolerance: 4000
124 | url: 'https://www.google.com/generate_204'
125 | interval: 30
126 | lazy: false
127 | {% endif %}
128 | {% if tier.type == 'loadbalance' %}
129 | url: 'https://www.google.com/generate_204'
130 | interval: 30
131 | lazy: false
132 | {% endif %}
133 | {% if tier.type == 'fallback' %}
134 | url: 'https://www.google.com/generate_204'
135 | interval: 30
136 | lazy: false
137 | {% endif %}
138 | {% endif %}
139 |
140 | {% endif %}
141 | {% endfor %}
142 |
143 |
144 |
145 |
146 |
147 | mixed-port: 1096
148 | redir-port: 7892
149 | external-controller: 127.0.0.1:9090
150 |
151 | script:
152 | shortcuts:
153 | udpnetwork: network == 'udp'
154 |
155 | rules:
156 | {% for domain in domains %}
157 | - RULE-SET,excludedips-{{ domain }},DIRECT
158 | {% endfor %}
159 | {% if udp_exists %}
160 | {% if meta %}
161 | - AND,((NETWORK,UDP)),main-udp
162 | {% else %}
163 | {% if premium %}
164 | - SCRIPT,udpnetwork,main-udp
165 | {% endif %}
166 | {% endif %}
167 | {% endif %}
168 | - MATCH,main
169 |
--------------------------------------------------------------------------------
/panel/panel/templates/providers/servergroup.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VZiChoushaDui/Libertea/8eda816ae8f39ea0fc9be7f683108a9bd7ae7674/panel/panel/templates/providers/servergroup.yaml
--------------------------------------------------------------------------------
/panel/panel/templates/providers/ss-v2ray.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: ss
3 | server: {{server}}
4 | port: {{port}}
5 | cipher: chacha20-ietf-poly1305
6 | password: {{password}}
7 | plugin: v2ray-plugin
8 | plugin-opts:
9 | mode: websocket
10 | tls: true
11 | skip-cert-verify: {{skip_cert_verify}}
12 | host: {{host}}
13 | path: "{{path}}"
14 | mux: 4
15 |
--------------------------------------------------------------------------------
/panel/panel/templates/providers/trojan-grpc.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: trojan
3 | password: {{password}}
4 | port: {{port}}
5 | server: {{server}}
6 | network: grpc
7 | sni: {{sni}}
8 | skip-cert-verify: {{skip_cert_verify}}
9 | udp: true
10 | grpc-opts:
11 | grpc-service-name: "{{ path[1:] }}"
12 |
--------------------------------------------------------------------------------
/panel/panel/templates/providers/trojan-ws.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: trojan
3 | password: {{password}}
4 | port: {{port}}
5 | server: {{server}}
6 | network: ws
7 | sni: {{sni}}
8 | skip-cert-verify: {{skip_cert_verify}}
9 | udp: true
10 | ws-opts:
11 | path: "{{path}}"
12 | headers:
13 | Host: {{host}}
14 | max-early-data: 2048
15 | early-data-header-name: Sec-WebSocket-Protocol
16 |
--------------------------------------------------------------------------------
/panel/panel/templates/providers/vless-grpc.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: vless
3 | server: {{server}}
4 | port: {{port}}
5 | uuid: {{password}}
6 | alterId: 0
7 | cipher: none
8 | udp: true
9 | tls: true
10 | skip-cert-verify: {{skip_cert_verify}}
11 | #servername: test.com # priority over wss host
12 | network: grpc
13 | sni: {{sni}}
14 | grpc-opts:
15 | grpc-service-name: "{{ path[1:] }}"
16 |
--------------------------------------------------------------------------------
/panel/panel/templates/providers/vless-ws.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: vless
3 | server: {{server}}
4 | port: {{port}}
5 | uuid: {{password}}
6 | alterId: 0
7 | cipher: none
8 | udp: true
9 | tls: true
10 | skip-cert-verify: {{skip_cert_verify}}
11 | network: ws
12 | sni: {{sni}}
13 | ws-opts:
14 | path: "{{path}}"
15 | headers:
16 | Host: {{host}}
17 |
--------------------------------------------------------------------------------
/panel/panel/templates/providers/vmess-grpc.yaml:
--------------------------------------------------------------------------------
1 | - name: "{{name}}"
2 | type: vmess
3 | server: {{server}}
4 | port: {{port}}
5 | uuid: {{password}}
6 | alterId: 0
7 | cipher: auto
8 | udp: true
9 | tls: true
10 | skip-cert-verify: {{skip_cert_verify}}
11 | #servername: test.com # priority over wss host
12 | network: grpc
13 | sni: {{sni}}
14 | grpc-opts:
15 | grpc-service-name: "{{ path[1:] }}"
16 |
--------------------------------------------------------------------------------
/panel/panel/templates/rules.yaml:
--------------------------------------------------------------------------------
1 | payload:
2 | {% for country in ips_direct_countries %}
3 | {% include "rules/" + country + "-direct.yaml" %}
4 | {% endfor %}
5 |
--------------------------------------------------------------------------------
/panel/panel/templates/rules/cu-direct.yaml:
--------------------------------------------------------------------------------
1 | # cu rules
2 | - IP-CIDR,152.206.0.0/15,DIRECT
3 | - IP-CIDR,169.158.0.0/16,DIRECT
4 | - IP-CIDR,181.225.224.0/19,DIRECT
5 | - IP-CIDR,190.6.64.0/19,DIRECT
6 | - IP-CIDR,190.15.144.0/20,DIRECT
7 | - IP-CIDR,190.92.112.0/20,DIRECT
8 | - IP-CIDR,190.107.0.0/20,DIRECT
9 | - IP-CIDR,196.1.112.0/24,DIRECT
10 | - IP-CIDR,196.1.135.0/24,DIRECT
11 | - IP-CIDR,196.3.152.0/24,DIRECT
12 | - IP-CIDR,200.0.16.0/24,DIRECT
13 | - IP-CIDR,200.0.24.0/22,DIRECT
14 | - IP-CIDR,200.5.12.0/22,DIRECT
15 | - IP-CIDR,200.13.144.0/21,DIRECT
16 | - IP-CIDR,200.14.48.0/21,DIRECT
17 | - IP-CIDR,200.55.128.0/18,DIRECT
18 | - IP-CIDR,201.220.192.0/19,DIRECT
19 | - IP-CIDR,2001:1340::/32,DIRECT
20 | - IP-CIDR,2001:1358::/32,DIRECT
21 | - IP-CIDR,2001:13c8::/32,DIRECT
22 | - IP-CIDR,2800:230::/32,DIRECT
23 | - IP-CIDR,2800:360::/32,DIRECT
24 | - IP-CIDR,2800:910::/32,DIRECT
25 |
26 |
--------------------------------------------------------------------------------
/panel/panel/templates/rules/tm-direct.yaml:
--------------------------------------------------------------------------------
1 | # tm rules
2 | - IP-CIDR,77.83.59.0/24,DIRECT
3 | - IP-CIDR,95.85.96.0/19,DIRECT
4 | - IP-CIDR,103.220.0.0/22,DIRECT
5 | - IP-CIDR,119.235.112.0/20,DIRECT
6 | - IP-CIDR,177.93.143.0/24,DIRECT
7 | - IP-CIDR,185.69.184.0/22,DIRECT
8 | - IP-CIDR,185.246.72.0/22,DIRECT
9 | - IP-CIDR,216.250.8.0/21,DIRECT
10 | - IP-CIDR,217.174.224.0/20,DIRECT
11 | - IP-CIDR,2a05:2180::/29,DIRECT
12 | - IP-CIDR,2a14:340::/29,DIRECT
13 |
14 |
--------------------------------------------------------------------------------
/panel/panel/templates/tier-udp.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% if provider.type == "trojan-ws" %}
5 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
6 | {% include ("providers/" + provider.type + ".yaml") %}
7 | {% endwith %}
8 | {% endif %}
9 | {% endif %}
10 | {% endfor %}
11 |
--------------------------------------------------------------------------------
/panel/panel/templates/tier.yaml:
--------------------------------------------------------------------------------
1 | proxies:
2 | {% for provider in providers %}
3 | {% if not provider.meta_only or meta %}
4 | {% with name=provider.name, server=provider.server, port=provider.port, password=provider.password, sni=provider.sni, path=provider.path, host=provider.host, skip_cert_verify=provider.skip_cert_verify %}
5 | {% include ("providers/" + provider.type + ".yaml") %}
6 | {% endwith %}
7 | {% endif %}
8 | {% endfor %}
9 |
--------------------------------------------------------------------------------
/panel/panel/welcome.py:
--------------------------------------------------------------------------------
1 | import re
2 | import json
3 | import urllib
4 | import requests
5 | import threading
6 | from . import utils
7 | from . import stats
8 | from . import config
9 | from . import settings
10 | from . import health_check
11 | from . import admin
12 | from pymongo import MongoClient
13 | from datetime import datetime, timedelta
14 | from flask import Blueprint, render_template, redirect, url_for, flash, request
15 |
16 | blueprint = Blueprint('welcome', __name__)
17 |
18 | root_url = '/' + config.get_admin_uuid() + '/'
19 |
20 | @blueprint.route(root_url + 'welcome/')
21 | def welcome():
22 | first_user = utils.get_users()[0]
23 | setup_finished = settings.get_camouflage_domain() != ""
24 |
25 | return render_template('admin/welcome/welcome.jinja',
26 | admin_uuid=config.get_admin_uuid(),
27 | first_user_url = first_user['panel_id'],
28 | setup_finished = setup_finished,
29 | )
30 |
31 | @blueprint.route(root_url + 'welcome/routes/')
32 | def routes():
33 | return render_template('admin/welcome/routes.jinja',
34 | admin_uuid=config.get_admin_uuid(),
35 | panel_domain=config.get_panel_domain(),
36 | )
37 |
38 | @blueprint.route(root_url + 'welcome/routes/custom/', methods=['GET'])
39 | def routes_custom():
40 | return render_template('admin/welcome/routes-custom.jinja',
41 | admin_uuid=config.get_admin_uuid(),
42 | server_ip=config.SERVER_MAIN_IP,
43 | )
44 |
45 | @blueprint.route(root_url + 'welcome/routes/custom/', methods=['POST'])
46 | def routes_custom_post():
47 | domain = request.form.get('domain', None)
48 | if domain is None:
49 | return redirect(url_for('welcome.routes_custom'))
50 |
51 | domain = domain.strip()
52 | if domain.startswith('http://'):
53 | domain = domain[7:]
54 | if domain.startswith('https://'):
55 | domain = domain[8:]
56 | if domain.startswith('www.'):
57 | domain = domain[4:]
58 |
59 | if len(domain) == 0:
60 | return redirect(url_for('welcome.routes_custom'))
61 |
62 | utils.add_domain(domain)
63 | threading.Thread(target=utils.update_domain_cache, args=(domain, 2)).start()
64 |
65 | return redirect(url_for('welcome.routes2'))
66 |
67 | @blueprint.route(root_url + 'welcome/routes/default/')
68 | def routes_default():
69 | domain = config.get_panel_domain()
70 | utils.add_domain(domain)
71 | threading.Thread(target=utils.update_domain_cache, args=(domain, 2)).start()
72 |
73 | return redirect(url_for('welcome.routes2'))
74 |
75 | @blueprint.route(root_url + 'welcome/routes2/')
76 | def routes2():
77 | return render_template('admin/welcome/routes2.jinja',
78 | admin_uuid=config.get_admin_uuid(),
79 | )
80 |
81 | @blueprint.route(root_url + 'welcome/country/', methods=['GET'])
82 | def country():
83 | next_page = request.args.get('next_page', None)
84 |
85 | return render_template('admin/welcome/country.jinja',
86 | admin_uuid=config.get_admin_uuid(),
87 | route_direct_countries=config.ROUTE_IP_LISTS,
88 | route_direct_country_enabled={x['id']: settings.get_route_direct_country_enabled(x['id']) for x in config.ROUTE_IP_LISTS},
89 | next_page=next_page,
90 | )
91 |
92 | @blueprint.route(root_url + 'welcome/country/', methods=['POST'])
93 | def country_post():
94 | next_page = request.args.get('next_page', None)
95 |
96 | route_direct = {x['id']: request.form.get('route_direct_' + x['id'], None) for x in config.ROUTE_IP_LISTS}
97 | for x in config.ROUTE_IP_LISTS:
98 | settings.set_route_direct_country_enabled(x['id'], route_direct[x['id']] == 'on')
99 |
100 | if next_page == "security":
101 | return redirect(url_for('admin.security'))
102 |
103 | return redirect(url_for('welcome.camouflage'))
104 |
105 | @blueprint.route(root_url + 'welcome/camouflage/', methods=['GET'])
106 | def camouflage():
107 | next_page = request.args.get('next_page', None)
108 |
109 | camouflage_error = request.args.get('camouflage_error', None)
110 | camouflage_domain = request.args.get('camouflage_domain', None)
111 |
112 | if camouflage_domain is None:
113 | saved_camouflage_domain = settings.get_camouflage_domain()
114 | if saved_camouflage_domain is None or saved_camouflage_domain == "":
115 | camouflage_domain = "https://"
116 | else:
117 | camouflage_domain = saved_camouflage_domain
118 | camouflage_domain_status, camouflage_domain = utils.check_camouflage_domain(camouflage_domain)
119 | if camouflage_domain_status != "":
120 | camouflage_error = camouflage_domain_status
121 |
122 | return render_template('admin/welcome/camouflage.jinja',
123 | admin_uuid=config.get_admin_uuid(),
124 | camouflage_domain=camouflage_domain,
125 | camouflage_error=camouflage_error,
126 | next_page=next_page,
127 | )
128 |
129 | @blueprint.route(root_url + 'welcome/camouflage/', methods=['POST'])
130 | def camouflage_post():
131 | next_page = request.form.get('next_page', None)
132 |
133 | camouflage_domain = request.form.get('camouflage_domain', None)
134 | if camouflage_domain == '' or camouflage_domain == 'https://':
135 | settings.set_camouflage_domain("")
136 | else:
137 | if not camouflage_domain.startswith('http'):
138 | camouflage_domain = 'https://' + camouflage_domain
139 |
140 | # check if domain is reachable
141 | camouflage_domain_status, camouflage_domain = utils.check_camouflage_domain(camouflage_domain)
142 | if camouflage_domain_status == "":
143 | settings.set_camouflage_domain(camouflage_domain)
144 | else:
145 | return redirect(root_url + 'welcome/camouflage/?camouflage_error=' + camouflage_domain_status + '&camouflage_domain=' + urllib.parse.quote(camouflage_domain))
146 |
147 | if next_page == "security":
148 | return redirect(url_for('admin.security'))
149 | return redirect(url_for('welcome.finished'))
150 |
151 | @blueprint.route(root_url + 'welcome/finished/')
152 | def finished():
153 | return render_template('admin/welcome/finished.jinja',
154 | admin_uuid=config.get_admin_uuid(),
155 | )
156 |
--------------------------------------------------------------------------------
/panel/requirements.txt:
--------------------------------------------------------------------------------
1 | flask>=2.2
2 | waitress
3 | Paste
4 | pymongo
5 | jinja2>=3
6 | uwsgi
7 | requests
8 | psutil
9 | certbot
10 | pytz
11 |
--------------------------------------------------------------------------------
/panel/serve.py:
--------------------------------------------------------------------------------
1 | import uwsgi
2 | import logging
3 | from waitress import serve
4 | from panel import create_app
5 | from paste.translogger import TransLogger
6 |
7 | logger = logging.getLogger('waitress')
8 | logger.setLevel(logging.INFO)
9 |
10 | app = TransLogger(create_app(), setup_console_handler=False)
11 |
12 | if __name__ == '__main__':
13 | print('Starting server...')
14 | serve(app, host='127.0.0.1', port=1000, threads=10)
15 |
--------------------------------------------------------------------------------
/panel/serve.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | LOG_MAX_SIZE_MB=100
4 | UWSGI_THREADS=6
5 | PORT=1000
6 |
7 | function log {
8 | cur_date=$(date +"%Y-%m-%d %H:%M:%S")
9 | echo "[$cur_date] $1"
10 | echo "[$cur_date] $1" >> /tmp/libertea-panel.log
11 | echo "[$cur_date] $1" >> /tmp/libertea-panel.init.log
12 | }
13 |
14 | log "Libertea panel serve script"
15 |
16 | dir=$(pwd)
17 | log "Current directory: $dir"
18 |
19 | # check if /tmp/libertea-panel.log is bigger than LOG_MAX_SIZE_MB, if so, remove it
20 | if [ -f /tmp/libertea-panel.log ]; then
21 | log_size=$(du -m /tmp/libertea-panel.log | cut -f1)
22 | if [ $log_size -gt $LOG_MAX_SIZE_MB ]; then
23 | log "Removing /tmp/libertea-panel.log"
24 | rm /tmp/libertea-panel.log
25 | log "Removed /tmp/libertea-panel.log because it was bigger than $LOG_MAX_SIZE_MB MB"
26 | fi
27 | fi
28 |
29 | log "Checking total memory"
30 | TOTAL_MEM=$(free -m | grep Mem | awk '{print $2}')
31 | if [ $TOTAL_MEM -gt 6000 ]; then
32 | log "Total memory is greater than 6000 MB, setting uwsgi threads to 10"
33 | UWSGI_THREADS=10
34 | fi
35 |
36 | log "Tuning kernel parameters"
37 | ulimit -n 10000
38 | ulimit -n 100000
39 | log " - ulimit: $(ulimit -n)"
40 | sysctl -w net.core.somaxconn=65534
41 | log " - net.core.somaxconn: $(sysctl -n net.core.somaxconn)"
42 | sysctl -w net.ipv4.ip_local_port_range="16384 65535"
43 | log " - net.ipv4.ip_local_port_range: $(sysctl -n net.ipv4.ip_local_port_range)"
44 | sysctl -w net.netfilter.nf_conntrack_max=262144
45 | log " - net.netfilter.nf_conntrack_max: $(sysctl -n net.netfilter.nf_conntrack_max)"
46 | sysctl -w net.ipv4.tcp_fin_timeout=15
47 | log " - net.ipv4.tcp_fin_timeout: $(sysctl -n net.ipv4.tcp_fin_timeout)"
48 |
49 | SOMAXCONN=100
50 | SOMAXCONN=$(sysctl -n net.core.somaxconn)
51 | if ! [[ $SOMAXCONN =~ ^[0-9]+$ ]]; then
52 | SOMAXCONN=100
53 | fi
54 | if [ $SOMAXCONN -gt 512 ]; then
55 | SOMAXCONN=512
56 | else
57 | SOMAXCONN=$(($SOMAXCONN - 1))
58 | fi
59 |
60 | log "Checking total memory"
61 | TOTAL_MEM=$(free -m | grep Mem | awk '{print $2}')
62 | if [ $TOTAL_MEM -gt 3000 ]; then
63 | log "Total memory is greater than 3000 MB, setting uwsgi threads to 10"
64 | UWSGI_THREADS=10
65 | fi
66 |
67 | log "Starting Libertea panel via uwsgi on port $PORT with $UWSGI_THREADS threads, and listen queue size of $SOMAXCONN"
68 | uwsgi --disable-logging --master -p $UWSGI_THREADS --need-app --http 127.0.0.1:$PORT --listen $SOMAXCONN -w serve:app --post-buffering 1 >> /tmp/libertea-panel.log 2>&1
69 |
70 | log "Libertea panel serve script exited with code $?"
71 |
--------------------------------------------------------------------------------
/providers/shadowsocks-grpc/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM teddysun/xray:latest
2 |
3 | RUN apk update && apk add iptables
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 |
7 | RUN chmod +x /entrypoint.sh
8 |
9 | ENTRYPOINT ["/entrypoint.sh"]
10 |
--------------------------------------------------------------------------------
/providers/shadowsocks-grpc/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "loglevel": "warning"
4 | },
5 | "inbounds": [
6 | {
7 | "port": {port},
8 | "protocol": "shadowsocks",
9 | "settings": {
10 | "clients": [
11 | {
12 | "password":"{password}",
13 | "method": "chacha20-ietf-poly1305"
14 | }
15 | ]
16 | },
17 | "streamSettings": {
18 | "network": "grpc",
19 | "security": "none",
20 | "grpcSettings": {
21 | "serviceName": "{url}"
22 | }
23 | }
24 | }
25 | ],
26 | "outbounds": [
27 | {
28 | "protocol": "freedom"
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/providers/shadowsocks-grpc/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting Xray..."
37 | /usr/bin/xray -config /etc/xray/config.json
38 |
--------------------------------------------------------------------------------
/providers/shadowsocks-grpc/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.json ]; then
7 | rm config.json
8 | fi
9 |
10 | PORT="$1"
11 | URL="$2"
12 | PASSWORD="$3"
13 |
14 | if [ -z "$PORT" ] || [ -z "$URL" ] || [ -z "$PASSWORD" ]; then
15 | echo "Usage: $0 "
16 | exit 1
17 | fi
18 |
19 | # Create config.json from config.json.sample
20 | cp config.json.sample config.json
21 | sed -i "s|{port}|$PORT|g" config.json
22 | sed -i "s|{port-internal}|$PORT_INTERNAL|g" config.json
23 | sed -i "s|{url}|$URL|g" config.json
24 | sed -i "s|{password}|$PASSWORD|g" config.json
25 |
--------------------------------------------------------------------------------
/providers/shadowsocks-v2ray/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mazy/ss-xray:latest
2 |
3 | USER root
4 |
5 | RUN apk update && apk add iptables
6 |
7 | COPY entrypoint.sh /entrypoint.sh
8 |
9 | RUN chmod +x /entrypoint.sh
10 |
11 | ENTRYPOINT ["/entrypoint.sh"]
12 |
--------------------------------------------------------------------------------
/providers/shadowsocks-v2ray/config.env.sample:
--------------------------------------------------------------------------------
1 | SS_PASSWORD={password}
2 | SS_PLUGIN=xray-plugin
3 | SS_PLUGIN_OPTS="server;path=/{url}"
4 | SS_HOST=example.com
5 | SS_SERVER_ADDR=0.0.0.0:{port}
6 | SS_METHOD=chacha20-ietf-poly1305
7 | SS_ARGS=
8 |
--------------------------------------------------------------------------------
/providers/shadowsocks-v2ray/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting shadowsocks..."
37 |
38 | # load variables from config.env
39 | . /etc/xray/config.env
40 |
41 | ssservice server \
42 | -s $SS_SERVER_ADDR \
43 | -k $SS_PASSWORD \
44 | -m $SS_METHOD \
45 | --plugin $SS_PLUGIN \
46 | --plugin-opts "$SS_PLUGIN_OPTS" \
47 | $SS_ARGS
48 |
--------------------------------------------------------------------------------
/providers/shadowsocks-v2ray/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.env ]; then
7 | rm config.env
8 | fi
9 |
10 | PORT="$1"
11 | URL="$2"
12 | PASSWORD="$3"
13 |
14 | if [ -z "$PORT" ] || [ -z "$URL" ] || [ -z "$PASSWORD" ]; then
15 | echo "Usage: $0 "
16 | exit 1
17 | fi
18 |
19 | # Create config.json from config.json.sample
20 | cp config.env.sample config.env
21 | sed -i "s|{port}|$PORT|g" config.env
22 | sed -i "s|{url}|$URL|g" config.env
23 | sed -i "s|{password}|$PASSWORD|g" config.env
24 |
--------------------------------------------------------------------------------
/providers/trojan-grpc/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM teddysun/xray:latest
2 |
3 | RUN apk update && apk add iptables
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 |
7 | RUN chmod +x /entrypoint.sh
8 |
9 | ENTRYPOINT ["/entrypoint.sh"]
10 |
--------------------------------------------------------------------------------
/providers/trojan-grpc/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "loglevel": "warning"
4 | },
5 | "inbounds": [
6 | {
7 | "port": {port},
8 | "protocol": "trojan",
9 | "settings": {
10 | "clients": [
11 | {
12 | "password":"{password}"
13 | }
14 | ]
15 | },
16 | "streamSettings": {
17 | "network": "grpc",
18 | "security": "none",
19 | "grpcSettings": {
20 | "serviceName": "{url}"
21 | }
22 | }
23 | }
24 | ],
25 | "outbounds": [
26 | {
27 | "protocol": "freedom"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/providers/trojan-grpc/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting Xray..."
37 | /usr/bin/xray -config /etc/xray/config.json
38 |
--------------------------------------------------------------------------------
/providers/trojan-grpc/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.json ]; then
7 | rm config.json
8 | fi
9 |
10 | PORT="$1"
11 | URL="$2"
12 | PASSWORD="$3"
13 |
14 | if [ -z "$PORT" ] || [ -z "$URL" ] || [ -z "$PASSWORD" ]; then
15 | echo "Usage: $0 "
16 | exit 1
17 | fi
18 |
19 | # Create config.json from config.json.sample
20 | cp config.json.sample config.json
21 | sed -i "s|{port}|$PORT|g" config.json
22 | sed -i "s|{port-internal}|$PORT_INTERNAL|g" config.json
23 | sed -i "s|{url}|$URL|g" config.json
24 | sed -i "s|{password}|$PASSWORD|g" config.json
25 |
--------------------------------------------------------------------------------
/providers/trojan-ws/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM teddysun/xray:latest
2 |
3 | RUN apk update && apk add iptables
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 |
7 | RUN chmod +x /entrypoint.sh
8 |
9 | ENTRYPOINT ["/entrypoint.sh"]
10 |
--------------------------------------------------------------------------------
/providers/trojan-ws/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "loglevel": "warning"
4 | },
5 | "inbounds": [
6 | {
7 | "port": {port},
8 | "protocol": "trojan",
9 | "settings": {
10 | "clients": [
11 | {
12 | "password":"{password}"
13 | }
14 | ],
15 | "fallbacks": [
16 | {
17 | "path": "/{url}",
18 | "dest": {port-internal},
19 | "xver": 1
20 | }
21 | ]
22 | },
23 | "streamSettings": {
24 | "network": "tcp",
25 | "security": "tls",
26 | "tlsSettings": {
27 | "alpn": [
28 | "http/1.1"
29 | ],
30 | "certificates": [
31 | {
32 | "certificateFile": "/etc/xray/cert.pem",
33 | "keyFile": "/etc/xray/privkey.pem"
34 | }
35 | ]
36 | }
37 | }
38 | },
39 | {
40 | "port": {port-internal},
41 | "listen": "127.0.0.1",
42 | "protocol": "trojan",
43 | "settings": {
44 | "clients": [
45 | {
46 | "password":"{password}"
47 | }
48 | ]
49 | },
50 | "streamSettings": {
51 | "network": "ws",
52 | "security": "none",
53 | "wsSettings": {
54 | "acceptProxyProtocol": true,
55 | "path": "/{url}"
56 | }
57 | }
58 | }
59 | ],
60 | "outbounds": [
61 | {
62 | "protocol": "freedom"
63 | }
64 | ]
65 | }
66 |
--------------------------------------------------------------------------------
/providers/trojan-ws/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting Xray..."
37 | /usr/bin/xray -config /etc/xray/config.json
38 |
--------------------------------------------------------------------------------
/providers/trojan-ws/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.json ]; then
7 | rm config.json
8 | fi
9 |
10 | PORT="$1"
11 | PORT_INTERNAL="$2"
12 | URL="$3"
13 | PASSWORD="$4"
14 |
15 | if [ -z "$PORT" ] || [ -z "$PORT_INTERNAL" ] || [ -z "$URL" ] || [ -z "$PASSWORD" ]; then
16 | echo "Usage: $0 "
17 | exit 1
18 | fi
19 |
20 | # Create config.json from config.json.sample
21 | cp config.json.sample config.json
22 | sed -i "s|{port}|$PORT|g" config.json
23 | sed -i "s|{port-internal}|$PORT_INTERNAL|g" config.json
24 | sed -i "s|{url}|$URL|g" config.json
25 | sed -i "s|{password}|$PASSWORD|g" config.json
26 |
--------------------------------------------------------------------------------
/providers/vless-grpc/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM teddysun/xray:latest
2 |
3 | RUN apk update && apk add iptables
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 |
7 | RUN chmod +x /entrypoint.sh
8 |
9 | ENTRYPOINT ["/entrypoint.sh"]
10 |
--------------------------------------------------------------------------------
/providers/vless-grpc/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "loglevel": "warning"
4 | },
5 | "inbounds": [
6 | {
7 | "port": {port},
8 | "protocol": "vless",
9 | "settings": {
10 | "clients": [
11 | {
12 | "id": "{uuid}",
13 | "level": 0,
14 | "email": "me@example.com"
15 | }
16 | ],
17 | "decryption": "none"
18 | },
19 | "streamSettings": {
20 | "network": "grpc",
21 | "security": "none",
22 | "grpcSettings": {
23 | "serviceName": "{url}"
24 | }
25 | }
26 | }
27 | ],
28 | "outbounds": [
29 | {
30 | "protocol": "freedom"
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/providers/vless-grpc/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting Xray..."
37 | /usr/bin/xray -config /etc/xray/config.json
38 |
--------------------------------------------------------------------------------
/providers/vless-grpc/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.json ]; then
7 | rm config.json
8 | fi
9 |
10 | PORT="$1"
11 | URL="$2"
12 | UUID="$3"
13 |
14 | if [ -z "$PORT" ] || [ -z "$URL" ] || [ -z "$UUID" ]; then
15 | echo "Usage: $0 "
16 | exit 1
17 | fi
18 |
19 | # Create config.json from config.json.sample
20 | cp config.json.sample config.json
21 | sed -i "s|{port}|$PORT|g" config.json
22 | sed -i "s|{port-internal}|$PORT_INTERNAL|g" config.json
23 | sed -i "s|{url}|$URL|g" config.json
24 | sed -i "s|{uuid}|$UUID|g" config.json
25 |
--------------------------------------------------------------------------------
/providers/vless-ws/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM teddysun/xray:latest
2 |
3 | RUN apk update && apk add iptables
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 |
7 | RUN chmod +x /entrypoint.sh
8 |
9 | ENTRYPOINT ["/entrypoint.sh"]
10 |
--------------------------------------------------------------------------------
/providers/vless-ws/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "loglevel": "warning"
4 | },
5 | "inbounds": [
6 | {
7 | "port": {port},
8 | "protocol": "vless",
9 | "settings": {
10 | "clients": [
11 | {
12 | "id": "{uuid}",
13 | "level": 0,
14 | "email": "me@example.com"
15 | }
16 | ],
17 | "decryption": "none",
18 | "fallbacks": [
19 | {
20 | "path": "/{url}",
21 | "dest": {port-internal},
22 | "xver": 1
23 | }
24 | ]
25 | },
26 | "streamSettings": {
27 | "network": "tcp",
28 | "security": "tls",
29 | "tlsSettings": {
30 | "alpn": [
31 | "http/1.1"
32 | ],
33 | "certificates": [
34 | {
35 | "certificateFile": "/etc/xray/cert.pem",
36 | "keyFile": "/etc/xray/privkey.pem"
37 | }
38 | ]
39 | }
40 | }
41 | },
42 | {
43 | "port": {port-internal},
44 | "listen": "127.0.0.1",
45 | "protocol": "vless",
46 | "settings": {
47 | "clients": [
48 | {
49 | "id": "{uuid}",
50 | "level": 0,
51 | "email": "me@example.com"
52 | }
53 | ],
54 | "decryption": "none"
55 | },
56 | "streamSettings": {
57 | "network": "ws",
58 | "security": "none",
59 | "wsSettings": {
60 | "acceptProxyProtocol": true,
61 | "path": "/{url}"
62 | }
63 | }
64 | }
65 | ],
66 | "outbounds": [
67 | {
68 | "protocol": "freedom"
69 | }
70 | ]
71 | }
72 |
--------------------------------------------------------------------------------
/providers/vless-ws/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting Xray..."
37 | /usr/bin/xray -config /etc/xray/config.json
38 |
--------------------------------------------------------------------------------
/providers/vless-ws/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.json ]; then
7 | rm config.json
8 | fi
9 |
10 | PORT="$1"
11 | PORT_INTERNAL="$2"
12 | URL="$3"
13 | UUID="$4"
14 |
15 | if [ -z "$PORT" ] || [ -z "$PORT_INTERNAL" ] || [ -z "$URL" ] || [ -z "$UUID" ]; then
16 | echo "Usage: $0 "
17 | exit 1
18 | fi
19 |
20 | # Create config.json from config.json.sample
21 | cp config.json.sample config.json
22 | sed -i "s|{port}|$PORT|g" config.json
23 | sed -i "s|{port-internal}|$PORT_INTERNAL|g" config.json
24 | sed -i "s|{url}|$URL|g" config.json
25 | sed -i "s|{uuid}|$UUID|g" config.json
26 |
--------------------------------------------------------------------------------
/providers/vmess-grpc/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM teddysun/xray:latest
2 |
3 | RUN apk update && apk add iptables
4 |
5 | COPY entrypoint.sh /entrypoint.sh
6 |
7 | RUN chmod +x /entrypoint.sh
8 |
9 | ENTRYPOINT ["/entrypoint.sh"]
10 |
--------------------------------------------------------------------------------
/providers/vmess-grpc/config.json.sample:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "loglevel": "warning"
4 | },
5 | "inbounds": [
6 | {
7 | "port": {port},
8 | "protocol": "vmess",
9 | "settings": {
10 | "clients": [
11 | {
12 | "id": "{uuid}",
13 | "level": 0,
14 | "email": "me@example.com"
15 | }
16 | ],
17 | "decryption": "none"
18 | },
19 | "streamSettings": {
20 | "network": "grpc",
21 | "security": "none",
22 | "grpcSettings": {
23 | "serviceName": "{url}"
24 | }
25 | }
26 | }
27 | ],
28 | "outbounds": [
29 | {
30 | "protocol": "freedom"
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/providers/vmess-grpc/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # disable all outbound connections except common ones, to prevent abuse
4 | echo " ** Setting up outbound firewall..."
5 |
6 | # get docker interface name
7 | DOCKER_INTERFACE=$(ip route | grep default | awk '{print $5}')
8 | echo " ** Docker interface: $DOCKER_INTERFACE"
9 |
10 | # NOTE: -I adds the rule to the top of the chain
11 |
12 | # drop all outbound connections (tcp udp)
13 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport 0:16384 -j DROP
14 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport 0:16384 -j DROP
15 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
16 | # iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp -j LOG --log-prefix "XRAY DROPPED: " --log-level 6
17 |
18 | # except established connections and allowed ports
19 | iptables -I OUTPUT -o $DOCKER_INTERFACE -m state --state ESTABLISHED,RELATED -j ACCEPT
20 |
21 | for port in $(echo $FIREWALL_OUTBOUND_TCP_PORTS | tr "," "
22 |
23 | "); do
24 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p tcp --dport $port -j ACCEPT
25 | echo " - TCP port $port"
26 | done
27 | for port in $(echo $FIREWALL_OUTBOUND_UDP_PORTS | tr "," "
28 |
29 | "); do
30 | iptables -I OUTPUT -o $DOCKER_INTERFACE -p udp --dport $port -j ACCEPT
31 | echo " - UDP port $port"
32 | done
33 |
34 |
35 | # run xray
36 | echo " ** Starting Xray..."
37 | /usr/bin/xray -config /etc/xray/config.json
38 |
--------------------------------------------------------------------------------
/providers/vmess-grpc/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
4 | cd "$DIR"
5 |
6 | if [ -f config.json ]; then
7 | rm config.json
8 | fi
9 |
10 | PORT="$1"
11 | URL="$2"
12 | UUID="$3"
13 |
14 | if [ -z "$PORT" ] || [ -z "$URL" ] || [ -z "$UUID" ]; then
15 | echo "Usage: $0 "
16 | exit 1
17 | fi
18 |
19 | # Create config.json from config.json.sample
20 | cp config.json.sample config.json
21 | sed -i "s|{port}|$PORT|g" config.json
22 | sed -i "s|{port-internal}|$PORT_INTERNAL|g" config.json
23 | sed -i "s|{url}|$URL|g" config.json
24 | sed -i "s|{uuid}|$UUID|g" config.json
25 |
--------------------------------------------------------------------------------
/proxy-docker-compose.dev.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | proxy-register:
4 | environment:
5 | - PROXY_REGISTER_ENDPOINT=${PROXY_REGISTER_ENDPOINT}
6 | - PANEL_SECRET_KEY=${PANEL_SECRET_KEY}
7 | build: ./proxy-register
8 | container_name: libertea-proxy-register
9 | restart: always
10 | logging:
11 | options:
12 | max-size: 10m
13 | haproxy:
14 | build: ./proxy-haproxy
15 | container_name: libertea-proxy-haproxy
16 | environment:
17 | - CONN_PROXY_IP=${CONN_PROXY_IP}
18 | volumes:
19 | - ./proxy-haproxy:/usr/local/etc/haproxy/
20 | - /etc/ssl/ha-certs:/etc/ssl/ha-certs
21 | restart: always
22 | network_mode: host
23 | logging:
24 | options:
25 | max-size: 10m
26 | asym-traffic:
27 | build: ./proxy-fake-traffic
28 | container_name: libertea-asym-traffic
29 | restart: always
30 | environment:
31 | - CONN_PROXY_IP=${CONN_PROXY_IP}
32 | logging:
33 | options:
34 | max-size: 10m
35 |
--------------------------------------------------------------------------------
/proxy-docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | proxy-register:
4 | environment:
5 | - PROXY_REGISTER_ENDPOINT=${PROXY_REGISTER_ENDPOINT}
6 | - PANEL_SECRET_KEY=${PANEL_SECRET_KEY}
7 | image: libertea/proxy-register
8 | container_name: libertea-proxy-register
9 | restart: always
10 | logging:
11 | options:
12 | max-size: 10m
13 | haproxy:
14 | image: libertea/proxy-haproxy
15 | container_name: libertea-proxy-haproxy
16 | environment:
17 | - CONN_PROXY_IP=${CONN_PROXY_IP}
18 | volumes:
19 | - ./proxy-haproxy:/usr/local/etc/haproxy/
20 | - /etc/ssl/ha-certs:/etc/ssl/ha-certs
21 | restart: always
22 | network_mode: host
23 | logging:
24 | options:
25 | max-size: 10m
26 | asym-traffic:
27 | image: libertea/proxy-fake-traffic
28 | container_name: libertea-asym-traffic
29 | restart: always
30 | environment:
31 | - CONN_PROXY_IP=${CONN_PROXY_IP}
32 | logging:
33 | options:
34 | max-size: 10m
35 |
--------------------------------------------------------------------------------
/proxy-fake-traffic/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 |
3 | RUN apt-get update && apt-get install -y \
4 | curl \
5 | netcat \
6 | bc
7 |
8 | COPY ./generate-traffic.sh /generate-traffic.sh
9 |
10 | RUN chmod +x /generate-traffic.sh
11 |
12 | ENTRYPOINT ["/bin/bash", "/generate-traffic.sh"]
13 |
--------------------------------------------------------------------------------
/proxy-fake-traffic/generate-traffic.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ### DEPRECATED ###
4 |
5 |
6 | # get country code
7 | COUNTRY_CODE=$(curl -s --max-time 3 https://ifconfig.io/country_code)
8 | if [ -z "$COUNTRY_CODE" ]; then
9 | COUNTRY_CODE=$(curl -s --max-time 3 https://ipapi.co/country_code)
10 | fi
11 | if [ -z "$COUNTRY_CODE" ]; then
12 | COUNTRY_CODE=$(curl -s --max-time 3 https://ipinfo.io/country)
13 | fi
14 | if [ -z "$COUNTRY_CODE" ]; then
15 | COUNTRY_CODE=$(curl -s --max-time 3 https://ipapi.co/country_code)
16 | fi
17 | if [ -z "$COUNTRY_CODE" ]; then
18 | echo "Could not get country code. Will generate fake traffic."
19 | COUNTRY_CODE="CN"
20 | fi
21 |
22 | countries=("CN" "RU" "CU" "TH" "TM" "IR" "SY" "SA" "TR")
23 |
24 | # check if country code is in the list
25 | if [[ " ${countries[@]} " =~ " ${COUNTRY_CODE} " ]]; then
26 | echo "Country code $COUNTRY_CODE is in the list. Will generate fake traffic."
27 | else
28 | echo "Country code $COUNTRY_CODE is not in the list."
29 | while true; do
30 | sleep 1
31 | done
32 | exit 1
33 | fi
34 |
35 |
36 | # generate fake traffic to $CONN_PROXY_IP port 443 so the traffic shape is not symmetric
37 | while true; do
38 |
39 | for i in {1..30}; do
40 |
41 | rand=$(od -N 4 -t uL -An /dev/urandom | tr -d " ")
42 | RANDOM_SIZE=$(( ( $rand % 100000 ) + 1000 ))
43 | RANDOM_STRING=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $RANDOM_SIZE | head -n 1)
44 |
45 | RANDOM_STRING_LEN=${#RANDOM_STRING}
46 | # echo "Sending $RANDOM_STRING_LEN bytes of random data to $CONN_PROXY_IP:443"
47 |
48 | # send random data to $CONN_PROXY_IP port 443
49 | echo "$RANDOM_STRING" | nc -w 1 $CONN_PROXY_IP 443
50 | done
51 |
52 | SLEEP_MS=$(( 10 * (( $rand % 300 ) + 1 ) ))
53 | SLEEP_S=$(echo "scale=2; $SLEEP_MS / 1000" | bc)
54 | echo "Sleeping for $SLEEP_S seconds"
55 | sleep $SLEEP_S
56 | done
57 |
--------------------------------------------------------------------------------
/proxy-fake-traffic/libertea-proxy-fake-traffic.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=LiberteaProxyFakeTraffic
3 | After=multi-user.target
4 | Conflicts=getty@tty1.service
5 |
6 | [Service]
7 | User=root
8 | EnvironmentFile={rootpath}/.env
9 | WorkingDirectory={rootpath}/proxy-fake-traffic
10 | Type=simple
11 | ExecStart=/bin/bash -c '/usr/bin/python3 -u {rootpath}/proxy-fake-traffic/main.py >> /tmp/libertea-proxy-fake-traffic.log 2>&1'
12 | Restart=always
13 | RestartSec=10
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------
/proxy-fake-traffic/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import json
4 | import time
5 | import random
6 | import socket
7 | import requests
8 | from datetime import datetime
9 | from urllib3.exceptions import InsecureRequestWarning
10 |
11 | FAKE_TRAFFIC_AVERAGE_GIGABYTES_PER_DAY = 10
12 | FAKE_TRAFFIC_AVERAGE_DELAY_BETWEEN_REQUESTS = 0.25
13 | FAKE_TRAFFIC_MIN_DELAY_BETWEEN_REQUESTS = 0.01
14 | LOG_LEVEL = 3
15 |
16 | FAKE_TRAFFIC_COUNTRIES_LIST = ["CN", "CU", "TH", "TM", "IR", "SY", "SA", "TR"]
17 |
18 | def get_fake_traffic_average_bytes_per_second():
19 | return FAKE_TRAFFIC_AVERAGE_GIGABYTES_PER_DAY * 1024 * 1024 * 1024 / (24 * 60 * 60)
20 |
21 | def get_fake_traffic_average_bytes_per_request():
22 | return get_fake_traffic_average_bytes_per_second() * FAKE_TRAFFIC_AVERAGE_DELAY_BETWEEN_REQUESTS
23 |
24 |
25 | # Force ipv4 for requests
26 | old_getaddrinfo = socket.getaddrinfo
27 | def new_getaddrinfo(*args, **kwargs):
28 | responses = old_getaddrinfo(*args, **kwargs)
29 | return [response
30 | for response in responses
31 | if response[0] == socket.AF_INET]
32 | socket.getaddrinfo = new_getaddrinfo
33 |
34 | def get_ip_api_url():
35 | return random.choice([
36 | 'https://ifconfig.io/country_code',
37 | 'https://ipinfo.io/country',
38 | 'https://ipapi.co/country_code',
39 | 'https://ifconfig.co/country-iso',
40 | 'https://api.myip.com',
41 | 'https://api.ipregistry.co/?key=tryout'
42 | ])
43 |
44 | def get_country_code():
45 | try:
46 | url = get_ip_api_url()
47 | result = requests.get(url, timeout=20).content.decode('utf8').strip()
48 | if url == 'https://api.myip.com':
49 | result = json.loads(result)['cc']
50 | elif url == 'https://api.ipregistry.co/?key=tryout':
51 | result = json.loads(result)['location']['country']['code']
52 | return result
53 | except Exception as e:
54 | print(e)
55 | return ''
56 |
57 | while True:
58 | SERVER_COUNTRY = get_country_code()
59 | if not len(SERVER_COUNTRY) == 2:
60 | raise Exception("couldn't fetch SERVER_MAIN_COUNTRY. Result was: " + str(SERVER_COUNTRY))
61 | if not SERVER_COUNTRY in FAKE_TRAFFIC_COUNTRIES_LIST:
62 | print("SERVER_COUNTRY", SERVER_COUNTRY, "not in countries list. Will not send fake traffic.")
63 | time.sleep(3 * 3600)
64 | else:
65 | break
66 |
67 | FAKE_TRAFFIC_ENDPOINT = os.environ.get('PROXY_REGISTER_ENDPOINT') + '/fake-traffic'
68 | FAKE_TRAFFIC_ENDPOINT = FAKE_TRAFFIC_ENDPOINT.replace(os.environ.get('CONN_PROXY_IP'), '127.0.0.1')
69 |
70 | def get_fake_traffic_random_data_size_bytes():
71 | # generate expovariate random data size, with a bias towards smaller sizes, with min size of 1 byte and average size of get_average_bytes_per_request()
72 | return max(1, int(random.expovariate(1 / get_fake_traffic_average_bytes_per_request())))
73 |
74 | def get_fake_traffic_sleep_time_secs():
75 | return FAKE_TRAFFIC_MIN_DELAY_BETWEEN_REQUESTS + random.random() * (FAKE_TRAFFIC_AVERAGE_DELAY_BETWEEN_REQUESTS * 2 - FAKE_TRAFFIC_MIN_DELAY_BETWEEN_REQUESTS)
76 |
77 | def get_fake_traffic_random_data():
78 | return os.urandom(get_fake_traffic_random_data_size_bytes())
79 |
80 | def fake_traffic_random_sleep(request_time_elapsed_secs):
81 | sleep_time_secs = get_fake_traffic_sleep_time_secs() - request_time_elapsed_secs
82 | if sleep_time_secs < FAKE_TRAFFIC_MIN_DELAY_BETWEEN_REQUESTS:
83 | sleep_time_secs = FAKE_TRAFFIC_MIN_DELAY_BETWEEN_REQUESTS
84 |
85 | if LOG_LEVEL >= 3:
86 | print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "Sleeping for", sleep_time_secs, "seconds")
87 | time.sleep(sleep_time_secs)
88 |
89 | start_time = datetime.now()
90 | total_len = 0
91 | while True:
92 | req_time_start = datetime.now()
93 |
94 | try:
95 | # generate random data
96 | data = get_fake_traffic_random_data()
97 |
98 | if LOG_LEVEL >= 2:
99 | total_len += len(data)
100 |
101 | if LOG_LEVEL >= 3:
102 | print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "Sending", len(data), "bytes to", FAKE_TRAFFIC_ENDPOINT)
103 | result = requests.post(FAKE_TRAFFIC_ENDPOINT,
104 | verify=False, timeout=5,
105 | data=data,
106 | headers={
107 | 'Content-Type': 'application/octet-stream',
108 | },
109 | )
110 |
111 | except Exception as e:
112 | if LOG_LEVEL >= 1:
113 | print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), e)
114 |
115 | req_time_end = datetime.now()
116 |
117 | fake_traffic_random_sleep((req_time_end - req_time_start).total_seconds())
118 |
119 | if LOG_LEVEL >= 2:
120 | avg_per_second = total_len / (datetime.now() - start_time).total_seconds()
121 | avg_gb_per_day = avg_per_second * 60 * 60 * 24 / (1024 * 1024 * 1024)
122 | avg_per_second = int(avg_per_second)
123 | avg_gb_per_day = round(avg_gb_per_day, 2)
124 | print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "Total sent:", total_len, "bytes in ", (datetime.now() - start_time).total_seconds(), "seconds")
125 | print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "Average:", avg_per_second, "bytes per second, or", avg_gb_per_day, "GB per day")
126 |
127 |
--------------------------------------------------------------------------------
/proxy-haproxy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM haproxy:2.7
2 |
3 | USER root
4 |
5 | RUN apt-get update
6 | RUN apt-get install libcap2-bin -y
7 | RUN setcap 'cap_net_bind_service=+ep' /usr/local/sbin/haproxy
8 |
9 | # RUN mkdir -p /home/haproxy_stats
10 | # RUN chown -R haproxy:haproxy /home/haproxy_stats
11 |
--------------------------------------------------------------------------------
/proxy-haproxy/haproxy.https.cfg:
--------------------------------------------------------------------------------
1 | global
2 | daemon
3 | maxconn 100000
4 | log 127.0.0.1:514 local0
5 | # lua-load /haproxy-files/script.lua
6 | # stats socket /home/haproxy_stats/stats
7 |
8 | defaults
9 | mode http
10 | log global
11 | option httplog
12 | option dontlognull
13 | option http-server-close
14 | timeout http-request 300s
15 | timeout queue 5s
16 | timeout connect 10s
17 | timeout client 300s
18 | timeout server 300s
19 | timeout http-keep-alive 60s
20 | timeout check 10s
21 |
22 | log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %U"
23 |
24 | # Main HTTPS frontend
25 | frontend main
26 | bind *:80 ssl crt /etc/ssl/ha-certs/ alpn h2,http/1.1
27 | bind *:443 ssl crt /etc/ssl/ha-certs/ alpn h2,http/1.1
28 |
29 | acl is_h2 req.hdr(Upgrade) -m found -i h2c
30 | use_backend h2 if is_h2
31 | use_backend default-backend
32 |
33 | backend h2
34 | option forwardfor
35 | server default "${CONN_PROXY_IP}:443" ssl verify none proto h2
36 |
37 | backend default-backend
38 | option forwardfor
39 | server default "${CONN_PROXY_IP}:443" ssl verify none
40 |
41 |
--------------------------------------------------------------------------------
/proxy-haproxy/haproxy.ssh.cfg:
--------------------------------------------------------------------------------
1 | global
2 | daemon
3 | maxconn 100000
4 | log 127.0.0.1:10514 local0
5 |
6 | defaults
7 | mode tcp
8 | log global
9 | option tcplog
10 | option dontlognull
11 | timeout http-request 300s
12 | timeout queue 5s
13 | timeout connect 10s
14 | timeout client 300s
15 | timeout server 300s
16 | timeout http-keep-alive 60s
17 | timeout check 10s
18 |
19 | frontend port80
20 | bind *:80
21 | mode tcp
22 |
23 | # %B is server to user bytes, %U is user to server bytes
24 | log-format "LIBERTEA_PROXY:80 %ci:%cp [%t] recv=%B sent=%U %Tw/%Tc/%Tt"
25 |
26 | tcp-request inspect-delay 50ms
27 | tcp-request content accept if HTTP
28 | tcp-request content accept if { req.ssl_hello_type 1 }
29 |
30 | use_backend forward_http if HTTP
31 | default_backend default-backend
32 |
33 | frontend redirtohttps
34 | bind *:81
35 | mode http
36 |
37 | http-request redirect scheme https if !{ ssl_fc }
38 |
39 | backend forward_http
40 | server s1 127.0.0.1:81
41 |
42 | frontend port443
43 | bind *:443
44 | mode tcp
45 |
46 | log-format "LIBERTEA_PROXY:443 %ci:%cp [%t] recv=%B sent=%U %Tw/%Tc/%Tt"
47 |
48 | default_backend default-backend
49 |
50 | frontend port8443
51 | bind *:8443 accept-proxy
52 | mode tcp
53 |
54 | log-format "LIBERTEA_PROXY:8443 %ci:%cp [%t] recv=%B sent=%U %Tw/%Tc/%Tt"
55 |
56 | default_backend default-backend
57 |
58 |
59 | backend default-backend
60 | balance roundrobin
61 | server s1 "127.0.0.1:10001" check send-proxy-v2
62 | server s2 "127.0.0.1:10002" check send-proxy-v2
63 | server s3 "127.0.0.1:10003" check send-proxy-v2
64 | server s4 "127.0.0.1:10004" check send-proxy-v2
65 |
--------------------------------------------------------------------------------
/proxy-haproxy/haproxy.tcp.cfg:
--------------------------------------------------------------------------------
1 | global
2 | daemon
3 | maxconn 100000
4 | log 127.0.0.1:10514 local0
5 |
6 | defaults
7 | mode tcp
8 | log global
9 | option tcplog
10 | option dontlognull
11 | timeout http-request 300s
12 | timeout queue 5s
13 | timeout connect 10s
14 | timeout client 300s
15 | timeout server 300s
16 | timeout http-keep-alive 60s
17 | timeout check 10s
18 |
19 | frontend port80
20 | bind *:80
21 | mode tcp
22 |
23 | # %B is server to user bytes, %U is user to server bytes
24 | log-format "LIBERTEA_PROXY:80 %ci:%cp [%t] recv=%B sent=%U %Tw/%Tc/%Tt"
25 |
26 | tcp-request inspect-delay 50ms
27 | tcp-request content accept if HTTP
28 | tcp-request content accept if { req.ssl_hello_type 1 }
29 |
30 | use_backend forward_http if HTTP
31 | default_backend proxied-backend
32 |
33 | frontend redirtohttps
34 | bind *:81
35 | mode http
36 |
37 | http-request redirect scheme https if !{ ssl_fc }
38 |
39 | backend forward_http
40 | server s1 127.0.0.1:81
41 |
42 | frontend port443
43 | bind *:443
44 | mode tcp
45 |
46 | log-format "LIBERTEA_PROXY:443 %ci:%cp [%t] recv=%B sent=%U %Tw/%Tc/%Tt"
47 |
48 | default_backend proxied-backend
49 |
50 | frontend port8443
51 | bind *:8443 accept-proxy
52 | mode tcp
53 |
54 | log-format "LIBERTEA_PROXY:8443 %ci:%cp [%t] recv=%B sent=%U %Tw/%Tc/%Tt"
55 |
56 | default_backend proxied-backend
57 |
58 | backend proxied-backend
59 | server s1 "${CONN_PROXY_IP}:8443" send-proxy-v2
60 |
61 | backend default-backend
62 | server s1 "${CONN_PROXY_IP}:443"
63 |
--------------------------------------------------------------------------------
/proxy-register/Dockerfile:
--------------------------------------------------------------------------------
1 | from python:3.8-alpine
2 |
3 | WORKDIR /app
4 | COPY . .
5 |
6 | RUN apk add --no-cache gcc python3-dev musl-dev linux-headers
7 |
8 | RUN pip install -r requirements.txt
9 |
10 | ENV PYTHONUNBUFFERED=0
11 |
12 | CMD ["python", "-u", "main.py"]
13 |
--------------------------------------------------------------------------------
/proxy-register/libertea-proxy-register.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=LiberteaProxyRegister
3 | After=multi-user.target
4 | Conflicts=getty@tty1.service
5 |
6 | [Service]
7 | User=root
8 | EnvironmentFile={rootpath}/.env
9 | WorkingDirectory={rootpath}/proxy-register
10 | Type=simple
11 | ExecStart=/bin/bash -c '/usr/bin/python3 -u {rootpath}/proxy-register/main.py >> /tmp/libertea-proxy-register.log'
12 | Restart=always
13 | RestartSec=10
14 |
15 | [Install]
16 | WantedBy=multi-user.target
17 |
--------------------------------------------------------------------------------
/proxy-register/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 | psutil
3 |
--------------------------------------------------------------------------------
/proxy-ssh-tunnel/install-services.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | TARGET_IP="$1"
4 | TARGET_PORT="$2"
5 | TARGET_USER="$3"
6 | LOCAL_PORT_START="$4"
7 | CONNECTIONS_COUNT="$5"
8 | SERVICE_POSTFIX="$6"
9 |
10 | DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
11 | cd "$DIR"
12 |
13 | # Install CONNECTIONS_COUNT variants of systemd service
14 |
15 | for i in $(seq 0 $(($CONNECTIONS_COUNT - 1))); do
16 | LOCALPORT=$(($LOCAL_PORT_START + $i))
17 |
18 | cp template.service /etc/systemd/system/libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
19 |
20 | sed -i "s/LOCALPORT/$LOCALPORT/g" /etc/systemd/system/libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
21 | sed -i "s/TARGETIP/$TARGET_IP/g" /etc/systemd/system/libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
22 | sed -i "s/TARGETPORT/$TARGET_PORT/g" /etc/systemd/system/libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
23 | sed -i "s/TARGETUSER/$TARGET_USER/g" /etc/systemd/system/libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
24 |
25 | systemctl daemon-reload
26 | systemctl enable libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
27 | systemctl restart libertea-proxy-ssh-tunnel-$i$SERVICE_POSTFIX.service
28 | done
29 |
--------------------------------------------------------------------------------
/proxy-ssh-tunnel/template.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Libertea SSH Tunnel
3 | After=network.target
4 |
5 | [Service]
6 | ExecStart=/usr/bin/autossh -NT -o ServerAliveInterval=1 -o ExitOnForwardFailure=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -L LOCALPORT:TARGETIP:TARGETPORT TARGETUSER@TARGETIP
7 | RestartSec=5
8 | Restart=always
9 |
10 | [Install]
11 | WantedBy=multi-user.target
12 |
--------------------------------------------------------------------------------
/push-docker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | version=`cat version.txt`
6 |
7 | # build {image} and push it to libertea:{image}:version on docker hub
8 |
9 | # ./haproxy
10 | echo " *** Building and pushing libertea/haproxy:$version *** "
11 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/haproxy:$version ./haproxy --push
12 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/haproxy:latest ./haproxy --push
13 |
14 | # ./syslog
15 | echo " *** Building and pushing libertea/syslog:$version *** "
16 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/syslog:$version ./syslog --push
17 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/syslog:latest ./syslog --push
18 |
19 | # ./log-parser
20 | echo " *** Building and pushing libertea/log-parser:$version *** "
21 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/log-parser:$version ./log-parser --push
22 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/log-parser:latest ./log-parser --push
23 |
24 | # providers/trojan-ws
25 | echo " *** Building and pushing libertea/provider-trojan-ws:$version *** "
26 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-trojan-ws:$version ./providers/trojan-ws --push
27 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-trojan-ws:latest ./providers/trojan-ws --push
28 |
29 | # providers/trojan-grpc
30 | echo " *** Building and pushing libertea/provider-trojan-grpc:$version *** "
31 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-trojan-grpc:$version ./providers/trojan-grpc --push
32 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-trojan-grpc:latest ./providers/trojan-grpc --push
33 |
34 | # providers/vless-ws
35 | echo " *** Building and pushing libertea/provider-vless-ws:$version *** "
36 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-vless-ws:$version ./providers/vless-ws --push
37 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-vless-ws:latest ./providers/vless-ws --push
38 |
39 | # providers/vless-grpc
40 | echo " *** Building and pushing libertea/provider-vless-grpc:$version *** "
41 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-vless-grpc:$version ./providers/vless-grpc --push
42 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-vless-grpc:latest ./providers/vless-grpc --push
43 |
44 | # providers/vmess-grpc
45 | echo " *** Building and pushing libertea/provider-vmess-grpc:$version *** "
46 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-vmess-grpc:$version ./providers/vmess-grpc --push
47 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-vmess-grpc:latest ./providers/vmess-grpc --push
48 |
49 | # providers/shadowsocks-v2ray
50 | echo " *** Building and pushing libertea/provider-shadowsocks-v2ray:$version *** "
51 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-shadowsocks-v2ray:$version ./providers/shadowsocks-v2ray --push
52 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/provider-shadowsocks-v2ray:latest ./providers/shadowsocks-v2ray --push
53 |
54 | # ./proxy-register
55 | echo " *** Building and pushing libertea/proxy-register:$version *** "
56 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/proxy-register:$version ./proxy-register --push
57 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/proxy-register:latest ./proxy-register --push
58 |
59 | # ./proxy-haproxy
60 | echo " *** Building and pushing libertea/proxy-haproxy:$version *** "
61 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/proxy-haproxy:$version ./proxy-haproxy --push
62 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/proxy-haproxy:latest ./proxy-haproxy --push
63 |
64 | # ./proxy-fake-traffic
65 | echo " *** Building and pushing libertea/proxy-fake-traffic:$version *** "
66 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/proxy-fake-traffic:$version ./proxy-fake-traffic --push
67 | docker buildx build --platform linux/amd64,linux/arm64 -t libertea/proxy-fake-traffic:latest ./proxy-fake-traffic --push
68 |
--------------------------------------------------------------------------------
/sample.env:
--------------------------------------------------------------------------------
1 | # Panel keys
2 | PANEL_MONGODB_PASSWORD=
3 | PANEL_SECRET_KEY=
4 | PANEL_ADMIN_UUID=
5 | PANEL_DOMAIN=example.com
6 | PANEL_ADMIN_PASSWORD=example
7 | PANEL_PROXY_CONNECT_UUID=
8 | PANEL_PROXY_CONFIGURATION_UUID=
9 |
10 | # Provider configuration
11 | CONN_VLESS_WS_AUTH_UUID=
12 | CONN_VLESS_WS_URL=
13 | CONN_VLESS_WS_IP=127.0.0.1
14 | CONN_TROJAN_WS_AUTH_PASSWORD=
15 | CONN_TROJAN_WS_URL=
16 | CONN_TROJAN_WS_IP=127.0.0.1
17 | CONN_TROJAN_GRPC_AUTH_PASSWORD=
18 | CONN_TROJAN_GRPC_URL=
19 | CONN_TROJAN_GRPC_IP=127.0.0.1
20 | CONN_VLESS_GRPC_AUTH_UUID=
21 | CONN_VLESS_GRPC_URL=
22 | CONN_VLESS_GRPC_IP=127.0.0.1
23 | CONN_SHADOWSOCKS_V2RAY_AUTH_PASSWORD=
24 | CONN_SHADOWSOCKS_V2RAY_URL=
25 | CONN_SHADOWSOCKS_V2RAY_IP=127.0.0.1
26 | CONN_SHADOWSOCKS_GRPC_AUTH_PASSWORD=
27 | CONN_SHADOWSOCKS_GRPC_URL=
28 | CONN_SHADOWSOCKS_GRPC_IP=127.0.0.1
29 | CONN_VMESS_GRPC_AUTH_UUID=
30 | CONN_VMESS_GRPC_URL=
31 | CONN_VMESS_GRPC_IP=127.0.0.1
32 |
33 | # Host controller configuration
34 | HOSTCONTROLLER_API_KEY=
35 |
36 | # Outbound firewall configuration
37 | FIREWALL_OUTBOUND_TCP_PORTS="22 53 80 8080 443 8443 3389 5222 5228:5230 5223 2197 8733" # ssh, dns, http, http, https, https, rdp, whatsappcall, GCM, Apple push, Apple push, phone link
38 | FIREWALL_OUTBOUND_UDP_PORTS="53 443 123 19302:19309 3389 3478" # dns, https, ntp, googlemeet, rdp, whatsappcall
39 |
--------------------------------------------------------------------------------
/syslog/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 | RUN apt-get update && apt-get install rsyslog -y
3 | RUN echo '$ModLoad imudp \n\
4 | $UDPServerRun 514 \n\
5 | $ModLoad imtcp \n\
6 | $InputTCPServerRun 514 \n\
7 | $template RemoteStore, "/var/log/log/event.log-%$year%%$Month%%$Day%-%$Hour%.log" \n\
8 | :source, !isequal, "localhost" -?RemoteStore \n\
9 | :source, isequal, "last" ~ ' > /etc/rsyslog.conf
10 |
11 | ENTRYPOINT ["rsyslogd", "-n"]
12 |
--------------------------------------------------------------------------------
/translate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | LANGUAGES=(zh fa)
4 | for lang in "${LANGUAGES[@]}"; do
5 | rm -f README-$lang.md
6 | echo "Translating README.md to README-$lang.md..."
7 | trans -s en -t $lang -i README.md -b --no-bidi -o README-$lang.md
8 | done
9 |
10 | sed -i 's/自由茶/Libertea/g' README-zh.md
11 | sed -i 's/لیبرتی/Libertea/g' README-fa.md
12 |
--------------------------------------------------------------------------------
/user.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | COMMAND="$1"
4 |
5 | # load .env file
6 | set -o allexport
7 | source .env
8 | set +o allexport
9 |
10 |
11 | if [[ "$COMMAND" == "add" ]]; then
12 | NOTE="$2"
13 | if [[ -z "$NOTE" ]]; then
14 | echo "Please provide a name for the new user"
15 | exit 1
16 | fi
17 |
18 | echo "Creating new user with name: $NOTE"
19 |
20 | # send a POST request to localhost:1000/api/createUser with X-API-KEY header
21 | # and the note as the form body
22 | result=$(curl -s -X POST -H "X-API-KEY: $HOSTCONTROLLER_API_KEY" -F "note=$NOTE" http://localhost:1000/api/createUser)
23 |
24 | is_duplicate=$(echo "$result" | jq -r '.duplicate')
25 | if [[ "$is_duplicate" == "true" ]]; then
26 | echo "User '$NOTE' already exists"
27 | exit 1
28 | fi
29 |
30 | # parse result json and extract panelId
31 | panelId=$(echo "$result" | jq -r '.panelId')
32 |
33 | if [[ -z "$panelId" ]]; then
34 | echo "Failed to create new user"
35 | exit 1
36 | fi
37 |
38 | echo "Created new user with panelId: $panelId"
39 |
40 | elif [[ "$COMMAND" == "remove" ]]; then
41 | NOTE="$2"
42 | if [[ -z "$NOTE" ]]; then
43 | echo "Please provide a name for the user to remove"
44 | exit 1
45 | fi
46 |
47 | echo "Deleting user with name: $NOTE"
48 |
49 | # send a POST request to localhost:1000/api/removeUser with X-API-KEY header
50 | # and the note as the form body, and get the response code
51 | # if response code is 200, then the user was deleted successfully
52 | # if response code is 404, then the user was not found
53 |
54 | response_code=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "X-API-KEY: $HOSTCONTROLLER_API_KEY" -F "note=$NOTE" http://localhost:1000/api/removeUser)
55 |
56 | if [[ "$response_code" == "200" ]]; then
57 | echo "Deleted user with name: $NOTE"
58 | elif [[ "$response_code" == "404" ]]; then
59 | echo "User with name: $NOTE not found"
60 | else
61 | echo "Failed to delete user with name: $NOTE"
62 | fi
63 | elif [[ "$COMMAND" == "list" ]]; then
64 | # send a GET request to localhost:1000/api/getUsers with X-API-KEY header
65 | # and get the response body and print it
66 | result=$(curl -s -X GET -H "X-API-KEY: $HOSTCONTROLLER_API_KEY" http://localhost:1000/api/getUsers)
67 |
68 | # convert json to table
69 | echo "$result" | jq -r '.[] | [.note, .panel_id] | @tsv' | column -t -s $'\t'
70 | else
71 | echo "Usage: "
72 | echo " ./user.sh add "
73 | echo " ./user.sh remove "
74 | echo " ./user.sh list"
75 | fi
76 |
77 |
78 |
--------------------------------------------------------------------------------
/version-proxy.txt:
--------------------------------------------------------------------------------
1 | 1007
2 |
--------------------------------------------------------------------------------
/version.txt:
--------------------------------------------------------------------------------
1 | 1043
2 |
--------------------------------------------------------------------------------