├── .gitignore ├── config.json ├── nginx.conf ├── README.md ├── xray.py ├── config_example.json ├── install.sh ├── client.py └── cloudflare.py /.gitignore: -------------------------------------------------------------------------------- 1 | cloudflare.conf -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "debug" 4 | }, 5 | "inbounds": [ 6 | { 7 | "listen": "0.0.0.0", 8 | "port": "1083", 9 | "protocol": "socks", 10 | "settings": { 11 | "auth": "noauth", 12 | "udp": true, 13 | "ip": "0.0.0.0" 14 | } 15 | }, 16 | { 17 | "listen": "0.0.0.0", 18 | "port": "1081", 19 | "protocol": "http" 20 | } 21 | ], 22 | "outbounds": [ 23 | { 24 | "protocol": "freedom" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | ssl_certificate /etc/v2ray/fullchain.pem; 4 | ssl_certificate_key /etc/v2ray/privkey.pem; 5 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 6 | ssl_ciphers HIGH:!aNULL:!MD5; 7 | server_name _; 8 | 9 | location /vless { 10 | proxy_redirect off; 11 | proxy_pass http://127.0.0.1:12346/vless; 12 | proxy_http_version 1.1; 13 | proxy_set_header Host $host; 14 | proxy_set_header Connection "Upgrade"; 15 | proxy_set_header Upgrade "WebSocket"; 16 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 17 | proxy_intercept_errors on; 18 | } 19 | 20 | location / { 21 | return 204; 22 | } 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xray vless-tls-ws Setup Script 2 | 3 | ## Prerequisites 4 | 5 | - A registered domain name and access to its DNS records 6 | - A Cloudflare account and API key 7 | - A server running Ubuntu or a derivative 8 | - curl and python3 installed on the server 9 | 10 | ## Cloudflare Global API Key 11 | 12 | 1. Log in to your Cloudflare account. 13 | 2. Click on the profile icon on the top right corner and select "My profile". 14 | 3. Click on the "API Tokens" tab. 15 | 4. Under the "Global API Key" section, click on the "View" button. 16 | 17 | ## Usage 18 | 19 | ``` 20 | git clone https://github.com/rrouzbeh/xray 21 | cd xray 22 | chmod +x install.sh 23 | ./install.sh 24 | python client.py 25 | ``` 26 | 27 | ## Note 28 | 29 | This script assumes that curl and python3 are installed on your server. If not, install them with `sudo apt-get install curl python3`. 30 | -------------------------------------------------------------------------------- /xray.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | import json 3 | import sys 4 | 5 | 6 | def open_example_config(config_path): 7 | with open(config_path, 'r') as f: 8 | config = json.load(f) 9 | return config 10 | 11 | 12 | def gen_config(config, uuid, domain): 13 | cert_path = f'/etc/letsencrypt/live/{domain}/' 14 | config = open_example_config('config_example.json') 15 | config['inbounds'][0]['settings']['clients'][0]['id'] = uuid 16 | config['inbounds'][0]['streamSettings']['tlsSettings']['serverName'] = domain 17 | return config 18 | 19 | 20 | if __name__ == '__main__': 21 | config = open_example_config('config_example.json') 22 | uuid = str(uuid.uuid4()) 23 | domain = sys.argv[1] 24 | res = gen_config(config, uuid, domain) 25 | # create config.json 26 | with open('config.json', 'w') as f: 27 | json.dump(res, f, indent=4) 28 | # create uuid.txt 29 | with open('uuid.txt', 'w') as f: 30 | f.write(uuid) 31 | print('config.json created successfully!') 32 | -------------------------------------------------------------------------------- /config_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 443, 8 | "listen": "0.0.0.0", 9 | "protocol": "vless", 10 | "settings": { 11 | "clients": [ 12 | { 13 | "id": "", 14 | "level": 0, 15 | "email": "" 16 | } 17 | ], 18 | "decryption": "none" 19 | }, 20 | "streamSettings": { 21 | "network": "ws", 22 | "security": "tls", 23 | "tlsSettings": { 24 | "alpn": ["http/1.1"], 25 | "serverName": "serverName", 26 | "certificates": [ 27 | { 28 | "certificateFile": "/etc/v2ray/fullchain.pem", 29 | "keyFile": "/etc/v2ray/privkey.pem" 30 | } 31 | ] 32 | }, 33 | "wsSettings": { 34 | "path": "/" 35 | } 36 | } 37 | } 38 | ], 39 | "outbounds": [ 40 | { 41 | "protocol": "freedom" 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | read -p "Enter the domain name: " domain 4 | read -p "Enter the Cloudflare email: " auth_email 5 | read -p "Enter the Cloudflare Global key: " auth_key 6 | # get server external IP 7 | ip=$(curl -s https://api.ipify.org) 8 | 9 | 10 | 11 | # sleep 10 seconds to wait for DNS propagation 12 | echo -ne '### (10%)\r' 13 | sleep 10 14 | sudo apt-get update > /dev/null 15 | bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install > /dev/null 16 | echo -ne '######### (30%)\r' 17 | python3 cloudflare.py $auth_email $auth_key $domain $ip 18 | echo -ne '############# (35%)\r' 19 | sudo apt-get install certbot python3-certbot-dns-cloudflare -y > /dev/null 20 | mkdir /root/.secrets/ 21 | touch /root/.secrets/cloudflare.ini 22 | echo "dns_cloudflare_email = $auth_email" >> /root/.secrets/cloudflare.ini 23 | echo "dns_cloudflare_api_key" = $auth_key >> /root/.secrets/cloudflare.ini 24 | sudo chmod 0700 /root/.secrets/ 25 | sudo chmod 0400 /root/.secrets/cloudflare.ini 26 | echo -ne '################ (45%)\r' 27 | sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.secrets/cloudflare.ini --non-interactive --agree-tos --email your-email@$domain -d $domain,*.$domain 28 | echo -ne '#################### (60%)\r' 29 | echo "Certificate generated for $domain" 30 | echo -ne '####################### (80%)\r' 31 | install -d -o nobody -g nogroup /etc/v2ray/ 32 | install -m 644 -o nobody -g nogroup /etc/letsencrypt/live/$domain/fullchain.pem -t /etc/v2ray/ 33 | install -m 600 -o nobody -g nogroup /etc/letsencrypt/live/$domain/privkey.pem -t /etc/v2ray/ 34 | python3 xray.py $domain 35 | echo -ne '######################## (90%)\r' 36 | cp config.json /usr/local/etc/xray/config.json 37 | sudo systemctl restart xray 38 | echo -ne '######################### (100%)\r' -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import sys 3 | import random 4 | import string 5 | import requests 6 | import json 7 | 8 | 9 | def generate_random_domain(subdomains, top_domains): 10 | subdomain = random.choice(subdomains) 11 | top_domain = random.choice(top_domains) 12 | domain = subdomain + "-" + top_domain 13 | random_string = ''.join(random.choices( 14 | string.ascii_lowercase, k=6)) 15 | return domain + random_string 16 | 17 | # file lines to list 18 | 19 | 20 | def file_to_list(file_path): 21 | with open(file_path, 'r') as f: 22 | lines = f.readlines() 23 | return lines 24 | 25 | 26 | def gen_vless_qr_code(server_config): 27 | # Generate vless:// link 28 | vless_link = f"vless://{server_config['user_id']}@{server_config['address']}:{server_config['port']}?type=ws&security=tls&path=%2F&host={server_config['tls_server_name']}&sni={server_config['tls_server_name']}&alpn=h2,http/1.1&fp=chrome#{server_config['tls_server_name']}" 29 | # Generate QR code 30 | qr_code = base64.b64encode(vless_link.encode('utf-8')).decode('utf-8') 31 | return qr_code, vless_link 32 | 33 | 34 | def cloudflare_dns_record_create(auth_email, auth_key, zone_id, record, server_ip): 35 | print("Creating DNS record...") 36 | print(f"DNS record: {record}") 37 | print(f"Zone ID: {zone_id}") 38 | # Set up the API request headers 39 | headers = { 40 | "X-Auth-Email": auth_email, 41 | "X-Auth-Key": auth_key, 42 | "Content-Type": "application/json" 43 | } 44 | data = { 45 | "type": "A", 46 | "name": record, 47 | "content": server_ip, 48 | "proxied": True 49 | } 50 | # Make the API request to create the DNS record 51 | response = requests.post( 52 | f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", 53 | headers=headers, 54 | json=data 55 | ) 56 | 57 | # Check the response status code to see if the request was successful 58 | if response.status_code == 200: 59 | print("DNS record created successfully!") 60 | else: 61 | print(f"Error creating DNS record: {response.json()}") 62 | 63 | 64 | if __name__ == '__main__': 65 | subdomains = ["www", "blog", "ftp", "mail", "webmail", "remote", "ns1", "ns2", "ns3", "ns4", "ns5", "ns6", "ns7", "ns8", "ns9", "test", "dev", "stage", 66 | "beta", "alpha", "docs", "docs-google", "drive", "drive-google", "calendar", "calendar-google", "maps", "maps-google", "app", "app-google", "apps"] 67 | top_domains = ["digikala", "alibaba", "soroush", "snapp", "divar", "zomato", "cafebazaar", "cheetah", "myket", "aparat", "sibapp", "parsijoo", "irna", "irib", "mehrnews", "varzesh3", "tnews", 68 | "iran-daily", "iran-emrooz", "iran-front", "iran-varzeshi", "iran-newspaper", "iran-review", "iran-student", "iran-times", "iran-tribune", "iran-press", "iran-dailynews", "iran-news"] 69 | 70 | with open('cloudflare.conf', 'r') as f: 71 | conf = json.load(f) 72 | cf_ips = file_to_list('cf_ips.txt') 73 | # sort cf_ips by second column and add it to a new list 74 | cf_ips = sorted(cf_ips, key=lambda x: int(x.strip().split(",")[1])) 75 | # get server external IP 76 | ip = requests.get('https://api.ipify.org').text 77 | with open('uuid.txt', 'r') as f: 78 | user_id = f.read() 79 | for i in range(5): 80 | address = cf_ips[i].strip().split(",")[0] 81 | record = generate_random_domain(subdomains, top_domains) 82 | domain = f'{record}.{conf["domain"]}' 83 | server_config = { 84 | "address": address, 85 | "port": 443, 86 | "user_id": user_id, 87 | "alter_id": 64, 88 | "tls_server_name": domain, 89 | "ws_path": "/" 90 | } 91 | if i < 4: 92 | cloudflare_dns_record_create( 93 | conf['auth_email'], conf['auth_key'], conf['zone_id'], record, ip) 94 | else: 95 | server_config["address"] = ip 96 | qr_code, vless_link = gen_vless_qr_code(server_config) 97 | print(qr_code) 98 | print(vless_link) 99 | -------------------------------------------------------------------------------- /cloudflare.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import sys 4 | import time 5 | 6 | 7 | def create_cloudflare_zone(auth_email, auth_key, domain): 8 | # Set up the API request headers 9 | headers = { 10 | "X-Auth-Email": auth_email, 11 | "X-Auth-Key": auth_key, 12 | "Content-Type": "application/json" 13 | } 14 | 15 | # Set up the API request body 16 | data = { 17 | "name": domain, 18 | "jump_start": False 19 | } 20 | 21 | # Make the API request to create the zone 22 | response = requests.post( 23 | "https://api.cloudflare.com/client/v4/zones", 24 | headers=headers, 25 | json=data 26 | ) 27 | 28 | # Check the response status code to see if the request was successful 29 | if response.status_code == 200: 30 | # save zone id to a file 31 | with open('zone_id.txt', 'w') as f: 32 | f.write(response.json()['result']['id']) 33 | print("Zone created successfully!") 34 | return response.json()['result']['id'] 35 | else: 36 | print(f"Error creating zone: {response.json()}") 37 | return None 38 | 39 | 40 | def set_ssl_full(auth_email, auth_key, zone_id): 41 | # Set up the API request headers 42 | headers = { 43 | "X-Auth-Email": auth_email, 44 | "X-Auth-Key": auth_key, 45 | "Content-Type": "application/json" 46 | } 47 | 48 | # Set up the API request body 49 | data = { 50 | "value": "full" 51 | } 52 | 53 | # Make the API request to set SSL to full 54 | response = requests.patch( 55 | f"https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/ssl", 56 | headers=headers, 57 | json=data 58 | ) 59 | 60 | # Check the response status code to see if the request was successful 61 | if response.status_code == 200: 62 | print("SSL set to full successfully!") 63 | else: 64 | print(f"Error setting SSL to full: {response.json()}") 65 | 66 | 67 | def enable_ws_proxy(auth_email, auth_key, zone_id): 68 | # Set up the API request headers 69 | headers = { 70 | "X-Auth-Email": auth_email, 71 | "X-Auth-Key": auth_key, 72 | "Content-Type": "application/json" 73 | } 74 | 75 | # Set up the API request body 76 | data = { 77 | "value": "on" 78 | } 79 | 80 | # Make the API request to enable ws proxy 81 | response = requests.patch( 82 | f"https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/websockets", 83 | headers=headers, 84 | json=data 85 | ) 86 | 87 | # Check the response status code to see if the request was successful 88 | if response.status_code == 200: 89 | print("Websocket proxy enabled successfully!") 90 | else: 91 | print(f"Error enabling websocket proxy: {response.json()}") 92 | 93 | 94 | def cloudflare_dns_record_create(auth_email, auth_key, zone_id, record): 95 | 96 | # Set up the API request headers 97 | headers = { 98 | "X-Auth-Email": auth_email, 99 | "X-Auth-Key": auth_key, 100 | "Content-Type": "application/json" 101 | } 102 | 103 | # Make the API request to create the DNS record 104 | response = requests.post( 105 | f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records", 106 | headers=headers, 107 | json=record 108 | ) 109 | 110 | # Check the response status code to see if the request was successful 111 | if response.status_code == 200: 112 | print("DNS record created successfully!") 113 | else: 114 | print(f"Error creating DNS record: {response.json()}") 115 | 116 | 117 | if __name__ == "__main__": 118 | auth_email = sys.argv[1] 119 | auth_key = sys.argv[2] 120 | domain = sys.argv[3] 121 | server_ip = sys.argv[4] 122 | 123 | # Create a zone 124 | zone_id = create_cloudflare_zone(auth_email, auth_key, domain) 125 | time.sleep(30) 126 | if zone_id is None: 127 | print("Error creating zone") 128 | sys.exit(1) 129 | conf = { 130 | "auth_email": auth_email, 131 | "auth_key": auth_key, 132 | "zone_id": zone_id, 133 | "domain": domain 134 | } 135 | with open('cloudflare.conf', 'w') as f: 136 | json.dump(conf, f, indent=4) 137 | # set ssl to full 138 | set_ssl_full(auth_email, auth_key, zone_id) 139 | enable_ws_proxy(auth_email, auth_key, zone_id) 140 | # The DNS record to create 141 | time.sleep(90) 142 | record = { 143 | "type": "A", 144 | "name": "@", 145 | "content": server_ip, 146 | "proxied": True 147 | } 148 | cloudflare_dns_record_create( 149 | auth_email=auth_email, auth_key=auth_key, zone_id=zone_id, record=record) 150 | --------------------------------------------------------------------------------