diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml
index e69de29..d69b160 100644
--- a/inventory/group_vars/all.yml
+++ b/inventory/group_vars/all.yml
@@ -0,0 +1,2 @@
+---
+ssh_public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQChdhIFLJ1nwmqkFJzgHKvPJXPdGRZ3tmN0IqAJyP61DSsQf4rJcw4fL9Z4UKGfIw4Mn4f8FFcAUCmD/2OZxSKjjQBYiq/+q3AS12PVUbDR3fE5IyZx3uDmAB3m9yJuIFGl3mqldefnnH98UA3+kwZgFoJXJwHgp6mTV+S5N0aJdX1gCwoVMz3imkUAkGQrK4eDp9T4YH94yVh8or3si+mQJe+5j5xjF7oFGWWVYIr7iNS6zrtgRQdFklAt8cFIx2Mxql6PuTNJo83zS1NUxijEEGC/aZMLhafsOWm8M63EvkXsGOYS2JN5ycKJaTTDiD22tyXP4a1Oe6SGzB7of2NJ9RUXYnq7IYNlMpLJ4D7tHyYJUEckRQy9yugYWSUFxPgLc6n1PxDUEk1hTVwm5xsha9kYYXtuAY4RI3uXkvYIMsuO92kwqnOwt6Yw285DrXQQYSLt2NGBYvGfVczrSM8TYwsCZ0wHOaNeZKieeOmNDeHcOX8smSsiYlF08NOJ6pk= stanito@MacM5"
\ No newline at end of file
diff --git a/inventory/hosts b/inventory/hosts
index e68604d..3a9d4f6 100644
--- a/inventory/hosts
+++ b/inventory/hosts
@@ -8,7 +8,7 @@ ansible_port=22
ansible_ssh_private_key_file=~/.ssh/id_rsa
# VPN серверы
-[vpnservers]
+[vpn]
access.stanito.me ansible_user=root
# Группы по ОС
diff --git a/playbooks/vpn.yml b/playbooks/vpn.yml
new file mode 100644
index 0000000..8495251
--- /dev/null
+++ b/playbooks/vpn.yml
@@ -0,0 +1,12 @@
+- name: Setup VLESS VPN server
+ hosts: vpn
+ become: yes
+
+ roles:
+ - role: xray-vps-setup
+
+ vars:
+ domain: access.stanito.me
+ setup_variant: xray
+ user_to_create: vpnuser
+ ssh_public_key: "{{ ssh_public_key }}"
\ No newline at end of file
diff --git a/requirements.yml b/requirements.yml
index 6408107..b11a499 100644
--- a/requirements.yml
+++ b/requirements.yml
@@ -2,6 +2,11 @@
roles:
# Install a role from Ansible Galaxy.
# note that ranges are not supported for roles
+ - name: xray-vps-setup
+ src: https://github.com/Akiyamov/xray-vps-setup.git
+ scm: git
+ version: master
+
- name: geerlingguy.certbot
version: "5.4.1"
diff --git a/roles/xray-vps-setup/.gitignore b/roles/xray-vps-setup/.gitignore
new file mode 100644
index 0000000..3a74357
--- /dev/null
+++ b/roles/xray-vps-setup/.gitignore
@@ -0,0 +1,4 @@
+gist*.md
+ansible.cfg
+inventory.yml
+playbook.yml
\ No newline at end of file
diff --git a/roles/xray-vps-setup/README.md b/roles/xray-vps-setup/README.md
new file mode 100644
index 0000000..2dbb7a2
--- /dev/null
+++ b/roles/xray-vps-setup/README.md
@@ -0,0 +1,60 @@
+# xray-vps-setup
+VLESS со своим доменом. А что еще нужно для счастья?
+
+В данном варианте VLESS слушает на 443 и принимате все запросы, делая запрос на локальный Caddy только для сертификатов. В таком варианте задержка будет меньше, чем в варианте с Caddy/NGINX перед VLESS, где происходит множество лишних запросов.
+## Скрипт
+
+- Установит Xray/Marzban на ваш выбор. Для маскировки страницы используется [Conflunce](https://github.com/Jolymmiles/confluence-marzban-home)
+- На ваше усмотрение настроит:
+- - Iptables, запретив все подключения, кроме SSH, 80 и 443.
+- - Создаст пользователя для подключения, запретив вход от рута
+- - Добавит этому пользователю ключ для SSH, запретив вход по паролю
+- Настроит WARP для ру-сайтов.
+```bash
+tmux
+bash <(wget -qO- https://raw.githubusercontent.com/Akiyamov/xray-vps-setup/refs/heads/main/vps-setup.sh)
+```
+
+## Плейбук
+
+[Ansible-galaxy](https://galaxy.ansible.com/ui/standalone/roles/Akiyamov/xray-vps-setup/install/)
+```yaml
+- name: Setup vps
+ hosts: some_host
+ roles:
+ - Akiyamov.xray-vps-setup
+ vars:
+ domain: example.com # домен, уровень неважен
+ setup_variant: marzban # marzban or xray
+ setup_warp: false # true or false
+ configure_security: true # true or false
+ user_to_create: xray_user # если configure_security: true, то обязательно
+ user_password: "xray_password" # если configure_security: true, то обязательно
+ SSH_PORT: 22 # если configure_security: true, то обязательно
+ ssh_public_key: "" # если configure_security: true, то обязательно
+```
+
+## Добавляем подписку и поддержку Mihomo
+
+```
+bash <(wget -qO- https://github.com/legiz-ru/marz-sub/raw/main/marz-sub.sh)
+```
+После этого сделайте `docker compose -f /opt/xray-vps-setup/docker-compose.yml down && docker compose -f /opt/xray-vps-setup/docker-compose.yml up -d`
+
+
+## Ручная установка
+
+Описана [здесь](https://github.com/Akiyamov/xray-vps-setup/blob/main/install_in_docker.md).
+
+## Почему не nginx, haproxy, 3x-ui, x-ui, sing-box...
+
+Caddy сам получит сертификаты, поэтому нам не придется их получать через `acme.sh` или `certbot`.
+3X-ui мерзотная панель.
+Sing-box не очень.
+XHTTP позже, а больше не надо. Уже точно.
+
+## Связь
+Issues, PR ну или мой [тг](https://t.me/Akiyamov).
+
+> [!IMPORTANT]
+> Дайте секс
\ No newline at end of file
diff --git a/roles/xray-vps-setup/defaults/main.yml b/roles/xray-vps-setup/defaults/main.yml
new file mode 100644
index 0000000..3adfd95
--- /dev/null
+++ b/roles/xray-vps-setup/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+# defaults file for vps-setup
diff --git a/roles/xray-vps-setup/handlers/main.yml b/roles/xray-vps-setup/handlers/main.yml
new file mode 100644
index 0000000..0afe763
--- /dev/null
+++ b/roles/xray-vps-setup/handlers/main.yml
@@ -0,0 +1,15 @@
+---
+# handlers file for vps-setup
+- name: Restart xray
+ service:
+ name: xray
+ state: restarted
+- name: Restart caddy
+ service:
+ name: caddy
+ state: restarted
+- name: Restart ssh
+ service:
+ name: ssh
+ state: restarted
+ daemon_reload: true
\ No newline at end of file
diff --git a/roles/xray-vps-setup/install_in_docker.md b/roles/xray-vps-setup/install_in_docker.md
new file mode 100644
index 0000000..fa02f3f
--- /dev/null
+++ b/roles/xray-vps-setup/install_in_docker.md
@@ -0,0 +1,356 @@
+
VLESS + Reality Self Steal в Docker
+
+### Что потребуется:
+- VPS
+- Свой домен
+
+В статье будет рассмотрена установка как чистого Xray, так и Marzban.
+
+## Настройка сервера
+
+### Настройка SSH
+
+На своем ПК, неважно, GNU/Linux или Windows. __На Windows используйте Powershell__. Открываем терминал и выполняем следующую команду:
+```bash
+ssh-keygen -t ed25519
+```
+После выполнения команды вам предложат изменить место хранения ключа и добавить пароль к нему. Менять локацию не надо, пароль же можете добавить ради безопасности.
+Создав ключ, вам будет выведена локация публичной и приватной его части, нам нужно перекинуть публичную часть этого ключа на нашу VPS.
+На Linux:
+```bash
+ssh-copy-id -i ~/.ssh/id_ed25519.pub ваш_пользователь@ваша_vps
+```
+На Windows:
+```powershell
+ssh-copy-id -i $env:USERPROFILE\.ssh\id_ed25519.pub ваш_пользователь@ваша_vps
+```
+Если данная команда у вас не сработала на Windows, то нужно выполнить следующую:
+```powershell
+type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh ваш_пользователь@ваша_vps "cat >> .ssh/authorized_keys"
+```
+__Далее все делается на VPS.__
+Для отключения входа по паролю выполняем следующую команду:
+```bash
+grep -r PasswordAuthentication /etc/ssh -l | xargs -n 1 sed -i -e "/PasswordAuthentication /c\PasswordAuthentication no"
+```
+Сделав это можно перезапустить SSH.
+```bash
+sudo systemctl restart ssh
+```
+
+### Настройки iptables
+Нам нужно оставить открытыми порты для SSH, 80(HTTP) и 443(HTTPS).
+Для этого нужно выполнить следующие команды:
+```bash
+iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
+iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
+iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
+iptables -A INPUT -i lo -j ACCEPT
+iptables -A OUTPUT -o lo -j ACCEPT
+iptables -P INPUT DROP
+iptables-save > /etc/network/iptables.rules
+```
+
+### Включение BBR
+Достаточно выполнить следующие команды:
+```bash
+echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
+echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
+sysctl -p
+```
+
+## Создание прокси
+
+### Установка Docker
+Для установки нужно выполнить следующую команду:
+```bash
+bash <(wget -qO- https://get.docker.com) @ -o get-docker.sh
+```
+Если вы работаете не от админа, то выполните следующие команды, чтобы не писать `sudo` каждый раз:
+```bash
+sudo groupadd docker
+sudo usermod -aG docker $USER
+```
+
+### Получение данных для прокси
+В этой части будут описаны необходимые данные, а также способ их получения. Позже эти данные будут использованы в конфигурации.
+- __VLESS_DOMAIN__: Ваш домен. Если используется punycode, то далее используется ТОЛЬКО на латинице.
+- __XRAY_PBK+PIK__: `docker run --rm ghcr.io/xtls/xray-core x25519`
+Оба значения для нас важны, Public key = PBK, Private key = PIK.
+- __XRAY_SID__: `openssl rand -hex 8`
+Short id, используется для различения разных клиентов
+
+Следующие данные нужны только если вы будете устанавливать панель Marzban.
+- __MARZBAN_USER__: `tr -dc A-Za-z0-9
+ Marzban
+
+```yaml
+services:
+ caddy:
+ image: caddy:2.9
+ restart: always
+ network_mode: host
+ volumes:
+ - ./caddy/data:/data
+ - ./caddy/Caddyfile:/etc/caddy/Caddyfile
+ - ./marzban_lib:/run/marzban
+ marzban:
+ image: gozargah/marzban:latest
+ restart: always
+ env_file: ./marzban/.env
+ network_mode: host
+ volumes:
+ - ./marzban_lib:/var/lib/marzban
+ - ./marzban/xray_config.json:/code/xray_config.json
+ - ./marzban/templates:/var/lib/marzban/templates
+```
+
+
+ Xray
+
+```yaml
+services:
+ caddy:
+ image: caddy:2.9
+ restart: always
+ network_mode: host
+ volumes:
+ - ./caddy/data:/data
+ - ./caddy/Caddyfile:/etc/caddy/Caddyfile
+ - ./caddy/templates:/srv
+ xray:
+ image: ghcr.io/xtls/xray-core:latest
+ restart: always
+ network_mode: host
+ volumes:
+ - ./xray:/etc/xray
+```
+
+Создаем папку `/opt/xray-vps-setup/caddy` и в ней создаем файл `Caddyfile` и меняем его следующим образом.
+Marzban
+
+```yaml
+{
+ https_port 4123
+ default_bind 127.0.0.1
+ servers {
+ listener_wrappers {
+ proxy_protocol {
+ allow 127.0.0.1/32
+ }
+ tls
+ }
+ }
+ auto_https disable_redirects
+}
+https://$VLESS_DOMAIN {
+ reverse_proxy * unix//run/marzban/marzban.socket
+}
+http://$VLESS_DOMAIN {
+ bind 0.0.0.0
+ redir https://$VLESS_DOMAIN{uri} permanent
+}
+:4123 {
+ tls internal
+ respond 204
+}
+:80 {
+ bind 0.0.0.0
+ respond 204
+}
+```
+
+
+Чистый Xray
+
+```yaml
+{
+ https_port 4123
+ default_bind 127.0.0.1
+ servers {
+ listener_wrappers {
+ proxy_protocol {
+ allow 127.0.0.1/32
+ }
+ tls
+ }
+ }
+ auto_https disable_redirects
+}
+https://$VLESS_DOMAIN {
+ root * /srv
+ file_server
+}
+http://$VLESS_DOMAIN {
+ bind 0.0.0.0
+ redir https://$VLESS_DOMAIN{uri} permanent
+}
+:4123 {
+ tls internal
+ respond 204
+}
+:80 {
+ bind 0.0.0.0
+ respond 204
+}
+```
+
+
+Настроив caddy требуется добавить страницу для маскировки. Для xray и marzban команды отличаются:
+Xray
+
+```bash
+wget -qO- https://raw.githubusercontent.com/Jolymmiles/confluence-marzban-home/main/index.html | envsubst > /opt/xray-vps-setup/caddy/templates/index.html
+```
+Marzban
+```bash
+wget -qO- https://raw.githubusercontent.com/Jolymmiles/confluence-marzban-home/main/index.html | envsubst > /opt/xray-vps-setup/marzban/templates/home/index.html
+```
+
+После этого надо создать файл конфигурации Xray, если вы ставите marzban, то он будет находится в `/opt/xray-vps-setup/marzban/xray_config.json`, если чистый xray, то `/opt/xray-vps-setup/xray/config.json`
+
+```json
+{
+ "log": {
+ "loglevel": "debug"
+ },
+ "inbounds": [
+ {
+ "tag": "VLESS TCP VISION REALITY",
+ "listen": "0.0.0.0",
+ "port": 443,
+ "protocol": "vless",
+ "settings": {
+ "clients": [
+ {
+ "id": "XRAY_UUDI", // ПОМЕНЯТЬ НА СВОЕ
+ "email": "default",
+ "flow": "xtls-rprx-vision"
+ }
+ ],
+ "decryption": "none"
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "security": "reality",
+ "realitySettings": {
+ "xver": 1,
+ "dest": "127.0.0.1:4123",
+ "serverNames": [
+ "VLESS_DOMAIN" // ПОМЕНЯТЬ НА СВОЕ
+ ],
+ "privateKey": "XRAY_PIK", // ПОМЕНЯТЬ НА СВОЕ
+ "shortIds": [
+ "XRAY_SID" // ПОМЕНЯТЬ НА СВОЕ
+ ]
+ }
+ },
+ "sniffing": {
+ "enabled": true,
+ "destOverride": [
+ "http",
+ "tls"
+ ],
+ "routeOnly": true
+ }
+ }
+ ],
+ "outbounds": [
+ {
+ "protocol": "freedom",
+ "tag": "direct",
+ "settings": {
+ "domainStrategy": "UseIPv4"
+ }
+ },
+ {
+ "protocol": "blackhole",
+ "tag": "block"
+ }
+ ],
+ "routing": {
+ "rules": [
+ {
+ "protocol": "bittorrent",
+ "outboundTag": "block"
+ }
+ ],
+ "domainStrategy": "IPIfNonMatch"
+ },
+ "dns": {
+ "servers": [
+ "1.1.1.1",
+ "8.8.8.8"
+ ],
+ "queryStrategy": "UseIPv4",
+ "disableFallback": false,
+ "tag": "dns-aux"
+ }
+}
+```
+
+Для Marzban необходимо также добавить `.env` файл. Создайте файл `/opt/xray-vps-setup/marzban/.env` и вставьте следующее:
+```
+SUDO_USERNAME = "xray_admin"
+SUDO_PASSWORD = "$MARZBAN_PASS"
+UVICORN_UDS = "/var/lib/marzban/marzban.socket"
+DASHBOARD_PATH = "/$MARZBAN_PATH/"
+XRAY_JSON = "xray_config.json"
+XRAY_SUBSCRIPTION_URL_PREFIX = "https://$VLESS_DOMAIN"
+XRAY_SUBSCRIPTION_PATH = "$MARZBAN_SUB_PATH"
+SQLALCHEMY_DATABASE_URL = "sqlite:////var/lib/marzban/db.sqlite3"
+CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/"
+SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html"
+HOME_PAGE_TEMPLATE="home/index.html"
+```
+
+## Настройка WARP
+
+Для того, чтобы доабвить WARP для того, чтобы в Россию наш юзер ходил черзе него, то надо сделать следующее.
+Устанавливаем WARP:
+```bash
+curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
+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
+apt update
+apt install cloudflare-warp -y
+```
+Настроим WARP:
+```bash
+warp-cli registration new
+warp-cli mode proxy
+warp-cli proxy port 40000
+warp-cli connect
+```
+Если на этом этапе ловим ошибку подключения, то не продолжайте, WARP не рабоатет.
+Установка `yq`:
+```bash
+wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && chmod +x /usr/bin/yq
+```
+Далее с помощью `yq` мы установим в уже существующий кофниг WARP:
+```bash
+yq eval '.outbounds += {"tag": "warp","protocol": "socks","settings": {"servers": [{"address": "127.0.0.1","port": 40000}]}}' -i $XRAY_CONFIG_WARP
+yq eval '.routing.rules += {"outboundTag": "warp", "domain": ["geosite:category-ru", "regexp:.*\\.xn--$", "regexp:.*\\.ru$", "regexp:.*\\.su$"]}' -i $XRAY_CONFIG_WARP
+
+```
+Заменяем $XRAY_CONFIG_WARP на `/opt/xray-vps-setup/marzban/xray_config.json` для marzban и на `/opt/xray-vps-setup/xray/config.json` для чистого xray. После этого перезапускаем все:
+```bash
+docker compose -f /opt/xray-vps-setup/docker-compose.yml down && docker compose -f /opt/xray-vps-setup/docker-compose.yml up -d
+```
+
+#
+
+Если вы хотите помочь что-то исправить, добавить и тд, то делайте PR или пишите в [ТГ](https://t.me/Akiyamov).
\ No newline at end of file
diff --git a/roles/xray-vps-setup/meta/.galaxy_install_info b/roles/xray-vps-setup/meta/.galaxy_install_info
new file mode 100644
index 0000000..7567566
--- /dev/null
+++ b/roles/xray-vps-setup/meta/.galaxy_install_info
@@ -0,0 +1,2 @@
+install_date: Mon Feb 9 17:35:32 2026
+version: null
diff --git a/roles/xray-vps-setup/meta/main.yml b/roles/xray-vps-setup/meta/main.yml
new file mode 100644
index 0000000..8e546c9
--- /dev/null
+++ b/roles/xray-vps-setup/meta/main.yml
@@ -0,0 +1,18 @@
+galaxy_info:
+ author: Akiyamov
+ description: Setup VPS for XRay
+ company: your company (optional)
+ license: BSD-3-Clause
+ min_ansible_version: 2.1
+ platforms:
+ - name: Ubuntu
+ versions:
+ - 16
+ - 18
+ - 20
+ - 22
+ - 24
+ galaxy_tags:
+ - xray
+ - vps
+dependencies: []
diff --git a/roles/xray-vps-setup/tasks/bbr.yml b/roles/xray-vps-setup/tasks/bbr.yml
new file mode 100644
index 0000000..9746ffc
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/bbr.yml
@@ -0,0 +1,10 @@
+- name: Set BBR
+ ansible.posix.sysctl:
+ name: net.core.default_qdisc
+ value: "fq"
+ state: present
+- name: Set queue
+ ansible.posix.sysctl:
+ name: net.ipv4.tcp_congestion_control
+ value: "bbr"
+ state: present
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/end_xray.yml b/roles/xray-vps-setup/tasks/end_xray.yml
new file mode 100644
index 0000000..8c82448
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/end_xray.yml
@@ -0,0 +1,63 @@
+- name: Print clipboard string
+ debug:
+ 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"
+- name: XRay outbound config
+ debug:
+ msg: |
+ {
+ "tag": "default",
+ "protocol": "vless",
+ "settings": {
+ "vnext": [
+ {
+ "address": "{{ vless.domain }}",
+ "port": 443,
+ "users": [
+ {
+ "id": "{{ xray_uuid.stdout }}",
+ "encryption": "none",
+ "flow": "xtls-rprx-vision"
+ }
+ ]
+ }
+ ]
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "security": "reality",
+ "realitySettings": {
+ "serverName": "{{ vless.domain }}",
+ "fingerprint": "chrome",
+ "publicKey": "{{ x25519_pbk.stdout }}",
+ "shortId": "{{ short_id.stdout }}",
+ "spiderX": ""
+ }
+ }
+ }
+- name: Sing-box outbound config
+ debug:
+ msg: |
+ {
+ "type": "vless",
+ "server": "{{ vless.domain }}",
+ "server_port": 443,
+ "uuid": "{{ xray_uuid.stdout }}",
+ "flow": "xtls-rprx-vision",
+ "tls": {
+ "enabled": true,
+ "insecure": false,
+ "server_name": "{{ vless.domain }}",
+ "utls": {
+ "enabled": true,
+ "fingerprint": "chrome"
+ },
+ "reality": {
+ "enabled": true,
+ "public_key": "{{ x25519_pbk.stdout }}",
+ "short_id": "{{ short_id.stdout }}"
+ }
+ }
+ }
+- name: Print PBK, SID and UUID to connect to server.
+ debug:
+ msg: "UUID: {{ xray_uuid.stdout }}, SID: {{ short_id.stdout }}, PBK: {{ x25519_pbk.stdout }}"
diff --git a/roles/xray-vps-setup/tasks/install_docker.yml b/roles/xray-vps-setup/tasks/install_docker.yml
new file mode 100644
index 0000000..33630b5
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/install_docker.yml
@@ -0,0 +1,13 @@
+- name: Add Docker GPG apt Key
+ ansible.builtin.apt_key:
+ url: https://download.docker.com/linux/ubuntu/gpg
+ state: present
+- name: Add Docker Repository
+ ansible.builtin.apt_repository:
+ repo: deb https://download.docker.com/linux/ubuntu focal stable
+ state: present
+- name: Update install docker-ce
+ ansible.builtin.apt:
+ name: docker-ce
+ state: latest
+ update_cache: true
diff --git a/roles/xray-vps-setup/tasks/install_marzban.yml b/roles/xray-vps-setup/tasks/install_marzban.yml
new file mode 100644
index 0000000..1b66ceb
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/install_marzban.yml
@@ -0,0 +1,31 @@
+- name: Generate marzban specific values
+ block:
+ - name: Generate marzban password
+ set_fact:
+ MARZBAN_PASS: "{{ lookup('password', '/dev/null length=13 chars=ascii_letters') }}"
+ - name: Generate marzban password
+ set_fact:
+ MARZBAN_PATH: "{{ lookup('password', '/dev/null length=8 chars=ascii_letters') }}"
+ - name: Generate marzban password
+ set_fact:
+ MARZBAN_SUB_PATH: "{{ lookup('password', '/dev/null length=8 chars=ascii_letters') }}"
+- name: Create dirs
+ file:
+ path: "{{ item }}"
+ state: directory
+ loop:
+ - /opt/xray-vps-setup/caddy
+ - /opt/xray-vps-setup/marzban
+ - /opt/xray-vps-setup/marzban/templates/home
+- name: Copy config files
+ ansible.builtin.template:
+ src: "{{ item.src }}"
+ dest: "{{ item.dest }}"
+ loop:
+ - { src: "caddyfile.j2", dest: "/opt/xray-vps-setup/caddy/Caddyfile" }
+ - { src: "xray.json.j2", dest: "/opt/xray-vps-setup/marzban/xray_config.json" }
+ - { src: "marzban.j2", dest: "/opt/xray-vps-setup/marzban/.env" }
+ - { src: "confluence.j2", dest: "/opt/xray-vps-setup/marzban/templates/home/index.html" }
+ - { src: "marzban_docker.j2", dest: "/opt/xray-vps-setup/docker-compose.yml" }
+- debug:
+ msg: "Marzban password: {{ MARZBAN_PASS }}, marzban path: {{ MARZBAN_PATH }}"
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/install_xray.yml b/roles/xray-vps-setup/tasks/install_xray.yml
new file mode 100644
index 0000000..8a20b77
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/install_xray.yml
@@ -0,0 +1,16 @@
+- name: Create dirs
+ file:
+ path: "{{ item }}"
+ state: directory
+ loop:
+ - /opt/xray-vps-setup/caddy/templates
+ - /opt/xray-vps-setup/xray
+- name: Copy config files
+ ansible.builtin.template:
+ src: "{{ item.src }}"
+ dest: "{{ item.dest }}"
+ loop:
+ - { src: "caddyfile.j2", dest: "/opt/xray-vps-setup/caddy/Caddyfile" }
+ - { src: "confluence.j2", dest: "/opt/xray-vps-setup/caddy/templates/index.html" }
+ - { src: "xray.json.j2", dest: "/opt/xray-vps-setup/xray/config.json" }
+ - { src: "xray_docker.j2", dest: "/opt/xray-vps-setup/docker-compose.yml" }
diff --git a/roles/xray-vps-setup/tasks/install_yq.yml b/roles/xray-vps-setup/tasks/install_yq.yml
new file mode 100644
index 0000000..41e2c20
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/install_yq.yml
@@ -0,0 +1,5 @@
+- name: Download yq
+ ansible.builtin.get_url:
+ url: https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
+ dest: /usr/bin/yq
+ mode: '0755'
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/iptables.yml b/roles/xray-vps-setup/tasks/iptables.yml
new file mode 100644
index 0000000..3ecd1f6
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/iptables.yml
@@ -0,0 +1,46 @@
+- name: IPTables rules
+ block:
+ - name: Install netfilter-persistent
+ apt:
+ name: netfilter-persistent
+ state: present
+ - name: Allow related and established connections
+ ansible.builtin.iptables:
+ chain: INPUT
+ ctstate: ESTABLISHED,RELATED
+ jump: ACCEPT
+ become: yes
+ - name: Allow new incoming SYN packets on specified port
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: tcp
+ destination_port: "{{ SSH_PORT }}"
+ ctstate: NEW
+ syn: match
+ jump: ACCEPT
+ - name: Allow ICMP
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: icmp
+ jump: ACCEPT
+ - name: Allow 80, 443 connections
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: tcp
+ destination_ports:
+ - "80"
+ - "443"
+ jump: ACCEPT
+ - name: Allow loopback in
+ shell:
+ cmd: iptables -A INPUT -i lo -j ACCEPT
+ - name: Allow loopback out
+ shell:
+ cmd: iptables -A OUTPUT -o lo -j ACCEPT
+ - name: INPUT DROP
+ ansible.builtin.iptables:
+ chain: INPUT
+ policy: DROP
+ - name: Save iptables rules
+ shell:
+ cmd: netfilter-persistent save
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/main.yml b/roles/xray-vps-setup/tasks/main.yml
new file mode 100644
index 0000000..b429189
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/main.yml
@@ -0,0 +1,54 @@
+---
+- name: Populate service facts
+ ansible.builtin.service_facts:
+- name: Enable BBR
+ include_tasks: bbr.yml
+- name: Install docker
+ include_tasks: install_docker.yml
+ when: ansible_facts.services['docker'] is undefined
+- name: Install/update yq
+ include_tasks: install_yq.yml
+- name: Security block
+ block:
+ - name: Edit SSHD config
+ include_tasks: ssh.yml
+ - name: Edit iptables
+ include_tasks: iptables.yml
+ - name: Add user
+ include_tasks: user.yml
+ when: configure_security|default(false)|bool == true
+- name: Generate values
+ block:
+ - name: Generate x25519 PIK
+ shell:
+ cmd: docker run --rm ghcr.io/xtls/xray-core x25519 | head -n1 | cut -d' ' -f 3
+ register: x25519_pik
+ - name: Generate x25519 PBK
+ shell:
+ cmd: docker run --rm ghcr.io/xtls/xray-core x25519 -i {{ x25519_pik.stdout }} | tail -1 | cut -d' ' -f 3
+ register: x25519_pbk
+ - name: Generate SID
+ shell:
+ cmd: openssl rand -hex 8
+ register: short_id
+ - name: Generate default user
+ shell:
+ cmd: docker run --rm ghcr.io/xtls/xray-core uuid
+ register: xray_uuid
+- name: Install marzban
+ include_tasks: install_marzban.yml
+ when: setup_variant == "marzban"
+- name: Install xray
+ include_tasks: install_xray.yml
+ when: setup_variant == "xray"
+- name: Install warp
+ include_tasks: setup_warp.yml
+ when: setup_warp|default(false)|bool == true
+- name: Start proxy
+ community.docker.docker_compose_v2:
+ project_src: /opt/xray-vps-setup
+ files:
+ - docker-compose.yml
+- name: End xray
+ include_tasks: end_xray.yml
+ when: setup_variant == "xray"
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/setup_warp.yml b/roles/xray-vps-setup/tasks/setup_warp.yml
new file mode 100644
index 0000000..5c638d3
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/setup_warp.yml
@@ -0,0 +1,61 @@
+#- name: Add WARP GPG key
+# ansible.builtin.get_url:
+# url: https://pkg.cloudflareclient.com/pubkey.gpg
+# dest: /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
+# mode: '0644'
+# force: true
+- name: Add WARP GPG key
+ ansible.builtin.shell:
+ cmd: curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
+- name: Add WARP repo
+ ansible.builtin.apt_repository:
+ filename: cloudflare-client
+ repo: "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ {{ ansible_facts['distribution_release'] }} main"
+- name:
+ apt:
+ name: cloudflare-warp
+ state: present
+ update_cache: yes
+- name: Register WARP
+ shell:
+ cmd: echo "y" | warp-cli registration new
+- shell:
+ cmd: warp-cli mode proxy
+- shell:
+ cmd: warp-cli proxy port 40000
+- shell:
+ cmd: warp-cli connect
+- name: Edit xray config
+ block:
+ - command:
+ argv:
+ - yq
+ - eval
+ - '.outbounds += {"tag": "warp","protocol": "socks","settings": {"servers": [{"address": "127.0.0.1","port": 40000}]}}'
+ - -i
+ - /opt/xray-vps-setup/xray/config.json
+ - command:
+ argv:
+ - yq
+ - eval
+ - '.routing.rules += {"outboundTag": "warp", "domain": ["geosite:category-ru", "regexp:.*\\.xn--$", "regexp:.*\\.ru$", "regexp:.*\\.su$"]}'
+ - -i
+ - /opt/xray-vps-setup/xray/config.json
+ when: setup_variant == "xray"
+- name: Edit marzban config
+ block:
+ - command:
+ argv:
+ - yq
+ - eval
+ - '.outbounds += {"tag": "warp","protocol": "socks","settings": {"servers": [{"address": "127.0.0.1","port": 40000}]}}'
+ - -i
+ - /opt/xray-vps-setup/marzban/xray_config.json
+ - command:
+ argv:
+ - yq
+ - eval
+ - '.routing.rules += {"outboundTag": "warp", "domain": ["geosite:category-ru", "regexp:.*\\.xn--$", "regexp:.*\\.ru$", "regexp:.*\\.su$"]}'
+ - -i
+ - /opt/xray-vps-setup/marzban/xray_config.json
+ when: setup_variant == "marzban"
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/ssh.yml b/roles/xray-vps-setup/tasks/ssh.yml
new file mode 100644
index 0000000..95f9e57
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/ssh.yml
@@ -0,0 +1,14 @@
+- name: Change SSH port
+ shell:
+ cmd: grep -r Port /etc/ssh -l | xargs -n 1 sed -i -e "/Port /c\Port {{ SSH_PORT }}"
+- name: Disable password login
+ shell:
+ cmd: grep -r PasswordAuthentication /etc/ssh -l | xargs -n 1 sed -i -e "/PasswordAuthentication /c\PasswordAuthentication no"
+- name: Disable root login
+ shell:
+ cmd: grep -r PermitRootLogin /etc/ssh -l | xargs -n 1 sed -i -e "/PermitRootLogin /c\PermitRootLogin no"
+- name: Restart ssh
+ service:
+ name: ssh
+ state: restarted
+ daemon_reload: true
\ No newline at end of file
diff --git a/roles/xray-vps-setup/tasks/user.yml b/roles/xray-vps-setup/tasks/user.yml
new file mode 100644
index 0000000..191eb0b
--- /dev/null
+++ b/roles/xray-vps-setup/tasks/user.yml
@@ -0,0 +1,14 @@
+- name: Add user
+ ansible.builtin.user:
+ name: "{{ user_to_create }}"
+ shell: /bin/bash
+ groups: sudo,docker
+ password: "{{ user_password | password_hash('sha512') }}"
+ append: yes
+ update_password: on_create
+ register: "xray_user"
+- name: Add ssh_pbk to user
+ ansible.posix.authorized_key:
+ user: "{{ user_to_create }}"
+ state: "present"
+ key: "{{ ssh_public_key }}"
diff --git a/roles/xray-vps-setup/templates/caddyfile.j2 b/roles/xray-vps-setup/templates/caddyfile.j2
new file mode 100644
index 0000000..257205a
--- /dev/null
+++ b/roles/xray-vps-setup/templates/caddyfile.j2
@@ -0,0 +1,50 @@
+{
+ https_port 4123
+ default_bind 127.0.0.1
+ servers {
+ listener_wrappers {
+ proxy_protocol {
+ allow 127.0.0.1/32
+ }
+ tls
+ }
+ }
+ auto_https disable_redirects
+}
+https://{{ domain }} {
+{% if setup_variant == "marzban" %}
+ reverse_proxy * unix//run/marzban/marzban.socket
+{% else %}
+ root * /srv
+ file_server
+{% endif %}
+ header {
+ -Server
+ }
+ handle_errors {
+ header {
+ -Server
+ }
+ }
+}
+http://{{ domain }} {
+ bind 0.0.0.0
+ redir https://{host}{uri} permanent
+
+ header {
+ -Server
+ }
+ handle_errors {
+ header {
+ -Server
+ }
+ }
+}
+:4123 {
+ tls internal
+ respond 204
+}
+:80 {
+ bind 0.0.0.0
+ respond 204
+}
diff --git a/roles/xray-vps-setup/templates/confluence.j2 b/roles/xray-vps-setup/templates/confluence.j2
new file mode 100644
index 0000000..89550a3
--- /dev/null
+++ b/roles/xray-vps-setup/templates/confluence.j2
@@ -0,0 +1,272 @@
+
+
+
+
+
+ Вход в Confluence
+
+
+
+
+
+
+

+
+
Войти в Confluence
+
+
Неправильное имя пользователя или пароль.
+
+
+
+
+
+
×
+
Для создания аккаунта обратитесь к администратору.
+
+
+
+
+
+
+
diff --git a/roles/xray-vps-setup/templates/marzban.j2 b/roles/xray-vps-setup/templates/marzban.j2
new file mode 100644
index 0000000..11b39a8
--- /dev/null
+++ b/roles/xray-vps-setup/templates/marzban.j2
@@ -0,0 +1,95 @@
+SUDO_USERNAME = "xray_admin"
+SUDO_PASSWORD = "{{ MARZBAN_PASS }}"
+UVICORN_UDS = "/var/lib/marzban/marzban.socket"
+DASHBOARD_PATH = "/{{ MARZBAN_PATH }}/"
+XRAY_JSON = "xray_config.json"
+XRAY_SUBSCRIPTION_URL_PREFIX = "https://{{ domain }}"
+XRAY_SUBSCRIPTION_PATH = "{{ MARZBAN_SUB_PATH }}"
+SQLALCHEMY_DATABASE_URL = "sqlite:////var/lib/marzban/db.sqlite3"
+# ALLOWED_ORIGINS=http://localhost,http://localhost:8000,http://example.com
+# XRAY_EXECUTABLE_PATH = "/usr/local/bin/xray"
+# XRAY_ASSETS_PATH = "/usr/local/share/xray"
+# XRAY_EXCLUDE_INBOUND_TAGS = "INBOUND_X INBOUND_Y"
+# XRAY_FALLBACKS_INBOUND_TAG = "INBOUND_X"
+
+# TELEGRAM_API_TOKEN = 123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+# TELEGRAM_ADMIN_ID = 987654321, 123456789
+# TELEGRAM_LOGGER_CHANNEL_ID = -1234567890123
+# TELEGRAM_DEFAULT_VLESS_FLOW = "xtls-rprx-vision"
+# TELEGRAM_PROXY_URL = "http://localhost:8080"
+
+# DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/xxxxxxx"
+
+CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/"
+# CLASH_SUBSCRIPTION_TEMPLATE="clash/my-custom-template.yml"
+SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html"
+HOME_PAGE_TEMPLATE="home/index.html"
+
+# V2RAY_SUBSCRIPTION_TEMPLATE="v2ray/default.json"
+# V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json"
+
+# SINGBOX_SUBSCRIPTION_TEMPLATE="singbox/default.json"
+# SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json"
+
+# MUX_TEMPLATE="mux/default.json"
+
+## Enable JSON config for compatible clients to use mux, fragment, etc. Default False.
+# USE_CUSTOM_JSON_DEFAULT=True
+## Your preferred config type for different clients
+## If USE_CUSTOM_JSON_DEFAULT is set True, all following programs will use the JSON config
+# USE_CUSTOM_JSON_FOR_V2RAYN=False
+# USE_CUSTOM_JSON_FOR_V2RAYNG=True
+# USE_CUSTOM_JSON_FOR_STREISAND=False
+
+## Set headers for subscription
+# SUB_PROFILE_TITLE = "Susbcription"
+# SUB_SUPPORT_URL = "https://t.me/support"
+# SUB_UPDATE_INTERVAL = "12"
+
+## External config to import into v2ray format subscription
+# EXTERNAL_CONFIG = "config://..."
+
+# SQLALCHEMY_POOL_SIZE = 10
+# SQLIALCHEMY_MAX_OVERFLOW = 30
+
+## Custom text for STATUS_TEXT variable
+# ACTIVE_STATUS_TEXT = "Active"
+# EXPIRED_STATUS_TEXT = "Expired"
+# LIMITED_STATUS_TEXT = "Limited"
+# DISABLED_STATUS_TEXT = "Disabled"
+# ONHOLD_STATUS_TEXT = "On-Hold"
+
+### Use negative values to disable auto-delete by default
+# USERS_AUTODELETE_DAYS = -1
+# USER_AUTODELETE_INCLUDE_LIMITED_ACCOUNTS = false
+
+## Customize all notifications
+# NOTIFY_STATUS_CHANGE = True
+# NOTIFY_USER_CREATED = True
+# NOTIFY_USER_UPDATED = True
+# NOTIFY_USER_DELETED = True
+# NOTIFY_USER_DATA_USED_RESET = True
+# NOTIFY_USER_SUB_REVOKED = True
+# NOTIFY_IF_DATA_USAGE_PERCENT_REACHED = True
+# NOTIFY_IF_DAYS_LEF_REACHED = True
+# NOTIFY_LOGIN = True
+
+## Whitelist of IPs/hosts to disable login notifications
+# LOGIN_NOTIFY_WHITE_LIST = '1.1.1.1,127.0.0.1'
+
+### for developers
+# DOCS=True
+# DEBUG=True
+
+# If You Want To Send Webhook To Multiple Server Add Multi Address
+# WEBHOOK_ADDRESS = "http://127.0.0.1:9000/,http://127.0.0.1:9001/"
+# WEBHOOK_SECRET = "something-very-very-secret"
+
+# VITE_BASE_API="https://example.com/api/"
+# JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 1440
+
+# JOB_CORE_HEALTH_CHECK_INTERVAL = 10
+# JOB_RECORD_NODE_USAGES_INTERVAL = 30
+# JOB_RECORD_USER_USAGES_INTERVAL = 10
+# JOB_REVIEW_USERS_INTERVAL = 10
+# JOB_SEND_NOTIFICATIONS_INTERVAL = 30
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates/marzban_docker.j2 b/roles/xray-vps-setup/templates/marzban_docker.j2
new file mode 100644
index 0000000..a2d2ebf
--- /dev/null
+++ b/roles/xray-vps-setup/templates/marzban_docker.j2
@@ -0,0 +1,20 @@
+services:
+ caddy:
+ image: caddy:2.9
+ container_name: caddy
+ restart: always
+ network_mode: host
+ volumes:
+ - ./caddy/data:/data
+ - ./caddy/Caddyfile:/etc/caddy/Caddyfile
+ - ./marzban_lib:/run/marzban
+ marzban:
+ image: gozargah/marzban:v0.8.4
+ container_name: marzban
+ restart: always
+ env_file: ./marzban/.env
+ network_mode: host
+ volumes:
+ - ./marzban_lib:/var/lib/marzban
+ - ./marzban/xray_config.json:/code/xray_config.json
+ - ./marzban/templates:/var/lib/marzban/templates
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates/xray.json.j2 b/roles/xray-vps-setup/templates/xray.json.j2
new file mode 100644
index 0000000..0abde50
--- /dev/null
+++ b/roles/xray-vps-setup/templates/xray.json.j2
@@ -0,0 +1,76 @@
+{
+ "log": {
+ "loglevel": "info"
+ },
+ "inbounds": [
+ {
+ "tag": "VLESS TCP VISION REALITY",
+ "listen": "0.0.0.0",
+ "port": 443,
+ "protocol": "vless",
+ "settings": {
+ "clients": [
+ {
+ "id": "{{ xray_uuid.stdout}}",
+ "email": "default",
+ "flow": "xtls-rprx-vision"
+ }],
+ "decryption": "none"
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "security": "reality",
+ "realitySettings": {
+ "xver": 1,
+ "dest": "127.0.0.1:4123",
+ "serverNames": [
+ "{{ domain }}"
+ ],
+ "privateKey": "{{ x25519_pik.stdout }}",
+ "shortIds": [
+ "{{ short_id.stdout }}"
+ ]
+ }
+ },
+ "sniffing": {
+ "enabled": true,
+ "destOverride": [
+ "http",
+ "tls"
+ ],
+ "routeOnly": true
+ }
+ }
+ ],
+ "outbounds": [
+ {
+ "protocol": "freedom",
+ "tag": "direct",
+ "settings": {
+ "domainStrategy": "UseIPv4"
+ }
+ },
+ {
+ "protocol": "blackhole",
+ "tag": "block"
+ }
+ ],
+ "routing": {
+ "rules": [
+ {
+ "protocol": "bittorrent",
+ "outboundTag": "block"
+ }
+ ],
+ "domainStrategy": "IPIfNonMatch"
+ },
+ "dns": {
+ "servers": [
+ "1.1.1.1",
+ "8.8.8.8"
+ ],
+ "queryStrategy": "UseIPv4",
+ "disableFallback": false,
+ "tag": "dns-aux"
+ }
+}
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates/xray_docker.j2 b/roles/xray-vps-setup/templates/xray_docker.j2
new file mode 100644
index 0000000..86d2207
--- /dev/null
+++ b/roles/xray-vps-setup/templates/xray_docker.j2
@@ -0,0 +1,19 @@
+services:
+ caddy:
+ image: caddy:2.9
+ container_name: caddy
+ restart: always
+ network_mode: host
+ volumes:
+ - ./caddy/data:/data
+ - ./caddy/Caddyfile:/etc/caddy/Caddyfile
+ - ./caddy/templates:/srv
+ xray:
+ image: ghcr.io/xtls/xray-core:25.6.8
+ restart: always
+ container_name: xray
+ user: root
+ command: run -c /etc/xray/config.json
+ network_mode: host
+ volumes:
+ - ./xray:/etc/xray
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates_for_script/caddy b/roles/xray-vps-setup/templates_for_script/caddy
new file mode 100644
index 0000000..27b1d25
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/caddy
@@ -0,0 +1,47 @@
+{
+ https_port 4123
+ default_bind 127.0.0.1
+ servers {
+ listener_wrappers {
+ proxy_protocol {
+ allow 127.0.0.1/32
+ }
+ tls
+ }
+ }
+ auto_https disable_redirects
+}
+https://$VLESS_DOMAIN {
+ $CADDY_REVERSE
+
+ header {
+ -Server
+ }
+ handle_errors {
+ header {
+ -Server
+ }
+ }
+}
+http://$VLESS_DOMAIN {
+ bind 0.0.0.0
+ redir https://$VLESS_DOMAIN{uri} permanent
+
+ header {
+ -Server
+ }
+
+ handle_errors {
+ header {
+ -Server
+ }
+ }
+}
+:4123 {
+ tls internal
+ respond 204
+}
+:80 {
+ bind 0.0.0.0
+ respond 204
+}
diff --git a/roles/xray-vps-setup/templates_for_script/compose b/roles/xray-vps-setup/templates_for_script/compose
new file mode 100644
index 0000000..74b4634
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/compose
@@ -0,0 +1,9 @@
+services:
+ caddy:
+ image: caddy:2.9
+ container_name: caddy
+ restart: always
+ network_mode: host
+ volumes:
+ - ./caddy/data:/data
+ - ./caddy/Caddyfile:/etc/caddy/Caddyfile
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates_for_script/confluence_page b/roles/xray-vps-setup/templates_for_script/confluence_page
new file mode 100644
index 0000000..89550a3
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/confluence_page
@@ -0,0 +1,272 @@
+
+
+
+
+
+ Вход в Confluence
+
+
+
+
+
+
+

+
+
Войти в Confluence
+
+
Неправильное имя пользователя или пароль.
+
+
+
+
+
+
×
+
Для создания аккаунта обратитесь к администратору.
+
+
+
+
+
+
+
diff --git a/roles/xray-vps-setup/templates_for_script/marzban b/roles/xray-vps-setup/templates_for_script/marzban
new file mode 100644
index 0000000..4a77755
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/marzban
@@ -0,0 +1,95 @@
+SUDO_USERNAME = "xray_admin"
+SUDO_PASSWORD = "$MARZBAN_PASS"
+UVICORN_UDS = "/var/lib/marzban/marzban.socket"
+DASHBOARD_PATH = "/$MARZBAN_PATH/"
+XRAY_JSON = "xray_config.json"
+XRAY_SUBSCRIPTION_URL_PREFIX = "https://$VLESS_DOMAIN"
+XRAY_SUBSCRIPTION_PATH = "$MARZBAN_SUB_PATH"
+SQLALCHEMY_DATABASE_URL = "sqlite:////var/lib/marzban/db.sqlite3"
+# ALLOWED_ORIGINS=http://localhost,http://localhost:8000,http://example.com
+# XRAY_EXECUTABLE_PATH = "/usr/local/bin/xray"
+# XRAY_ASSETS_PATH = "/usr/local/share/xray"
+# XRAY_EXCLUDE_INBOUND_TAGS = "INBOUND_X INBOUND_Y"
+# XRAY_FALLBACKS_INBOUND_TAG = "INBOUND_X"
+
+# TELEGRAM_API_TOKEN = 123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+# TELEGRAM_ADMIN_ID = 987654321, 123456789
+# TELEGRAM_LOGGER_CHANNEL_ID = -1234567890123
+# TELEGRAM_DEFAULT_VLESS_FLOW = "xtls-rprx-vision"
+# TELEGRAM_PROXY_URL = "http://localhost:8080"
+
+# DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/xxxxxxx"
+
+CUSTOM_TEMPLATES_DIRECTORY="/var/lib/marzban/templates/"
+# CLASH_SUBSCRIPTION_TEMPLATE="clash/my-custom-template.yml"
+SUBSCRIPTION_PAGE_TEMPLATE="subscription/index.html"
+HOME_PAGE_TEMPLATE="home/index.html"
+
+# V2RAY_SUBSCRIPTION_TEMPLATE="v2ray/default.json"
+# V2RAY_SETTINGS_TEMPLATE="v2ray/settings.json"
+
+# SINGBOX_SUBSCRIPTION_TEMPLATE="singbox/default.json"
+# SINGBOX_SETTINGS_TEMPLATE="singbox/settings.json"
+
+# MUX_TEMPLATE="mux/default.json"
+
+## Enable JSON config for compatible clients to use mux, fragment, etc. Default False.
+# USE_CUSTOM_JSON_DEFAULT=True
+## Your preferred config type for different clients
+## If USE_CUSTOM_JSON_DEFAULT is set True, all following programs will use the JSON config
+# USE_CUSTOM_JSON_FOR_V2RAYN=False
+# USE_CUSTOM_JSON_FOR_V2RAYNG=True
+# USE_CUSTOM_JSON_FOR_STREISAND=False
+
+## Set headers for subscription
+# SUB_PROFILE_TITLE = "Susbcription"
+# SUB_SUPPORT_URL = "https://t.me/support"
+# SUB_UPDATE_INTERVAL = "12"
+
+## External config to import into v2ray format subscription
+# EXTERNAL_CONFIG = "config://..."
+
+# SQLALCHEMY_POOL_SIZE = 10
+# SQLIALCHEMY_MAX_OVERFLOW = 30
+
+## Custom text for STATUS_TEXT variable
+# ACTIVE_STATUS_TEXT = "Active"
+# EXPIRED_STATUS_TEXT = "Expired"
+# LIMITED_STATUS_TEXT = "Limited"
+# DISABLED_STATUS_TEXT = "Disabled"
+# ONHOLD_STATUS_TEXT = "On-Hold"
+
+### Use negative values to disable auto-delete by default
+# USERS_AUTODELETE_DAYS = -1
+# USER_AUTODELETE_INCLUDE_LIMITED_ACCOUNTS = false
+
+## Customize all notifications
+# NOTIFY_STATUS_CHANGE = True
+# NOTIFY_USER_CREATED = True
+# NOTIFY_USER_UPDATED = True
+# NOTIFY_USER_DELETED = True
+# NOTIFY_USER_DATA_USED_RESET = True
+# NOTIFY_USER_SUB_REVOKED = True
+# NOTIFY_IF_DATA_USAGE_PERCENT_REACHED = True
+# NOTIFY_IF_DAYS_LEF_REACHED = True
+# NOTIFY_LOGIN = True
+
+## Whitelist of IPs/hosts to disable login notifications
+# LOGIN_NOTIFY_WHITE_LIST = '1.1.1.1,127.0.0.1'
+
+### for developers
+# DOCS=True
+# DEBUG=True
+
+# If You Want To Send Webhook To Multiple Server Add Multi Address
+# WEBHOOK_ADDRESS = "http://127.0.0.1:9000/,http://127.0.0.1:9001/"
+# WEBHOOK_SECRET = "something-very-very-secret"
+
+# VITE_BASE_API="https://example.com/api/"
+# JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 1440
+
+# JOB_CORE_HEALTH_CHECK_INTERVAL = 10
+# JOB_RECORD_NODE_USAGES_INTERVAL = 30
+# JOB_RECORD_USER_USAGES_INTERVAL = 10
+# JOB_REVIEW_USERS_INTERVAL = 10
+# JOB_SEND_NOTIFICATIONS_INTERVAL = 30
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates_for_script/sing_box_outbound b/roles/xray-vps-setup/templates_for_script/sing_box_outbound
new file mode 100644
index 0000000..4335abb
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/sing_box_outbound
@@ -0,0 +1,21 @@
+{
+ "type": "vless",
+ "server": "$VLESS_DOMAIN",
+ "server_port": 443,
+ "uuid": "$XRAY_UUID",
+ "flow": "xtls-rprx-vision",
+ "tls": {
+ "enabled": true,
+ "insecure": false,
+ "server_name": "$VLESS_DOMAIN",
+ "utls": {
+ "enabled": true,
+ "fingerprint": "chrome"
+ },
+ "reality": {
+ "enabled": true,
+ "public_key": "$XRAY_PBK",
+ "short_id": "$XRAY_SID"
+ }
+ }
+}
diff --git a/roles/xray-vps-setup/templates_for_script/xray b/roles/xray-vps-setup/templates_for_script/xray
new file mode 100644
index 0000000..6eea0d2
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/xray
@@ -0,0 +1,77 @@
+{
+ "log": {
+ "loglevel": "none"
+ },
+ "inbounds": [
+ {
+ "tag": "VLESS TCP VISION REALITY",
+ "listen": "0.0.0.0",
+ "port": 443,
+ "protocol": "vless",
+ "settings": {
+ "clients": [
+ {
+ "id": "$XRAY_UUID",
+ "email": "default",
+ "flow": "xtls-rprx-vision"
+ }
+ ],
+ "decryption": "none"
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "security": "reality",
+ "realitySettings": {
+ "xver": 1,
+ "dest": "127.0.0.1:4123",
+ "serverNames": [
+ "$VLESS_DOMAIN"
+ ],
+ "privateKey": "$XRAY_PIK",
+ "shortIds": [
+ "$XRAY_SID"
+ ]
+ }
+ },
+ "sniffing": {
+ "enabled": true,
+ "destOverride": [
+ "http",
+ "tls"
+ ],
+ "routeOnly": true
+ }
+ }
+ ],
+ "outbounds": [
+ {
+ "protocol": "freedom",
+ "tag": "direct",
+ "settings": {
+ "domainStrategy": "UseIPv4"
+ }
+ },
+ {
+ "protocol": "blackhole",
+ "tag": "block"
+ }
+ ],
+ "routing": {
+ "rules": [
+ {
+ "protocol": "bittorrent",
+ "outboundTag": "block"
+ }
+ ],
+ "domainStrategy": "IPIfNonMatch"
+ },
+ "dns": {
+ "servers": [
+ "1.1.1.1",
+ "8.8.8.8"
+ ],
+ "queryStrategy": "UseIPv4",
+ "disableFallback": false,
+ "tag": "dns-aux"
+ }
+}
\ No newline at end of file
diff --git a/roles/xray-vps-setup/templates_for_script/xray_outbound b/roles/xray-vps-setup/templates_for_script/xray_outbound
new file mode 100644
index 0000000..69a1b71
--- /dev/null
+++ b/roles/xray-vps-setup/templates_for_script/xray_outbound
@@ -0,0 +1,30 @@
+{
+ "tag": "default",
+ "protocol": "vless",
+ "settings": {
+ "vnext": [
+ {
+ "address": "$VLESS_DOMAIN",
+ "port": 443,
+ "users": [
+ {
+ "id": "$XRAY_UUID",
+ "encryption": "none",
+ "flow": "xtls-rprx-vision"
+ }
+ ]
+ }
+ ]
+ },
+ "streamSettings": {
+ "network": "tcp",
+ "security": "reality",
+ "realitySettings": {
+ "serverName": "$VLESS_DOMAIN",
+ "fingerprint": "chrome",
+ "publicKey": "$XRAY_PBK",
+ "shortId": "$XRAY_SID",
+ "spiderX": ""
+ }
+ }
+}
diff --git a/roles/xray-vps-setup/tests/test.yml b/roles/xray-vps-setup/tests/test.yml
new file mode 100644
index 0000000..6bda0dc
--- /dev/null
+++ b/roles/xray-vps-setup/tests/test.yml
@@ -0,0 +1,5 @@
+---
+- hosts: localhost
+ remote_user: root
+ roles:
+ - vps-setup
diff --git a/roles/xray-vps-setup/vars/main.yml b/roles/xray-vps-setup/vars/main.yml
new file mode 100644
index 0000000..9addcf1
--- /dev/null
+++ b/roles/xray-vps-setup/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for vps-setup
diff --git a/roles/xray-vps-setup/vps-setup.sh b/roles/xray-vps-setup/vps-setup.sh
new file mode 100644
index 0000000..9f70711
--- /dev/null
+++ b/roles/xray-vps-setup/vps-setup.sh
@@ -0,0 +1,304 @@
+#/bin/bash
+
+set -e
+
+export GIT_BRANCH="main"
+export GIT_REPO="Akiyamov/xray-vps-setup"
+
+# Check if script started as root
+if [ "$EUID" -ne 0 ]
+ then echo "Please run as root"
+ exit
+fi
+
+# Install idn
+apt-get update
+apt-get install idn sudo dnsutils -y
+
+# Read domain input
+read -ep "Enter your domain:"$'\n' input_domain
+
+export VLESS_DOMAIN=$(echo $input_domain | idn)
+
+SERVER_IPS=($(hostname -I))
+
+RESOLVED_IP=$(dig +short $VLESS_DOMAIN | tail -n1)
+
+if [ -z "$RESOLVED_IP" ]; then
+ echo "Warning: Domain has no DNS record"
+ 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
+ if [[ "$prompt_response" =~ ^([yY])$ ]]; then
+ echo "Ok, proceeding without DNS verification"
+ else
+ echo "Come back later"
+ exit 1
+ fi
+else
+ MATCH_FOUND=false
+ for server_ip in "${SERVER_IPS[@]}"; do
+ if [ "$RESOLVED_IP" == "$server_ip" ]; then
+ MATCH_FOUND=true
+ break
+ fi
+ done
+
+ if [ "$MATCH_FOUND" = true ]; then
+ echo "✓ DNS record points to this server ($RESOLVED_IP)"
+ else
+ echo "Warning: DNS record exists but points to different IP"
+ echo " Domain resolves to: $RESOLVED_IP"
+ echo " This server's IPs: ${SERVER_IPS[*]}"
+ read -ep "Continue anyway? [y/N]"$'\n' prompt_response
+ if [[ "$prompt_response" =~ ^([yY])$ ]]; then
+ echo "Ok, proceeding"
+ else
+ echo "Come back later"
+ exit 1
+ fi
+ fi
+fi
+
+read -ep "Do you want to install marzban? [y/N] "$'\n' marzban_input
+
+read -ep "Do you want to configure server security? Do this on first run only. [y/N] "$'\n' configure_ssh_input
+if [[ ${configure_ssh_input,,} == "y" ]]; then
+ # Read SSH port
+ read -ep "Enter SSH port. Default 22, can't use ports: 80, 443 and 4123:"$'\n' input_ssh_port
+
+ while [[ "$input_ssh_port" -eq "80" || "$input_ssh_port" -eq "443" || "$input_ssh_port" -eq "4123" ]]; do
+ read -ep "No, ssh can't use $input_ssh_port as port, write again:"$'\n' input_ssh_port
+ done
+ # Read SSH Pubkey
+ read -ep "Enter SSH public key:"$'\n' input_ssh_pbk
+ echo "$input_ssh_pbk" > ./test_pbk
+ ssh-keygen -l -f ./test_pbk
+ PBK_STATUS=$(echo $?)
+ if [ "$PBK_STATUS" -eq 255 ]; then
+ 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."
+ exit
+ fi
+ rm ./test_pbk
+fi
+
+read -ep "Do you want to install WARP and use it on russian websites? [y/N] "$'\n' configure_warp_input
+if [[ ${configure_warp_input,,} == "y" ]]; then
+ if ! curl -I https://api.cloudflareclient.com --connect-timeout 10 > /dev/null 2>&1; then
+ echo "Warp can't be used"
+ configure_warp_input="n"
+ fi
+fi
+
+# Check congestion protocol
+if sysctl net.ipv4.tcp_congestion_control | grep bbr; then
+ echo "BBR is already used"
+else
+ echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
+ sysctl -p > /dev/null
+ echo "Enabled BBR"
+fi
+
+export ARCH=$(dpkg --print-architecture)
+
+yq_install() {
+ wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_$ARCH -O /usr/bin/yq && chmod +x /usr/bin/yq
+}
+
+yq_install
+
+docker_install() {
+ curl -fsSL https://get.docker.com | sh
+}
+
+if ! command -v docker 2>&1 >/dev/null; then
+ docker_install
+fi
+
+# Generate values for XRay
+export SSH_USER=$(tr -dc A-Za-z0-9 ./docker-compose.yml
+ yq eval \
+ '.services.marzban.image = "gozargah/marzban:v0.8.4" |
+ .services.marzban.container_name = "marzban" |
+ .services.marzban.restart = "always" |
+ .services.marzban.env_file = "./marzban/.env" |
+ .services.marzban.network_mode = "host" |
+ .services.marzban.volumes[0] = "./marzban_lib:/var/lib/marzban" |
+ .services.marzban.volumes[1] = "./marzban/xray_config.json:/code/xray_config.json" |
+ .services.marzban.volumes[2] = "./marzban/templates:/var/lib/marzban/templates" |
+ .services.caddy.volumes[2] = "./marzban_lib:/run/marzban"' -i /opt/xray-vps-setup/docker-compose.yml
+ mkdir -p marzban caddy
+ wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/marzban | envsubst > ./marzban/.env
+ mkdir -p /opt/xray-vps-setup/marzban/templates/home
+ wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/confluence_page | envsubst > ./marzban/templates/home/index.html
+ export CADDY_REVERSE="reverse_proxy * unix//run/marzban/marzban.socket"
+ wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/caddy" | envsubst > ./caddy/Caddyfile
+ wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/xray" | envsubst > ./marzban/xray_config.json
+ else
+ wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/compose | envsubst > ./docker-compose.yml
+ mkdir -p /opt/xray-vps-setup/caddy/templates
+ yq eval \
+ '.services.xray.image = "ghcr.io/xtls/xray-core:25.6.8" |
+ .services.xray.container_name = "xray" |
+ .services.xray.user = "root" |
+ .services.xray.command = "run -c /etc/xray/config.json" |
+ .services.xray.restart = "always" |
+ .services.xray.network_mode = "host" |
+ .services.caddy.volumes[2] = "./caddy/templates:/srv" |
+ .services.xray.volumes[0] = "./xray:/etc/xray"' -i /opt/xray-vps-setup/docker-compose.yml
+ wget -qO- https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/confluence_page | envsubst > ./caddy/templates/index.html
+ export CADDY_REVERSE="root * /srv
+ file_server"
+ mkdir -p xray caddy
+ wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/xray" | envsubst > ./xray/config.json
+ wget -qO- "https://raw.githubusercontent.com/$GIT_REPO/refs/heads/$GIT_BRANCH/templates_for_script/caddy" | envsubst > ./caddy/Caddyfile
+ fi
+}
+
+xray_setup
+
+sshd_edit() {
+ grep -r Port /etc/ssh -l | xargs -n 1 sed -i -e "/Port /c\Port $SSH_PORT"
+ grep -r PasswordAuthentication /etc/ssh -l | xargs -n 1 sed -i -e "/PasswordAuthentication /c\PasswordAuthentication no"
+ grep -r PermitRootLogin /etc/ssh -l | xargs -n 1 sed -i -e "/PermitRootLogin /c\PermitRootLogin no"
+ systemctl daemon-reload
+ systemctl restart ssh
+}
+
+add_user() {
+ useradd $SSH_USER -s /bin/bash
+ usermod -aG sudo $SSH_USER
+ echo $SSH_USER:$SSH_USER_PASS | chpasswd
+ mkdir -p /home/$SSH_USER/.ssh
+ touch /home/$SSH_USER/.ssh/authorized_keys
+ echo $input_ssh_pbk >> /home/$SSH_USER/.ssh/authorized_keys
+ chmod 700 /home/$SSH_USER/.ssh/
+ chmod 600 /home/$SSH_USER/.ssh/authorized_keys
+ chown $SSH_USER:$SSH_USER -R /home/$SSH_USER
+ usermod -aG docker $SSH_USER
+}
+
+debconf-set-selections <