├── 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 |