├── vars └── main.yml ├── defaults └── main.yml ├── .gitignore ├── tests └── test.yml ├── tasks ├── install_yq.yml ├── bbr.yml ├── user.yml ├── install_docker.yml ├── ssh.yml ├── install_xray.yml ├── install_marzban.yml ├── iptables.yml ├── main.yml ├── setup_warp.yml └── end_xray.yml ├── templates_for_script ├── compose ├── sing_box_outbound ├── xray_outbound ├── caddy ├── xray ├── marzban └── confluence_page ├── handlers └── main.yml ├── meta └── main.yml ├── templates ├── xray_docker.j2 ├── marzban_docker.j2 ├── caddyfile.j2 ├── xray.json.j2 ├── marzban.j2 └── confluence.j2 ├── README.md ├── install_in_docker.md └── vps-setup.sh /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for vps-setup 3 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for vps-setup 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | gist*.md 2 | ansible.cfg 3 | inventory.yml 4 | playbook.yml -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - vps-setup 6 | -------------------------------------------------------------------------------- /tasks/install_yq.yml: -------------------------------------------------------------------------------- 1 | - name: Download yq 2 | ansible.builtin.get_url: 3 | url: https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 4 | dest: /usr/bin/yq 5 | mode: '0755' -------------------------------------------------------------------------------- /templates_for_script/compose: -------------------------------------------------------------------------------- 1 | services: 2 | caddy: 3 | image: caddy:2.9 4 | container_name: caddy 5 | restart: always 6 | network_mode: host 7 | volumes: 8 | - ./caddy/data:/data 9 | - ./caddy/Caddyfile:/etc/caddy/Caddyfile -------------------------------------------------------------------------------- /tasks/bbr.yml: -------------------------------------------------------------------------------- 1 | - name: Set BBR 2 | ansible.posix.sysctl: 3 | name: net.core.default_qdisc 4 | value: "fq" 5 | state: present 6 | - name: Set queue 7 | ansible.posix.sysctl: 8 | name: net.ipv4.tcp_congestion_control 9 | value: "bbr" 10 | state: present -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for vps-setup 3 | - name: Restart xray 4 | service: 5 | name: xray 6 | state: restarted 7 | - name: Restart caddy 8 | service: 9 | name: caddy 10 | state: restarted 11 | - name: Restart ssh 12 | service: 13 | name: ssh 14 | state: restarted 15 | daemon_reload: true -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Akiyamov 3 | description: Setup VPS for XRay 4 | company: your company (optional) 5 | license: BSD-3-Clause 6 | min_ansible_version: 2.1 7 | platforms: 8 | - name: Ubuntu 9 | versions: 10 | - 16 11 | - 18 12 | - 20 13 | - 22 14 | - 24 15 | galaxy_tags: 16 | - xray 17 | - vps 18 | dependencies: [] 19 | -------------------------------------------------------------------------------- /tasks/user.yml: -------------------------------------------------------------------------------- 1 | - name: Add user 2 | ansible.builtin.user: 3 | name: "{{ user_to_create }}" 4 | shell: /bin/bash 5 | groups: sudo,docker 6 | password: "{{ user_password | password_hash('sha512') }}" 7 | append: yes 8 | update_password: on_create 9 | register: "xray_user" 10 | - name: Add ssh_pbk to user 11 | ansible.posix.authorized_key: 12 | user: "{{ user_to_create }}" 13 | state: "present" 14 | key: "{{ ssh_public_key }}" 15 | -------------------------------------------------------------------------------- /tasks/install_docker.yml: -------------------------------------------------------------------------------- 1 | - name: Add Docker GPG apt Key 2 | ansible.builtin.apt_key: 3 | url: https://download.docker.com/linux/ubuntu/gpg 4 | state: present 5 | - name: Add Docker Repository 6 | ansible.builtin.apt_repository: 7 | repo: deb https://download.docker.com/linux/ubuntu focal stable 8 | state: present 9 | - name: Update install docker-ce 10 | ansible.builtin.apt: 11 | name: docker-ce 12 | state: latest 13 | update_cache: true 14 | -------------------------------------------------------------------------------- /templates/xray_docker.j2: -------------------------------------------------------------------------------- 1 | services: 2 | caddy: 3 | image: caddy:2.9 4 | container_name: caddy 5 | restart: always 6 | network_mode: host 7 | volumes: 8 | - ./caddy/data:/data 9 | - ./caddy/Caddyfile:/etc/caddy/Caddyfile 10 | - ./caddy/templates:/srv 11 | xray: 12 | image: ghcr.io/xtls/xray-core:25.6.8 13 | restart: always 14 | container_name: xray 15 | user: root 16 | command: run -c /etc/xray/config.json 17 | network_mode: host 18 | volumes: 19 | - ./xray:/etc/xray -------------------------------------------------------------------------------- /tasks/ssh.yml: -------------------------------------------------------------------------------- 1 | - name: Change SSH port 2 | shell: 3 | cmd: grep -r Port /etc/ssh -l | xargs -n 1 sed -i -e "/Port /c\Port {{ SSH_PORT }}" 4 | - name: Disable password login 5 | shell: 6 | cmd: grep -r PasswordAuthentication /etc/ssh -l | xargs -n 1 sed -i -e "/PasswordAuthentication /c\PasswordAuthentication no" 7 | - name: Disable root login 8 | shell: 9 | cmd: grep -r PermitRootLogin /etc/ssh -l | xargs -n 1 sed -i -e "/PermitRootLogin /c\PermitRootLogin no" 10 | - name: Restart ssh 11 | service: 12 | name: ssh 13 | state: restarted 14 | daemon_reload: true -------------------------------------------------------------------------------- /templates_for_script/sing_box_outbound: -------------------------------------------------------------------------------- 1 | { 2 | "type": "vless", 3 | "server": "$VLESS_DOMAIN", 4 | "server_port": 443, 5 | "uuid": "$XRAY_UUID", 6 | "flow": "xtls-rprx-vision", 7 | "tls": { 8 | "enabled": true, 9 | "insecure": false, 10 | "server_name": "$VLESS_DOMAIN", 11 | "utls": { 12 | "enabled": true, 13 | "fingerprint": "chrome" 14 | }, 15 | "reality": { 16 | "enabled": true, 17 | "public_key": "$XRAY_PBK", 18 | "short_id": "$XRAY_SID" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /templates/marzban_docker.j2: -------------------------------------------------------------------------------- 1 | services: 2 | caddy: 3 | image: caddy:2.9 4 | container_name: caddy 5 | restart: always 6 | network_mode: host 7 | volumes: 8 | - ./caddy/data:/data 9 | - ./caddy/Caddyfile:/etc/caddy/Caddyfile 10 | - ./marzban_lib:/run/marzban 11 | marzban: 12 | image: gozargah/marzban:v0.8.4 13 | container_name: marzban 14 | restart: always 15 | env_file: ./marzban/.env 16 | network_mode: host 17 | volumes: 18 | - ./marzban_lib:/var/lib/marzban 19 | - ./marzban/xray_config.json:/code/xray_config.json 20 | - ./marzban/templates:/var/lib/marzban/templates -------------------------------------------------------------------------------- /tasks/install_xray.yml: -------------------------------------------------------------------------------- 1 | - name: Create dirs 2 | file: 3 | path: "{{ item }}" 4 | state: directory 5 | loop: 6 | - /opt/xray-vps-setup/caddy/templates 7 | - /opt/xray-vps-setup/xray 8 | - name: Copy config files 9 | ansible.builtin.template: 10 | src: "{{ item.src }}" 11 | dest: "{{ item.dest }}" 12 | loop: 13 | - { src: "caddyfile.j2", dest: "/opt/xray-vps-setup/caddy/Caddyfile" } 14 | - { src: "confluence.j2", dest: "/opt/xray-vps-setup/caddy/templates/index.html" } 15 | - { src: "xray.json.j2", dest: "/opt/xray-vps-setup/xray/config.json" } 16 | - { src: "xray_docker.j2", dest: "/opt/xray-vps-setup/docker-compose.yml" } 17 | -------------------------------------------------------------------------------- /templates_for_script/xray_outbound: -------------------------------------------------------------------------------- 1 | { 2 | "tag": "default", 3 | "protocol": "vless", 4 | "settings": { 5 | "vnext": [ 6 | { 7 | "address": "$VLESS_DOMAIN", 8 | "port": 443, 9 | "users": [ 10 | { 11 | "id": "$XRAY_UUID", 12 | "encryption": "none", 13 | "flow": "xtls-rprx-vision" 14 | } 15 | ] 16 | } 17 | ] 18 | }, 19 | "streamSettings": { 20 | "network": "tcp", 21 | "security": "reality", 22 | "realitySettings": { 23 | "serverName": "$VLESS_DOMAIN", 24 | "fingerprint": "chrome", 25 | "publicKey": "$XRAY_PBK", 26 | "shortId": "$XRAY_SID", 27 | "spiderX": "" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /templates/caddyfile.j2: -------------------------------------------------------------------------------- 1 | { 2 | https_port 4123 3 | default_bind 127.0.0.1 4 | servers { 5 | listener_wrappers { 6 | proxy_protocol { 7 | allow 127.0.0.1/32 8 | } 9 | tls 10 | } 11 | } 12 | auto_https disable_redirects 13 | } 14 | https://{{ domain }} { 15 | {% if setup_variant == "marzban" %} 16 | reverse_proxy * unix//run/marzban/marzban.socket 17 | {% else %} 18 | root * /srv 19 | file_server 20 | {% endif %} 21 | header { 22 | -Server 23 | } 24 | handle_errors { 25 | header { 26 | -Server 27 | } 28 | } 29 | } 30 | http://{{ domain }} { 31 | bind 0.0.0.0 32 | redir https://{host}{uri} permanent 33 | 34 | header { 35 | -Server 36 | } 37 | handle_errors { 38 | header { 39 | -Server 40 | } 41 | } 42 | } 43 | :4123 { 44 | tls internal 45 | respond 204 46 | } 47 | :80 { 48 | bind 0.0.0.0 49 | respond 204 50 | } 51 | -------------------------------------------------------------------------------- /templates_for_script/caddy: -------------------------------------------------------------------------------- 1 | { 2 | https_port 4123 3 | default_bind 127.0.0.1 4 | servers { 5 | listener_wrappers { 6 | proxy_protocol { 7 | allow 127.0.0.1/32 8 | } 9 | tls 10 | } 11 | } 12 | auto_https disable_redirects 13 | } 14 | https://$VLESS_DOMAIN { 15 | $CADDY_REVERSE 16 | 17 | header { 18 | -Server 19 | } 20 | handle_errors { 21 | header { 22 | -Server 23 | } 24 | } 25 | } 26 | http://$VLESS_DOMAIN { 27 | bind 0.0.0.0 28 | redir https://$VLESS_DOMAIN{uri} permanent 29 | 30 | header { 31 | -Server 32 | } 33 | 34 | handle_errors { 35 | header { 36 | -Server 37 | } 38 | } 39 | } 40 | :4123 { 41 | tls internal 42 | respond 204 43 | } 44 | :80 { 45 | bind 0.0.0.0 46 | respond 204 47 | } 48 | -------------------------------------------------------------------------------- /tasks/install_marzban.yml: -------------------------------------------------------------------------------- 1 | - name: Generate marzban specific values 2 | block: 3 | - name: Generate marzban password 4 | set_fact: 5 | MARZBAN_PASS: "{{ lookup('password', '/dev/null length=13 chars=ascii_letters') }}" 6 | - name: Generate marzban password 7 | set_fact: 8 | MARZBAN_PATH: "{{ lookup('password', '/dev/null length=8 chars=ascii_letters') }}" 9 | - name: Generate marzban password 10 | set_fact: 11 | MARZBAN_SUB_PATH: "{{ lookup('password', '/dev/null length=8 chars=ascii_letters') }}" 12 | - name: Create dirs 13 | file: 14 | path: "{{ item }}" 15 | state: directory 16 | loop: 17 | - /opt/xray-vps-setup/caddy 18 | - /opt/xray-vps-setup/marzban 19 | - /opt/xray-vps-setup/marzban/templates/home 20 | - name: Copy config files 21 | ansible.builtin.template: 22 | src: "{{ item.src }}" 23 | dest: "{{ item.dest }}" 24 | loop: 25 | - { src: "caddyfile.j2", dest: "/opt/xray-vps-setup/caddy/Caddyfile" } 26 | - { src: "xray.json.j2", dest: "/opt/xray-vps-setup/marzban/xray_config.json" } 27 | - { src: "marzban.j2", dest: "/opt/xray-vps-setup/marzban/.env" } 28 | - { src: "confluence.j2", dest: "/opt/xray-vps-setup/marzban/templates/home/index.html" } 29 | - { src: "marzban_docker.j2", dest: "/opt/xray-vps-setup/docker-compose.yml" } 30 | - debug: 31 | msg: "Marzban password: {{ MARZBAN_PASS }}, marzban path: {{ MARZBAN_PATH }}" -------------------------------------------------------------------------------- /tasks/iptables.yml: -------------------------------------------------------------------------------- 1 | - name: IPTables rules 2 | block: 3 | - name: Install netfilter-persistent 4 | apt: 5 | name: netfilter-persistent 6 | state: present 7 | - name: Allow related and established connections 8 | ansible.builtin.iptables: 9 | chain: INPUT 10 | ctstate: ESTABLISHED,RELATED 11 | jump: ACCEPT 12 | become: yes 13 | - name: Allow new incoming SYN packets on specified port 14 | ansible.builtin.iptables: 15 | chain: INPUT 16 | protocol: tcp 17 | destination_port: "{{ SSH_PORT }}" 18 | ctstate: NEW 19 | syn: match 20 | jump: ACCEPT 21 | - name: Allow ICMP 22 | ansible.builtin.iptables: 23 | chain: INPUT 24 | protocol: icmp 25 | jump: ACCEPT 26 | - name: Allow 80, 443 connections 27 | ansible.builtin.iptables: 28 | chain: INPUT 29 | protocol: tcp 30 | destination_ports: 31 | - "80" 32 | - "443" 33 | jump: ACCEPT 34 | - name: Allow loopback in 35 | shell: 36 | cmd: iptables -A INPUT -i lo -j ACCEPT 37 | - name: Allow loopback out 38 | shell: 39 | cmd: iptables -A OUTPUT -o lo -j ACCEPT 40 | - name: INPUT DROP 41 | ansible.builtin.iptables: 42 | chain: INPUT 43 | policy: DROP 44 | - name: Save iptables rules 45 | shell: 46 | cmd: netfilter-persistent save -------------------------------------------------------------------------------- /templates_for_script/xray: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "none" 4 | }, 5 | "inbounds": [ 6 | { 7 | "tag": "VLESS TCP VISION REALITY", 8 | "listen": "0.0.0.0", 9 | "port": 443, 10 | "protocol": "vless", 11 | "settings": { 12 | "clients": [ 13 | { 14 | "id": "$XRAY_UUID", 15 | "email": "default", 16 | "flow": "xtls-rprx-vision" 17 | } 18 | ], 19 | "decryption": "none" 20 | }, 21 | "streamSettings": { 22 | "network": "tcp", 23 | "security": "reality", 24 | "realitySettings": { 25 | "xver": 1, 26 | "dest": "127.0.0.1:4123", 27 | "serverNames": [ 28 | "$VLESS_DOMAIN" 29 | ], 30 | "privateKey": "$XRAY_PIK", 31 | "shortIds": [ 32 | "$XRAY_SID" 33 | ] 34 | } 35 | }, 36 | "sniffing": { 37 | "enabled": true, 38 | "destOverride": [ 39 | "http", 40 | "tls" 41 | ], 42 | "routeOnly": true 43 | } 44 | } 45 | ], 46 | "outbounds": [ 47 | { 48 | "protocol": "freedom", 49 | "tag": "direct", 50 | "settings": { 51 | "domainStrategy": "UseIPv4" 52 | } 53 | }, 54 | { 55 | "protocol": "blackhole", 56 | "tag": "block" 57 | } 58 | ], 59 | "routing": { 60 | "rules": [ 61 | { 62 | "protocol": "bittorrent", 63 | "outboundTag": "block" 64 | } 65 | ], 66 | "domainStrategy": "IPIfNonMatch" 67 | }, 68 | "dns": { 69 | "servers": [ 70 | "1.1.1.1", 71 | "8.8.8.8" 72 | ], 73 | "queryStrategy": "UseIPv4", 74 | "disableFallback": false, 75 | "tag": "dns-aux" 76 | } 77 | } -------------------------------------------------------------------------------- /templates/xray.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "info" 4 | }, 5 | "inbounds": [ 6 | { 7 | "tag": "VLESS TCP VISION REALITY", 8 | "listen": "0.0.0.0", 9 | "port": 443, 10 | "protocol": "vless", 11 | "settings": { 12 | "clients": [ 13 | { 14 | "id": "{{ xray_uuid.stdout}}", 15 | "email": "default", 16 | "flow": "xtls-rprx-vision" 17 | }], 18 | "decryption": "none" 19 | }, 20 | "streamSettings": { 21 | "network": "tcp", 22 | "security": "reality", 23 | "realitySettings": { 24 | "xver": 1, 25 | "dest": "127.0.0.1:4123", 26 | "serverNames": [ 27 | "{{ domain }}" 28 | ], 29 | "privateKey": "{{ x25519_pik.stdout }}", 30 | "shortIds": [ 31 | "{{ short_id.stdout }}" 32 | ] 33 | } 34 | }, 35 | "sniffing": { 36 | "enabled": true, 37 | "destOverride": [ 38 | "http", 39 | "tls" 40 | ], 41 | "routeOnly": true 42 | } 43 | } 44 | ], 45 | "outbounds": [ 46 | { 47 | "protocol": "freedom", 48 | "tag": "direct", 49 | "settings": { 50 | "domainStrategy": "UseIPv4" 51 | } 52 | }, 53 | { 54 | "protocol": "blackhole", 55 | "tag": "block" 56 | } 57 | ], 58 | "routing": { 59 | "rules": [ 60 | { 61 | "protocol": "bittorrent", 62 | "outboundTag": "block" 63 | } 64 | ], 65 | "domainStrategy": "IPIfNonMatch" 66 | }, 67 | "dns": { 68 | "servers": [ 69 | "1.1.1.1", 70 | "8.8.8.8" 71 | ], 72 | "queryStrategy": "UseIPv4", 73 | "disableFallback": false, 74 | "tag": "dns-aux" 75 | } 76 | } -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Populate service facts 3 | ansible.builtin.service_facts: 4 | - name: Enable BBR 5 | include_tasks: bbr.yml 6 | - name: Install docker 7 | include_tasks: install_docker.yml 8 | when: ansible_facts.services['docker'] is undefined 9 | - name: Install/update yq 10 | include_tasks: install_yq.yml 11 | - name: Security block 12 | block: 13 | - name: Edit SSHD config 14 | include_tasks: ssh.yml 15 | - name: Edit iptables 16 | include_tasks: iptables.yml 17 | - name: Add user 18 | include_tasks: user.yml 19 | when: configure_security|default(false)|bool == true 20 | - name: Generate values 21 | block: 22 | - name: Generate x25519 PIK 23 | shell: 24 | cmd: docker run --rm ghcr.io/xtls/xray-core x25519 | head -n1 | cut -d' ' -f 3 25 | register: x25519_pik 26 | - name: Generate x25519 PBK 27 | shell: 28 | cmd: docker run --rm ghcr.io/xtls/xray-core x25519 -i {{ x25519_pik.stdout }} | tail -1 | cut -d' ' -f 3 29 | register: x25519_pbk 30 | - name: Generate SID 31 | shell: 32 | cmd: openssl rand -hex 8 33 | register: short_id 34 | - name: Generate default user 35 | shell: 36 | cmd: docker run --rm ghcr.io/xtls/xray-core uuid 37 | register: xray_uuid 38 | - name: Install marzban 39 | include_tasks: install_marzban.yml 40 | when: setup_variant == "marzban" 41 | - name: Install xray 42 | include_tasks: install_xray.yml 43 | when: setup_variant == "xray" 44 | - name: Install warp 45 | include_tasks: setup_warp.yml 46 | when: setup_warp|default(false)|bool == true 47 | - name: Start proxy 48 | community.docker.docker_compose_v2: 49 | project_src: /opt/xray-vps-setup 50 | files: 51 | - docker-compose.yml 52 | - name: End xray 53 | include_tasks: end_xray.yml 54 | when: setup_variant == "xray" -------------------------------------------------------------------------------- /tasks/setup_warp.yml: -------------------------------------------------------------------------------- 1 | #- name: Add WARP GPG key 2 | # ansible.builtin.get_url: 3 | # url: https://pkg.cloudflareclient.com/pubkey.gpg 4 | # dest: /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg 5 | # mode: '0644' 6 | # force: true 7 | - name: Add WARP GPG key 8 | ansible.builtin.shell: 9 | cmd: curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg 10 | - name: Add WARP repo 11 | ansible.builtin.apt_repository: 12 | filename: cloudflare-client 13 | repo: "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ {{ ansible_facts['distribution_release'] }} main" 14 | - name: 15 | apt: 16 | name: cloudflare-warp 17 | state: present 18 | update_cache: yes 19 | - name: Register WARP 20 | shell: 21 | cmd: echo "y" | warp-cli registration new 22 | - shell: 23 | cmd: warp-cli mode proxy 24 | - shell: 25 | cmd: warp-cli proxy port 40000 26 | - shell: 27 | cmd: warp-cli connect 28 | - name: Edit xray config 29 | block: 30 | - command: 31 | argv: 32 | - yq 33 | - eval 34 | - '.outbounds += {"tag": "warp","protocol": "socks","settings": {"servers": [{"address": "127.0.0.1","port": 40000}]}}' 35 | - -i 36 | - /opt/xray-vps-setup/xray/config.json 37 | - command: 38 | argv: 39 | - yq 40 | - eval 41 | - '.routing.rules += {"outboundTag": "warp", "domain": ["geosite:category-ru", "regexp:.*\\.xn--$", "regexp:.*\\.ru$", "regexp:.*\\.su$"]}' 42 | - -i 43 | - /opt/xray-vps-setup/xray/config.json 44 | when: setup_variant == "xray" 45 | - name: Edit marzban config 46 | block: 47 | - command: 48 | argv: 49 | - yq 50 | - eval 51 | - '.outbounds += {"tag": "warp","protocol": "socks","settings": {"servers": [{"address": "127.0.0.1","port": 40000}]}}' 52 | - -i 53 | - /opt/xray-vps-setup/marzban/xray_config.json 54 | - command: 55 | argv: 56 | - yq 57 | - eval 58 | - '.routing.rules += {"outboundTag": "warp", "domain": ["geosite:category-ru", "regexp:.*\\.xn--$", "regexp:.*\\.ru$", "regexp:.*\\.su$"]}' 59 | - -i 60 | - /opt/xray-vps-setup/marzban/xray_config.json 61 | when: setup_variant == "marzban" -------------------------------------------------------------------------------- /tasks/end_xray.yml: -------------------------------------------------------------------------------- 1 | - name: Print clipboard string 2 | debug: 3 | msg: "vless://{{ xray_uuid.stdout }}@{{ vless.domain }}:443?type=tcp&security=reality&pbk={{ x25519_pbk.stdout }}&fp=chrome&sni={{ vless.domain }}&sid={{ short_id.stdout }}&spx=%2F&flow=xtls-rprx-vision" 4 | - name: XRay outbound config 5 | debug: 6 | msg: | 7 | { 8 | "tag": "default", 9 | "protocol": "vless", 10 | "settings": { 11 | "vnext": [ 12 | { 13 | "address": "{{ vless.domain }}", 14 | "port": 443, 15 | "users": [ 16 | { 17 | "id": "{{ xray_uuid.stdout }}", 18 | "encryption": "none", 19 | "flow": "xtls-rprx-vision" 20 | } 21 | ] 22 | } 23 | ] 24 | }, 25 | "streamSettings": { 26 | "network": "tcp", 27 | "security": "reality", 28 | "realitySettings": { 29 | "serverName": "{{ vless.domain }}", 30 | "fingerprint": "chrome", 31 | "publicKey": "{{ x25519_pbk.stdout }}", 32 | "shortId": "{{ short_id.stdout }}", 33 | "spiderX": "" 34 | } 35 | } 36 | } 37 | - name: Sing-box outbound config 38 | debug: 39 | msg: | 40 | { 41 | "type": "vless", 42 | "server": "{{ vless.domain }}", 43 | "server_port": 443, 44 | "uuid": "{{ xray_uuid.stdout }}", 45 | "flow": "xtls-rprx-vision", 46 | "tls": { 47 | "enabled": true, 48 | "insecure": false, 49 | "server_name": "{{ vless.domain }}", 50 | "utls": { 51 | "enabled": true, 52 | "fingerprint": "chrome" 53 | }, 54 | "reality": { 55 | "enabled": true, 56 | "public_key": "{{ x25519_pbk.stdout }}", 57 | "short_id": "{{ short_id.stdout }}" 58 | } 59 | } 60 | } 61 | - name: Print PBK, SID and UUID to connect to server. 62 | debug: 63 | msg: "UUID: {{ xray_uuid.stdout }}, SID: {{ short_id.stdout }}, PBK: {{ x25519_pbk.stdout }}" 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xray-vps-setup 2 | VLESS со своим доменом. А что еще нужно для счастья? 3 | 4 | В данном варианте VLESS слушает на 443 и принимате все запросы, делая запрос на локальный Caddy только для сертификатов. В таком варианте задержка будет меньше, чем в варианте с Caddy/NGINX перед VLESS, где происходит множество лишних запросов. 5 | ## Скрипт 6 | 7 | - Установит Xray/Marzban на ваш выбор. Для маскировки страницы используется [Conflunce](https://github.com/Jolymmiles/confluence-marzban-home) 8 | - На ваше усмотрение настроит: 9 | - - Iptables, запретив все подключения, кроме SSH, 80 и 443. 10 | - - Создаст пользователя для подключения, запретив вход от рута 11 | - - Добавит этому пользователю ключ для SSH, запретив вход по паролю 12 | - Настроит WARP для ру-сайтов. 13 | ```bash 14 | bash <(wget -qO- https://raw.githubusercontent.com/Akiyamov/xray-vps-setup/refs/heads/main/vps-setup.sh) 15 | ``` 16 | 17 | ## Плейбук 18 | 19 | [Ansible-galaxy](https://galaxy.ansible.com/ui/standalone/roles/Akiyamov/xray-vps-setup/install/) 20 | ```yaml 21 | - name: Setup vps 22 | hosts: some_host 23 | roles: 24 | - Akiyamov.xray-vps-setup 25 | vars: 26 | domain: example.com # домен, уровень неважен 27 | setup_variant: marzban # marzban or xray 28 | setup_warp: false # true or false 29 | configure_security: true # true or false 30 | user_to_create: xray_user # если configure_security: true, то обязательно 31 | user_password: "xray_password" # если configure_security: true, то обязательно 32 | SSH_PORT: 22 # если configure_security: true, то обязательно 33 | ssh_public_key: "" # если configure_security: true, то обязательно 34 | ``` 35 | 36 | ## Добавляем подписку и поддержку Mihomo 37 | 38 | ``` 39 | bash <(wget -qO- https://github.com/legiz-ru/marz-sub/raw/main/marz-sub.sh) 40 | ``` 41 | После этого сделайте `docker compose -f /opt/xray-vps-setup/docker-compose.yml down && docker compose -f /opt/xray-vps-setup/docker-compose.yml up -d` 42 | 43 | 44 | ## Ручная установка 45 | 46 | Описана [здесь](https://github.com/Akiyamov/xray-vps-setup/blob/main/install_in_docker.md). 47 | 48 | ## Почему не nginx, haproxy, 3x-ui, x-ui, sing-box... 49 | 50 | Caddy сам получит сертификаты, поэтому нам не придется их получать через `acme.sh` или `certbot`. 51 | 3X-ui мерзотная панель. 52 | Sing-box не очень. 53 | XHTTP позже, а больше не надо. Уже точно. 54 | 55 | ## Связь 56 | Issues, PR ну или мой [тг](https://t.me/Akiyamov). 57 | 58 | > [!IMPORTANT] 59 | > Дайте секс -------------------------------------------------------------------------------- /templates_for_script/marzban: -------------------------------------------------------------------------------- 1 | SUDO_USERNAME = "xray_admin" 2 | SUDO_PASSWORD = "$MARZBAN_PASS" 3 | UVICORN_UDS = "/var/lib/marzban/marzban.socket" 4 | DASHBOARD_PATH = "/$MARZBAN_PATH/" 5 | XRAY_JSON = "xray_config.json" 6 | XRAY_SUBSCRIPTION_URL_PREFIX = "https://$VLESS_DOMAIN" 7 | XRAY_SUBSCRIPTION_PATH = "$MARZBAN_SUB_PATH" 8 | SQLALCHEMY_DATABASE_URL = "sqlite:////var/lib/marzban/db.sqlite3" 9 | # ALLOWED_ORIGINS=http://localhost,http://localhost:8000,http://example.com 10 | # XRAY_EXECUTABLE_PATH = "/usr/local/bin/xray" 11 | # XRAY_ASSETS_PATH = "/usr/local/share/xray" 12 | # XRAY_EXCLUDE_INBOUND_TAGS = "INBOUND_X INBOUND_Y" 13 | # XRAY_FALLBACKS_INBOUND_TAG = "INBOUND_X" 14 | 15 | # TELEGRAM_API_TOKEN = 123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 16 | # TELEGRAM_ADMIN_ID = 987654321, 123456789 17 | # TELEGRAM_LOGGER_CHANNEL_ID = -1234567890123 18 | # TELEGRAM_DEFAULT_VLESS_FLOW = "xtls-rprx-vision" 19 | # TELEGRAM_PROXY_URL = "http://localhost:8080" 20 | 21 | # DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/xxxxxxx" 22 | 23 | CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" 24 | # CLASH_SUBSCRIPTION_TEMPLATE="clash/my-custom-template.yml" 25 | SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html" 26 | HOME_PAGE_TEMPLATE="home/index.html" 27 | 28 | # V2RAY_SUBSCRIPTION_TEMPLATE="v2ray/default.json" 29 | # V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json" 30 | 31 | # SINGBOX_SUBSCRIPTION_TEMPLATE="singbox/default.json" 32 | # SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json" 33 | 34 | # MUX_TEMPLATE="mux/default.json" 35 | 36 | ## Enable JSON config for compatible clients to use mux, fragment, etc. Default False. 37 | # USE_CUSTOM_JSON_DEFAULT=True 38 | ## Your preferred config type for different clients 39 | ## If USE_CUSTOM_JSON_DEFAULT is set True, all following programs will use the JSON config 40 | # USE_CUSTOM_JSON_FOR_V2RAYN=False 41 | # USE_CUSTOM_JSON_FOR_V2RAYNG=True 42 | # USE_CUSTOM_JSON_FOR_STREISAND=False 43 | 44 | ## Set headers for subscription 45 | # SUB_PROFILE_TITLE = "Susbcription" 46 | # SUB_SUPPORT_URL = "https://t.me/support" 47 | # SUB_UPDATE_INTERVAL = "12" 48 | 49 | ## External config to import into v2ray format subscription 50 | # EXTERNAL_CONFIG = "config://..." 51 | 52 | # SQLALCHEMY_POOL_SIZE = 10 53 | # SQLIALCHEMY_MAX_OVERFLOW = 30 54 | 55 | ## Custom text for STATUS_TEXT variable 56 | # ACTIVE_STATUS_TEXT = "Active" 57 | # EXPIRED_STATUS_TEXT = "Expired" 58 | # LIMITED_STATUS_TEXT = "Limited" 59 | # DISABLED_STATUS_TEXT = "Disabled" 60 | # ONHOLD_STATUS_TEXT = "On-Hold" 61 | 62 | ### Use negative values to disable auto-delete by default 63 | # USERS_AUTODELETE_DAYS = -1 64 | # USER_AUTODELETE_INCLUDE_LIMITED_ACCOUNTS = false 65 | 66 | ## Customize all notifications 67 | # NOTIFY_STATUS_CHANGE = True 68 | # NOTIFY_USER_CREATED = True 69 | # NOTIFY_USER_UPDATED = True 70 | # NOTIFY_USER_DELETED = True 71 | # NOTIFY_USER_DATA_USED_RESET = True 72 | # NOTIFY_USER_SUB_REVOKED = True 73 | # NOTIFY_IF_DATA_USAGE_PERCENT_REACHED = True 74 | # NOTIFY_IF_DAYS_LEF_REACHED = True 75 | # NOTIFY_LOGIN = True 76 | 77 | ## Whitelist of IPs/hosts to disable login notifications 78 | # LOGIN_NOTIFY_WHITE_LIST = '1.1.1.1,127.0.0.1' 79 | 80 | ### for developers 81 | # DOCS=True 82 | # DEBUG=True 83 | 84 | # If You Want To Send Webhook To Multiple Server Add Multi Address 85 | # WEBHOOK_ADDRESS = "http://127.0.0.1:9000/,http://127.0.0.1:9001/" 86 | # WEBHOOK_SECRET = "something-very-very-secret" 87 | 88 | # VITE_BASE_API="https://example.com/api/" 89 | # JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 1440 90 | 91 | # JOB_CORE_HEALTH_CHECK_INTERVAL = 10 92 | # JOB_RECORD_NODE_USAGES_INTERVAL = 30 93 | # JOB_RECORD_USER_USAGES_INTERVAL = 10 94 | # JOB_REVIEW_USERS_INTERVAL = 10 95 | # JOB_SEND_NOTIFICATIONS_INTERVAL = 30 -------------------------------------------------------------------------------- /templates/marzban.j2: -------------------------------------------------------------------------------- 1 | SUDO_USERNAME = "xray_admin" 2 | SUDO_PASSWORD = "{{ MARZBAN_PASS }}" 3 | UVICORN_UDS = "/var/lib/marzban/marzban.socket" 4 | DASHBOARD_PATH = "/{{ MARZBAN_PATH }}/" 5 | XRAY_JSON = "xray_config.json" 6 | XRAY_SUBSCRIPTION_URL_PREFIX = "https://{{ domain }}" 7 | XRAY_SUBSCRIPTION_PATH = "{{ MARZBAN_SUB_PATH }}" 8 | SQLALCHEMY_DATABASE_URL = "sqlite:////var/lib/marzban/db.sqlite3" 9 | # ALLOWED_ORIGINS=http://localhost,http://localhost:8000,http://example.com 10 | # XRAY_EXECUTABLE_PATH = "/usr/local/bin/xray" 11 | # XRAY_ASSETS_PATH = "/usr/local/share/xray" 12 | # XRAY_EXCLUDE_INBOUND_TAGS = "INBOUND_X INBOUND_Y" 13 | # XRAY_FALLBACKS_INBOUND_TAG = "INBOUND_X" 14 | 15 | # TELEGRAM_API_TOKEN = 123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 16 | # TELEGRAM_ADMIN_ID = 987654321, 123456789 17 | # TELEGRAM_LOGGER_CHANNEL_ID = -1234567890123 18 | # TELEGRAM_DEFAULT_VLESS_FLOW = "xtls-rprx-vision" 19 | # TELEGRAM_PROXY_URL = "http://localhost:8080" 20 | 21 | # DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/xxxxxxx" 22 | 23 | CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" 24 | # CLASH_SUBSCRIPTION_TEMPLATE="clash/my-custom-template.yml" 25 | SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html" 26 | HOME_PAGE_TEMPLATE="home/index.html" 27 | 28 | # V2RAY_SUBSCRIPTION_TEMPLATE="v2ray/default.json" 29 | # V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json" 30 | 31 | # SINGBOX_SUBSCRIPTION_TEMPLATE="singbox/default.json" 32 | # SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json" 33 | 34 | # MUX_TEMPLATE="mux/default.json" 35 | 36 | ## Enable JSON config for compatible clients to use mux, fragment, etc. Default False. 37 | # USE_CUSTOM_JSON_DEFAULT=True 38 | ## Your preferred config type for different clients 39 | ## If USE_CUSTOM_JSON_DEFAULT is set True, all following programs will use the JSON config 40 | # USE_CUSTOM_JSON_FOR_V2RAYN=False 41 | # USE_CUSTOM_JSON_FOR_V2RAYNG=True 42 | # USE_CUSTOM_JSON_FOR_STREISAND=False 43 | 44 | ## Set headers for subscription 45 | # SUB_PROFILE_TITLE = "Susbcription" 46 | # SUB_SUPPORT_URL = "https://t.me/support" 47 | # SUB_UPDATE_INTERVAL = "12" 48 | 49 | ## External config to import into v2ray format subscription 50 | # EXTERNAL_CONFIG = "config://..." 51 | 52 | # SQLALCHEMY_POOL_SIZE = 10 53 | # SQLIALCHEMY_MAX_OVERFLOW = 30 54 | 55 | ## Custom text for STATUS_TEXT variable 56 | # ACTIVE_STATUS_TEXT = "Active" 57 | # EXPIRED_STATUS_TEXT = "Expired" 58 | # LIMITED_STATUS_TEXT = "Limited" 59 | # DISABLED_STATUS_TEXT = "Disabled" 60 | # ONHOLD_STATUS_TEXT = "On-Hold" 61 | 62 | ### Use negative values to disable auto-delete by default 63 | # USERS_AUTODELETE_DAYS = -1 64 | # USER_AUTODELETE_INCLUDE_LIMITED_ACCOUNTS = false 65 | 66 | ## Customize all notifications 67 | # NOTIFY_STATUS_CHANGE = True 68 | # NOTIFY_USER_CREATED = True 69 | # NOTIFY_USER_UPDATED = True 70 | # NOTIFY_USER_DELETED = True 71 | # NOTIFY_USER_DATA_USED_RESET = True 72 | # NOTIFY_USER_SUB_REVOKED = True 73 | # NOTIFY_IF_DATA_USAGE_PERCENT_REACHED = True 74 | # NOTIFY_IF_DAYS_LEF_REACHED = True 75 | # NOTIFY_LOGIN = True 76 | 77 | ## Whitelist of IPs/hosts to disable login notifications 78 | # LOGIN_NOTIFY_WHITE_LIST = '1.1.1.1,127.0.0.1' 79 | 80 | ### for developers 81 | # DOCS=True 82 | # DEBUG=True 83 | 84 | # If You Want To Send Webhook To Multiple Server Add Multi Address 85 | # WEBHOOK_ADDRESS = "http://127.0.0.1:9000/,http://127.0.0.1:9001/" 86 | # WEBHOOK_SECRET = "something-very-very-secret" 87 | 88 | # VITE_BASE_API="https://example.com/api/" 89 | # JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 1440 90 | 91 | # JOB_CORE_HEALTH_CHECK_INTERVAL = 10 92 | # JOB_RECORD_NODE_USAGES_INTERVAL = 30 93 | # JOB_RECORD_USER_USAGES_INTERVAL = 10 94 | # JOB_REVIEW_USERS_INTERVAL = 10 95 | # JOB_SEND_NOTIFICATIONS_INTERVAL = 30 -------------------------------------------------------------------------------- /templates/confluence.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Вход в Confluence 7 | 131 | 132 | 133 | 134 |
135 | 138 |

Войти в Confluence

139 |
140 | 141 | 142 | 143 |
144 |
Неправильное имя пользователя или пароль.
145 | 148 |
149 | 150 | 156 | 157 | 270 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /templates_for_script/confluence_page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Вход в Confluence 7 | 131 | 132 | 133 | 134 |
135 | 138 |

Войти в Confluence

139 |
140 | 141 | 142 | 143 |
144 |
Неправильное имя пользователя или пароль.
145 | 148 |
149 | 150 | 156 | 157 | 270 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /install_in_docker.md: -------------------------------------------------------------------------------- 1 |

VLESS + Reality Self Steal в Docker

2 | 3 | ### Что потребуется: 4 | - VPS 5 | - Свой домен 6 | 7 | В статье будет рассмотрена установка как чистого Xray, так и Marzban. 8 | 9 | ## Настройка сервера 10 | 11 | ### Настройка SSH 12 | 13 | На своем ПК, неважно, GNU/Linux или Windows. __На Windows используйте Powershell__. Открываем терминал и выполняем следующую команду: 14 | ```bash 15 | ssh-keygen -t ed25519 16 | ``` 17 | После выполнения команды вам предложат изменить место хранения ключа и добавить пароль к нему. Менять локацию не надо, пароль же можете добавить ради безопасности. 18 | Создав ключ, вам будет выведена локация публичной и приватной его части, нам нужно перекинуть публичную часть этого ключа на нашу VPS. 19 | На Linux: 20 | ```bash 21 | ssh-copy-id -i ~/.ssh/id_ed25519.pub ваш_пользователь@ваша_vps 22 | ``` 23 | На Windows: 24 | ```powershell 25 | ssh-copy-id -i $env:USERPROFILE\.ssh\id_ed25519.pub ваш_пользователь@ваша_vps 26 | ``` 27 | Если данная команда у вас не сработала на Windows, то нужно выполнить следующую: 28 | ```powershell 29 | type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh ваш_пользователь@ваша_vps "cat >> .ssh/authorized_keys" 30 | ``` 31 | __Далее все делается на VPS.__ 32 | Для отключения входа по паролю выполняем следующую команду: 33 | ```bash 34 | grep -r PasswordAuthentication /etc/ssh -l | xargs -n 1 sed -i -e "/PasswordAuthentication /c\PasswordAuthentication no" 35 | ``` 36 | Сделав это можно перезапустить SSH. 37 | ```bash 38 | sudo systemctl restart ssh 39 | ``` 40 | 41 | ### Настройки iptables 42 | Нам нужно оставить открытыми порты для SSH, 80(HTTP) и 443(HTTPS). 43 | Для этого нужно выполнить следующие команды: 44 | ```bash 45 | iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 46 | iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT 47 | iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT 48 | iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT 49 | iptables -A INPUT -i lo -j ACCEPT 50 | iptables -A OUTPUT -o lo -j ACCEPT 51 | iptables -P INPUT DROP 52 | iptables-save > /etc/network/iptables.rules 53 | ``` 54 | 55 | ### Включение BBR 56 | Достаточно выполнить следующие команды: 57 | ```bash 58 | echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf 59 | echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf 60 | sysctl -p 61 | ``` 62 | 63 | ## Создание прокси 64 | 65 | ### Установка Docker 66 | Для установки нужно выполнить следующую команду: 67 | ```bash 68 | bash <(wget -qO- https://get.docker.com) @ -o get-docker.sh 69 | ``` 70 | Если вы работаете не от админа, то выполните следующие команды, чтобы не писать `sudo` каждый раз: 71 | ```bash 72 | sudo groupadd docker 73 | sudo usermod -aG docker $USER 74 | ``` 75 | 76 | ### Получение данных для прокси 77 | В этой части будут описаны необходимые данные, а также способ их получения. Позже эти данные будут использованы в конфигурации. 78 | - __VLESS_DOMAIN__: Ваш домен. Если используется punycode, то далее используется ТОЛЬКО на латинице. 79 | - __XRAY_PBK+PIK__: `docker run --rm ghcr.io/xtls/xray-core x25519` 80 | Оба значения для нас важны, Public key = PBK, Private key = PIK. 81 | - __XRAY_SID__: `openssl rand -hex 8` 82 | Short id, используется для различения разных клиентов 83 | 84 | Следующие данные нужны только если вы будете устанавливать панель Marzban. 85 | - __MARZBAN_USER__: `tr -dc A-Za-z0-9 99 | Marzban 100 | 101 | ```yaml 102 | services: 103 | caddy: 104 | image: caddy:2.9 105 | restart: always 106 | network_mode: host 107 | volumes: 108 | - ./caddy/data:/data 109 | - ./caddy/Caddyfile:/etc/caddy/Caddyfile 110 | - ./marzban_lib:/run/marzban 111 | marzban: 112 | image: gozargah/marzban:latest 113 | restart: always 114 | env_file: ./marzban/.env 115 | network_mode: host 116 | volumes: 117 | - ./marzban_lib:/var/lib/marzban 118 | - ./marzban/xray_config.json:/code/xray_config.json 119 | - ./marzban/templates:/var/lib/marzban/templates 120 | ``` 121 | 122 |
123 | Xray 124 | 125 | ```yaml 126 | services: 127 | caddy: 128 | image: caddy:2.9 129 | restart: always 130 | network_mode: host 131 | volumes: 132 | - ./caddy/data:/data 133 | - ./caddy/Caddyfile:/etc/caddy/Caddyfile 134 | - ./caddy/templates:/srv 135 | xray: 136 | image: ghcr.io/xtls/xray-core:latest 137 | restart: always 138 | network_mode: host 139 | volumes: 140 | - ./xray:/etc/xray 141 | ``` 142 |
143 | Создаем папку `/opt/xray-vps-setup/caddy` и в ней создаем файл `Caddyfile` и меняем его следующим образом. 144 |
Marzban 145 | 146 | ```yaml 147 | { 148 | https_port 4123 149 | default_bind 127.0.0.1 150 | servers { 151 | listener_wrappers { 152 | proxy_protocol { 153 | allow 127.0.0.1/32 154 | } 155 | tls 156 | } 157 | } 158 | auto_https disable_redirects 159 | } 160 | https://$VLESS_DOMAIN { 161 | reverse_proxy * unix//run/marzban/marzban.socket 162 | } 163 | http://$VLESS_DOMAIN { 164 | bind 0.0.0.0 165 | redir https://$VLESS_DOMAIN{uri} permanent 166 | } 167 | :4123 { 168 | tls internal 169 | respond 204 170 | } 171 | :80 { 172 | bind 0.0.0.0 173 | respond 204 174 | } 175 | ``` 176 | 177 |
178 |
Чистый Xray 179 | 180 | ```yaml 181 | { 182 | https_port 4123 183 | default_bind 127.0.0.1 184 | servers { 185 | listener_wrappers { 186 | proxy_protocol { 187 | allow 127.0.0.1/32 188 | } 189 | tls 190 | } 191 | } 192 | auto_https disable_redirects 193 | } 194 | https://$VLESS_DOMAIN { 195 | root * /srv 196 | file_server 197 | } 198 | http://$VLESS_DOMAIN { 199 | bind 0.0.0.0 200 | redir https://$VLESS_DOMAIN{uri} permanent 201 | } 202 | :4123 { 203 | tls internal 204 | respond 204 205 | } 206 | :80 { 207 | bind 0.0.0.0 208 | respond 204 209 | } 210 | ``` 211 | 212 |
213 | Настроив caddy требуется добавить страницу для маскировки. Для xray и marzban команды отличаются: 214 | Xray 215 | 216 | ```bash 217 | wget -qO- https://raw.githubusercontent.com/Jolymmiles/confluence-marzban-home/main/index.html | envsubst > /opt/xray-vps-setup/caddy/templates/index.html 218 | ``` 219 | Marzban 220 | ```bash 221 | wget -qO- https://raw.githubusercontent.com/Jolymmiles/confluence-marzban-home/main/index.html | envsubst > /opt/xray-vps-setup/marzban/templates/home/index.html 222 | ``` 223 | 224 | После этого надо создать файл конфигурации Xray, если вы ставите marzban, то он будет находится в `/opt/xray-vps-setup/marzban/xray_config.json`, если чистый xray, то `/opt/xray-vps-setup/xray/config.json` 225 | 226 | ```json 227 | { 228 | "log": { 229 | "loglevel": "debug" 230 | }, 231 | "inbounds": [ 232 | { 233 | "tag": "VLESS TCP VISION REALITY", 234 | "listen": "0.0.0.0", 235 | "port": 443, 236 | "protocol": "vless", 237 | "settings": { 238 | "clients": [ 239 | { 240 | "id": "XRAY_UUDI", // ПОМЕНЯТЬ НА СВОЕ 241 | "email": "default", 242 | "flow": "xtls-rprx-vision" 243 | } 244 | ], 245 | "decryption": "none" 246 | }, 247 | "streamSettings": { 248 | "network": "tcp", 249 | "security": "reality", 250 | "realitySettings": { 251 | "xver": 1, 252 | "dest": "127.0.0.1:4123", 253 | "serverNames": [ 254 | "VLESS_DOMAIN" // ПОМЕНЯТЬ НА СВОЕ 255 | ], 256 | "privateKey": "XRAY_PIK", // ПОМЕНЯТЬ НА СВОЕ 257 | "shortIds": [ 258 | "XRAY_SID" // ПОМЕНЯТЬ НА СВОЕ 259 | ] 260 | } 261 | }, 262 | "sniffing": { 263 | "enabled": true, 264 | "destOverride": [ 265 | "http", 266 | "tls" 267 | ], 268 | "routeOnly": true 269 | } 270 | } 271 | ], 272 | "outbounds": [ 273 | { 274 | "protocol": "freedom", 275 | "tag": "direct", 276 | "settings": { 277 | "domainStrategy": "UseIPv4" 278 | } 279 | }, 280 | { 281 | "protocol": "blackhole", 282 | "tag": "block" 283 | } 284 | ], 285 | "routing": { 286 | "rules": [ 287 | { 288 | "protocol": "bittorrent", 289 | "outboundTag": "block" 290 | } 291 | ], 292 | "domainStrategy": "IPIfNonMatch" 293 | }, 294 | "dns": { 295 | "servers": [ 296 | "1.1.1.1", 297 | "8.8.8.8" 298 | ], 299 | "queryStrategy": "UseIPv4", 300 | "disableFallback": false, 301 | "tag": "dns-aux" 302 | } 303 | } 304 | ``` 305 | 306 | Для Marzban необходимо также добавить `.env` файл. Создайте файл `/opt/xray-vps-setup/marzban/.env` и вставьте следующее: 307 | ``` 308 | SUDO_USERNAME = "xray_admin" 309 | SUDO_PASSWORD = "$MARZBAN_PASS" 310 | UVICORN_UDS = "/var/lib/marzban/marzban.socket" 311 | DASHBOARD_PATH = "/$MARZBAN_PATH/" 312 | XRAY_JSON = "xray_config.json" 313 | XRAY_SUBSCRIPTION_URL_PREFIX = "https://$VLESS_DOMAIN" 314 | XRAY_SUBSCRIPTION_PATH = "$MARZBAN_SUB_PATH" 315 | SQLALCHEMY_DATABASE_URL = "sqlite:////var/lib/marzban/db.sqlite3" 316 | CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/" 317 | SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html" 318 | HOME_PAGE_TEMPLATE="home/index.html" 319 | ``` 320 | 321 | ## Настройка WARP 322 | 323 | Для того, чтобы доабвить WARP для того, чтобы в Россию наш юзер ходил черзе него, то надо сделать следующее. 324 | Устанавливаем WARP: 325 | ```bash 326 | curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg 327 | echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/cloudflare-client.list 328 | apt update 329 | apt install cloudflare-warp -y 330 | ``` 331 | Настроим WARP: 332 | ```bash 333 | warp-cli registration new 334 | warp-cli mode proxy 335 | warp-cli proxy port 40000 336 | warp-cli connect 337 | ``` 338 | Если на этом этапе ловим ошибку подключения, то не продолжайте, WARP не рабоатет. 339 | Установка `yq`: 340 | ```bash 341 | wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq 342 | ``` 343 | Далее с помощью `yq` мы установим в уже существующий кофниг WARP: 344 | ```bash 345 | yq eval '.outbounds += {"tag": "warp","protocol": "socks","settings": {"servers": [{"address": "127.0.0.1","port": 40000}]}}' -i $XRAY_CONFIG_WARP 346 | yq eval '.routing.rules += {"outboundTag": "warp", "domain": ["geosite:category-ru", "regexp:.*\\.xn--$", "regexp:.*\\.ru$", "regexp:.*\\.su$"]}' -i $XRAY_CONFIG_WARP 347 | 348 | ``` 349 | Заменяем $XRAY_CONFIG_WARP на `/opt/xray-vps-setup/marzban/xray_config.json` для marzban и на `/opt/xray-vps-setup/xray/config.json` для чистого xray. После этого перезапускаем все: 350 | ```bash 351 | docker compose -f /opt/xray-vps-setup/docker-compose.yml down && docker compose -f /opt/xray-vps-setup/docker-compose.yml up -d 352 | ``` 353 | 354 | # 355 | 356 | Если вы хотите помочь что-то исправить, добавить и тд, то делайте PR или пишите в [ТГ](https://t.me/Akiyamov). -------------------------------------------------------------------------------- /vps-setup.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | set -e 4 | 5 | export GIT_BRANCH="main" 6 | export GIT_REPO="Akiyamov/xray-vps-setup" 7 | 8 | # Check if script started as root 9 | if [ "$EUID" -ne 0 ] 10 | then echo "Please run as root" 11 | exit 12 | fi 13 | 14 | # Install idn 15 | apt-get update 16 | apt-get install idn sudo dnsutils -y 17 | 18 | # Read domain input 19 | read -ep "Enter your domain:"$'\n' input_domain 20 | 21 | export VLESS_DOMAIN=$(echo $input_domain | idn) 22 | 23 | SERVER_IPS=($(hostname -I)) 24 | 25 | RESOLVED_IP=$(dig +short $VLESS_DOMAIN | tail -n1) 26 | 27 | if [ -z "$RESOLVED_IP" ]; then 28 | echo "Warning: Domain has no DNS record" 29 | read -ep "Are you sure? That domain has no DNS record. If you didn't add that you will have to restart xray and caddy by yourself [y/N]"$'\n' prompt_response 30 | if [[ "$prompt_response" =~ ^([yY])$ ]]; then 31 | echo "Ok, proceeding without DNS verification" 32 | else 33 | echo "Come back later" 34 | exit 1 35 | fi 36 | else 37 | MATCH_FOUND=false 38 | for server_ip in "${SERVER_IPS[@]}"; do 39 | if [ "$RESOLVED_IP" == "$server_ip" ]; then 40 | MATCH_FOUND=true 41 | break 42 | fi 43 | done 44 | 45 | if [ "$MATCH_FOUND" = true ]; then 46 | echo "✓ DNS record points to this server ($RESOLVED_IP)" 47 | else 48 | echo "Warning: DNS record exists but points to different IP" 49 | echo " Domain resolves to: $RESOLVED_IP" 50 | echo " This server's IPs: ${SERVER_IPS[*]}" 51 | read -ep "Continue anyway? [y/N]"$'\n' prompt_response 52 | if [[ "$prompt_response" =~ ^([yY])$ ]]; then 53 | echo "Ok, proceeding" 54 | else 55 | echo "Come back later" 56 | exit 1 57 | fi 58 | fi 59 | fi 60 | 61 | read -ep "Do you want to install marzban? [y/N] "$'\n' marzban_input 62 | 63 | read -ep "Do you want to configure server security? Do this on first run only. [y/N] "$'\n' configure_ssh_input 64 | if [[ ${configure_ssh_input,,} == "y" ]]; then 65 | # Read SSH port 66 | read -ep "Enter SSH port. Default 22, can't use ports: 80, 443 and 4123:"$'\n' input_ssh_port 67 | 68 | while [[ "$input_ssh_port" -eq "80" || "$input_ssh_port" -eq "443" || "$input_ssh_port" -eq "4123" ]]; do 69 | read -ep "No, ssh can't use $input_ssh_port as port, write again:"$'\n' input_ssh_port 70 | done 71 | # Read SSH Pubkey 72 | read -ep "Enter SSH public key:"$'\n' input_ssh_pbk 73 | echo "$input_ssh_pbk" > ./test_pbk 74 | ssh-keygen -l -f ./test_pbk 75 | PBK_STATUS=$(echo $?) 76 | if [ "$PBK_STATUS" -eq 255 ]; then 77 | echo "Can't verify the public key. Try again and make sure to include 'ssh-rsa' or 'ssh-ed25519' followed by 'user@pcname' at the end of the file." 78 | exit 79 | fi 80 | rm ./test_pbk 81 | fi 82 | 83 | read -ep "Do you want to install WARP and use it on russian websites? [y/N] "$'\n' configure_warp_input 84 | if [[ ${configure_warp_input,,} == "y" ]]; then 85 | if ! curl -I https://api.cloudflareclient.com --connect-timeout 10 > /dev/null 2>&1; then 86 | echo "Warp can't be used" 87 | configure_warp_input="n" 88 | fi 89 | fi 90 | 91 | # Check congestion protocol 92 | if sysctl net.ipv4.tcp_congestion_control | grep bbr; then 93 | echo "BBR is already used" 94 | else 95 | echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf 96 | echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf 97 | sysctl -p > /dev/null 98 | echo "Enabled BBR" 99 | fi 100 | 101 | export ARCH=$(dpkg --print-architecture) 102 | 103 | yq_install() { 104 | wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_$ARCH -O /usr/bin/yq && chmod +x /usr/bin/yq 105 | } 106 | 107 | yq_install 108 | 109 | docker_install() { 110 | bash <(wget -qO- https://get.docker.com) @ -o get-docker.sh 111 | } 112 | 113 | if ! command -v docker 2>&1 >/dev/null; then 114 | docker_install 115 | fi 116 | 117 | # Generate values for XRay 118 | export SSH_USER=$(tr -dc A-Za-z0-9 ./docker-compose.yml 139 | yq eval \ 140 | '.services.marzban.image = "gozargah/marzban:v0.8.4" | 141 | .services.marzban.container_name = "marzban" | 142 | .services.marzban.restart = "always" | 143 | .services.marzban.env_file = "./marzban/.env" | 144 | .services.marzban.network_mode = "host" | 145 | .services.marzban.volumes[0] = "./marzban_lib:/var/lib/marzban" | 146 | .services.marzban.volumes[1] = "./marzban/xray_config.json:/code/xray_config.json" | 147 | .services.marzban.volumes[2] = "./marzban/templates:/var/lib/marzban/templates" | 148 | .services.caddy.volumes[2] = "./marzban_lib:/run/marzban"' -i /opt/xray-vps-setup/docker-compose.yml 149 | mkdir -p marzban caddy 150 | wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/marzban | envsubst > ./marzban/.env 151 | mkdir -p /opt/xray-vps-setup/marzban/templates/home 152 | wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/confluence_page | envsubst > ./marzban/templates/home/index.html 153 | export CADDY_REVERSE="reverse_proxy * unix//run/marzban/marzban.socket" 154 | wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/caddy" | envsubst > ./caddy/Caddyfile 155 | wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/xray" | envsubst > ./marzban/xray_config.json 156 | else 157 | wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/compose | envsubst > ./docker-compose.yml 158 | mkdir -p /opt/xray-vps-setup/caddy/templates 159 | yq eval \ 160 | '.services.xray.image = "ghcr.io/xtls/xray-core:25.6.8" | 161 | .services.xray.container_name = "xray" | 162 | .services.xray.user = "root" | 163 | .services.xray.command = "run -c /etc/xray/config.json" | 164 | .services.xray.restart = "always" | 165 | .services.xray.network_mode = "host" | 166 | .services.caddy.volumes[2] = "./caddy/templates:/srv" | 167 | .services.xray.volumes[0] = "./xray:/etc/xray"' -i /opt/xray-vps-setup/docker-compose.yml 168 | wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/confluence_page | envsubst > ./caddy/templates/index.html 169 | export CADDY_REVERSE="root * /srv 170 | file_server" 171 | mkdir -p xray caddy 172 | wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/xray" | envsubst > ./xray/config.json 173 | wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/caddy" | envsubst > ./caddy/Caddyfile 174 | fi 175 | } 176 | 177 | xray_setup 178 | 179 | sshd_edit() { 180 | grep -r Port /etc/ssh -l | xargs -n 1 sed -i -e "/Port /c\Port $SSH_PORT" 181 | grep -r PasswordAuthentication /etc/ssh -l | xargs -n 1 sed -i -e "/PasswordAuthentication /c\PasswordAuthentication no" 182 | grep -r PermitRootLogin /etc/ssh -l | xargs -n 1 sed -i -e "/PermitRootLogin /c\PermitRootLogin no" 183 | systemctl daemon-reload 184 | systemctl restart ssh 185 | } 186 | 187 | add_user() { 188 | useradd $SSH_USER -s /bin/bash 189 | usermod -aG sudo $SSH_USER 190 | echo $SSH_USER:$SSH_USER_PASS | chpasswd 191 | mkdir -p /home/$SSH_USER/.ssh 192 | touch /home/$SSH_USER/.ssh/authorized_keys 193 | echo $input_ssh_pbk >> /home/$SSH_USER/.ssh/authorized_keys 194 | chmod 700 /home/$SSH_USER/.ssh/ 195 | chmod 600 /home/$SSH_USER/.ssh/authorized_keys 196 | chown $SSH_USER:$SSH_USER -R /home/$SSH_USER 197 | usermod -aG docker $SSH_USER 198 | } 199 | 200 | debconf-set-selections <