├── LICENSE
├── README.md
├── README_RU.md
├── adh
└── AdGuardHome.yaml
├── bot
└── reverse_proxy_bot.py
├── database
├── x-ui.db
└── x-ui.db_old
├── media
├── reverse_proxy_manager.png
├── reverse_proxy_manager_RU.png
├── xui.png
├── xui_rp_install.png
└── xui_rp_install_RU.png
├── other
├── check_domain_ip.sh
├── flag_final.sh
├── reverse_proxy_server.sh
└── x-ui.gpg
├── reverse_proxy.sh
├── reverse_proxy_bot.sh
├── reverse_proxy_random_site.sh
└── warp
└── xui-rp-warp.sh
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 cortez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # REVERSE_PROXY ([Russian](/README_RU.md))
2 |

3 |
4 | -----
5 |
6 | ### Server using NGINX reverse proxy
7 | This script is designed for quick and easy setup of a reverse proxy server using NGINX. In this setup, all incoming requests are processed by NGINX, and the server functions as a reverse proxy server only if the request contains the correct path (URI). This enhances security and improves access control management.
8 |
9 | > [!IMPORTANT]
10 | > This script has been tested in a KVM virtualization environment. You will need your own domain, which needs to be bound to Cloudflare for it to work correctly. It is recommended to run the script as root on a freshly installed system.
11 |
12 | > [!NOTE]
13 | > The script is configured according to routing rules for users in Russia.
14 |
15 | ### Supported Operating Systems:
16 |
17 | | **Ubuntu** | **Debian** | **CentOS** |
18 | |------------------|-----------------|-----------------|
19 | | 24.04 LTS | 12 bookworm | Stream 9 |
20 | | 22.04 LTS | 11 bullseye | Stream 8 |
21 | | 20.04 LTS | 10 buster | 7 |
22 |
23 | -----
24 |
25 | ### Setting up cloudflare
26 | 1. Upgrade the system and reboot the server.
27 | 2. Configure Cloudflare:
28 | - Bind your domain to Cloudflare.
29 | - Add the following DNS records:
30 |
31 | SERVER 1
32 | | Type | Name | Content | Proxy status |
33 | | ----- | ---------------- | ---------------- | ------------- |
34 | | A | example.com | your_server_ip | DNS only |
35 | | CNAME | www | example.com | DNS only |
36 |
37 | SERVER 2
38 | | Type | Name | Content | Proxy status |
39 | | ----- | ---------------- | ---------------- | ------------- |
40 | | A | nl.example.com | your_server_ip | DNS only |
41 | | CNAME | www.nl | nl.example.com | DNS only |
42 |
43 | 3. SSL/TLS settings in Cloudflare:
44 | - Go to SSL/TLS > Overview and select Full for the Configure option.
45 | - Set the Minimum TLS Version to TLS 1.3.
46 | - Enable TLS 1.3 (true) under Edge Certificates.
47 |
48 | -----
49 |
50 | ### Includes:
51 |
52 | 1. Proxy server configuration:
53 | - Support for automatic configuration updates through subscription and JSON subscription with the ability to convert to formats for popular applications.
54 | - You must enable MUX (multiplexing TCP connections) in each client application
55 | - gRPC-TLS
56 | - XHTTP-TLS
57 | - HTTPUpgrade-TLS
58 | - Websocket-TLS
59 | - The user “flow”: “xtls-rprx-vision” must be enabled
60 | - TCP-REALITY (Steal oneself) (disconnection will result in loss of access)
61 | - TCP-TLS
62 | - Important: it is recommended to choose one suitable connection type and use it for optimal performance. You can disable all incoming connections except the one marked as STEAL. Disabling STEAL will result in losing access to the web interface, as this connection type is used for proxy management access.
63 | 2. Configuring NGINX reverse proxy on port 443.
64 | 3. Providing security:
65 | - Automatic system updates via unattended-upgrades.
66 | - Configuring Cloudflare SSL certificates with automatic updates to secure connections.
67 | - Configuring WARP to protect traffic.
68 | - Configuring UFW (Uncomplicated Firewall) for access control.
69 | - Configuring SSH, to provide the minimum required security.
70 | - Disabling IPv6 to prevent possible vulnerabilities.
71 | - Encrypting DNS queries using systemd-resolved (DoT) or AdGuard Home (Dot, DoH).
72 | - Selecting a random website template from an array.
73 | 4. Enabling BBR - improving the performance of TCP connections.
74 | 5. Optional extras:
75 | - Installing and configuring Node Exporter for system performance monitoring and integrating with Prometheus and Grafana for real-time metrics visualization.
76 | - Setting up Shell In A Box for secure, web-based SSH access to the server.
77 | - Updated SSH welcome message (motd) with useful system information, service status, and available updates.
78 | - VNStat integration for traffic monitoring, with the ability to get statistics by time.
79 |
80 | -----
81 |
82 | ### REVERSE_PROXY_MANAGER:
83 |
84 | 
85 |
86 | -----
87 |
88 | ### Help message of the script:
89 | ```
90 | Usage: reverse-proxy [-u|--utils ] [-d|--dns ] [-a|--addu ]
91 | [-r|--autoupd ] [-b|--bbr ] [-i|--ipv6 ] [-w|--warp ]
92 | [-c|--cert ] [-m|--mon ] [-l|--shell ] [-n|--nginx ]
93 | [-p|--panel ] [--custom ] [-f|--firewall ] [-s|--ssh ]
94 | [-t|--tgbot ] [-g|--generate ] [-x|--skip-check ] [-o|--subdomain ]
95 | [--update] [-h|--help]"
96 |
97 | -u, --utils Additional utilities (default: true)
98 | -d, --dns DNS encryption (default: true)
99 | -a, --addu User addition (default: true)
100 | -r, --autoupd Automatic updates (default: true)
101 | -b, --bbr BBR (TCP Congestion Control) (default: true)
102 | -i, --ipv6 Disable IPv6 support (default: true)
103 | -w, --warp Warp (default: false)
104 | -c, --cert Certificate issuance for domain (default: true)
105 | -m, --mon Monitoring services (node_exporter) (default: false)
106 | -l, --shell Shell In A Box installation (default: false)
107 | -n, --nginx NGINX installation (default: true)
108 | -p, --panel Panel installation for user management (default: true)
109 | --custom Custom JSON subscription (default: true)
110 | -f, --firewall Firewall configuration (default: true)
111 | -s, --ssh SSH access (default: true)
112 | -t, --tgbot Telegram bot integration (default: false)
113 | -g, --generate Generate a random string for configuration (default: true)
114 | -x, --skip-check Disable the check functionality (default: false)
115 | -o, --subdomain Support for subdomains (default: false)
116 | --update Update version of Reverse-proxy manager
117 | -h, --help Display this help message
118 | ```
119 |
120 | ### Installation of REVERSE_PROXY:
121 |
122 | To begin configuring the server, simply run the following command in a terminal:
123 | ```sh
124 | bash <(curl -Ls https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/refs/heads/main/reverse_proxy.sh)
125 | ```
126 |
127 | ### Installing a random template for the website:
128 | ```sh
129 | bash <(curl -Ls https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/refs/heads/main/reverse_proxy_random_site.sh)
130 | ```
131 |
132 | The script will then prompt you for the necessary configuration information:
133 |
134 | 
135 |
136 | ### Note:
137 | - Once the configuration is complete, the script will display all the necessary links and login information for the administration panel.
138 | - All configurations can be modified as needed due to the flexibility of the settings.
139 |
140 | -----
141 |
142 | > [!IMPORTANT]
143 | > This repository is intended solely for educational purposes and for studying the principles of reverse proxy servers and network security. The script demonstrates the setup of a proxy server using NGINX for reverse proxy, traffic management, and attack protection.
144 | >
145 | >We strongly remind you that using this tool to bypass network restrictions or censorship is illegal in certain countries that have laws regulating the use of technologies to circumvent internet restrictions.
146 | >
147 | >This project is not intended for use in ways that violate information protection laws or interfere with censorship mechanisms. We take no responsibility for any legal consequences arising from the use of this script.
148 | >
149 | >Use this tool/script only for demonstration purposes, as an example of reverse proxy operation and data protection. We strongly recommend removing the script after reviewing it. Further use is at your own risk.
150 | >
151 | >If you are unsure whether the use of this tool or its components violates the laws of your country, refrain from interacting with this tool.
152 |
153 | -----
154 |
155 | ## Stargazers over time
156 | [](https://starchart.cc/cortez24rus/xui-reverse-proxy)
157 |
--------------------------------------------------------------------------------
/README_RU.md:
--------------------------------------------------------------------------------
1 | # REVERSE_PROXY ([English](/README.md))
2 | 
3 |
4 | -----
5 |
6 | ### Сервер с использованием реверс-прокси NGINX
7 | Этот скрипт предназначен для быстрой и простой настройки обратного прокси-сервера с использованием NGINX. В данном варианте все входящие запросы обрабатываются NGINX, а сервер работает как обратный прокси-сервер только при условии, что запрос содержит правильный путь (URI). Это повышает безопасность и улучшает управление доступом.
8 |
9 | > [!IMPORTANT]
10 | > Этот скрипт был протестирован в среде виртуализации KVM. Для корректной работы вам потребуется собственный домен, который` необходимо привязать к Cloudflare. Скрипт рекомендуется запускать с правами root на свежеустановленной системе.
11 |
12 | > [!NOTE]
13 | > Скрипт настроен с учётом специфики маршрутизации для пользователей из России.
14 |
15 | ### Поддерживаемые операционные системы:
16 |
17 | | **Ubuntu** | **Debian** | **CentOS** |
18 | |------------------|-----------------|------------------|
19 | | 24.04 LTS | 12 bookworm | Stream 9 |
20 | | 22.04 LTS | 11 bullseye | Stream 8 |
21 | | 20.04 LTS | 10 buster | 7 |
22 |
23 | -----
24 |
25 | ### Настройка cloudflare
26 | 1. Обновите систему и перезагрузите сервер.
27 | 2. Настройте Cloudflare:
28 | - Привяжите ваш домен к Cloudflare.
29 | - Добавьте следующие DNS записи:
30 |
31 | Сервер #1
32 | | Type | Name | Content | Proxy status |
33 | | ----- | ---------------- | ---------------- | ------------- |
34 | | A | example.com | your_server_ip | DNS only |
35 | | CNAME | www | example.com | DNS only |
36 |
37 | Сервер #2
38 | | Type | Name | Content | Proxy status |
39 | | ----- | ---------------- | ---------------- | ------------- |
40 | | A | nl.example.com | your_server_ip | DNS only |
41 | | CNAME | www.nl | nl.example.com | DNS only |
42 |
43 | 3. Настройки SSL/TLS в Cloudflare:
44 | - Перейдите в раздел SSL/TLS > Overview и выберите Full для параметра Configure.
45 | - Установите Minimum TLS Version на TLS 1.3.
46 | - Включите TLS 1.3 (true) в разделе Edge Certificates.
47 |
48 | -----
49 |
50 | ### REVERSE_PROXY_MANAGER:
51 |
52 | 
53 |
54 | -----
55 |
56 | ### Включает в себя:
57 |
58 | 1. Конфигурация прокси сервера:
59 | - Поддержка автоматического обновления конфигураций через подписку и JSON подписку с возможностью конвертации в форматы для популярных приложений.
60 | - Необходимо включать MUX (мультиплексирование TCP соединений) в каждом клиентском приложении
61 | - gRPC-TLS
62 | - XHTTP-TLS
63 | - HTTPUpgrade-TLS
64 | - Websocket-TLS
65 | - Необходимо включать у пользователя "flow": "xtls-rprx-vision"
66 | - TCP-REALITY (Steal oneself) (отключение приведет к потере доступа)
67 | - TCP-TLS
68 | - Важно: рекомендуется выбрать один подходящий тип подключения и использовать его для оптимальной работы. Вы можете отключить все входящие соединения, кроме того, которое отмечено как STEAL. Отключение STEAL приведет к потере доступа к веб-интерфейсу, так как этот тип соединения используется для доступа к управлению прокси.
69 | 2. Настройку обратного прокси NGINX на порт 443.
70 | 3. Обеспечение безопасности:
71 | - Автоматические обновления системы через unattended-upgrades.
72 | - Настройка SSL сертификатов Cloudflare с автоматическим обновлением для защиты соединений.
73 | - Настройка WARP для защиты трафика.
74 | - Настройка UFW (Uncomplicated Firewall) для управления доступом.
75 | - Настройка SSH, для обеспечения минимально необходимой безопасности.
76 | - Отключение IPv6 для предотвращения возможных уязвимостей.
77 | - Шифрование DNS-запросов с использованием systemd-resolved (Dot) или AdGuard Home (Dot, DoH).
78 | - Выбор случайного шаблона веб-сайта из массива.
79 | 4. Включение BBR — улучшение производительности TCP-соединений.
80 | 5. Дополнительные опции:
81 | - Настройка Shell In A Box для безопасного доступа к серверу через SSH через веб-интерфейс.
82 | - Установка и настройка Node Exporter для мониторинга производительности системы с интеграцией с Prometheus и Grafana для визуализации метрик в реальном времени.
83 | - Обновленное приветственное сообщение (motd) при подключении через SSH, содержащее полезную информацию о системе, состоянии сервисов и доступных обновлениях.
84 | - Интеграция VNStat для мониторинга трафика, с возможностью получения статистики по времени.
85 | -----
86 |
87 | ### Сообщение помощи для скрипта:
88 | ```
89 |
90 | Использование: reverse_proxy [-u|--utils ] [-d|--dns ] [-a|--addu ]
91 | [-r|--autoupd ] [-b|--bbr ] [-i|--ipv6 ] [-w|--warp ]
92 | [-c|--cert ] [-m|--mon ] [-l|--shell ] [-n|--nginx ]
93 | [-p|--panel ] [--custom ] [-f|--firewall ] [-s|--ssh ]
94 | [-t|--tgbot ] [-g|--generate ] [-x|--skip-check ] [-o|--subdomain ]
95 | [--update] [-h|--help]"
96 |
97 | -u, --utils Дополнительные утилиты (по умолчанию: true)
98 | -d, --dns Шифрование DNS (по умолчанию: true)
99 | -a, --addu Добавление пользователя (по умолчанию: true)
100 | -r, --autoupd Автоматические обновления (по умолчанию: true)
101 | -b, --bbr BBR (управление перегрузкой TCP) (по умолчанию: true)
102 | -i, --ipv6 Отключить поддержку IPv6 (по умолчанию: true)
103 | -w, --warp Warp (по умолчанию: false)
104 | -c, --cert Выпуск сертификатов для домена (по умолчанию: true)
105 | -m, --mon Сервисы мониторинга (node_exporter) (по умолчанию: false)
106 | -l, --shell Установка Shell In A Box (по умолчанию: false)
107 | -n, --nginx Установка NGINX (по умолчанию: true)
108 | -p, --panel Установка панели для управления пользователями (по умолчанию: true)
109 | --custom Кастомная JSON-подписка (по умолчанию: true)
110 | -f, --firewall Настройка файрвола (по умолчанию: true)
111 | -s, --ssh SSH доступ (по умолчанию: true)
112 | -t, --tgbot Интеграция Telegram бота (по умолчанию: false)
113 | -g, --generate Генерация случайных путей для конфигурации (по умолчанию: true)
114 | -x, --skip-check Отключение проверки (по умолчанию: false)
115 | -o, --subdomain Поддержка поддоменов (по умолчанию: false)
116 | --update Обновить версию Reverse-proxy manager
117 | -h, --help Показать это сообщение помощи
118 | ```
119 |
120 | -----
121 |
122 | ### Установка REVERSE_PROXY:
123 |
124 | Для начала настройки сервера выполните следующую команду в терминале:
125 | ```sh
126 | bash <(curl -Ls https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/refs/heads/main/reverse_proxy.sh)
127 | ```
128 |
129 | ### Установка случайного шаблона для веб-сайта:
130 | ```sh
131 | bash <(curl -Ls https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/refs/heads/main/reverse_proxy_random_site.sh)
132 | ```
133 |
134 | Скрипт запросит у вас необходимую конфигурационную информацию:
135 |
136 | 
137 |
138 | ### Примечание:
139 | - После завершения настройки скрипт отобразит все необходимые ссылки и данные для входа в административную панель.
140 | - Все конфигурации можно будет изменять по мере необходимости, благодаря гибкости настроек.
141 |
142 | -----
143 |
144 | > [!IMPORTANT]
145 | > Этот репозиторий предназначен исключительно для образовательных целей и для изучения принципов работы обратных прокси-серверов и сетевой безопасности. Скрипт демонстрирует настройку прокси-сервера с использованием NGINX для реверс-прокси, управления трафиком и защиты от атак.
146 | >
147 | > Мы настоятельно напоминаем, что использование этого инструмента с целью обхода сетевых блокировок или цензуры является незаконным в ряде стран, где существуют законы, регулирующие использование технологий для обхода ограничений в интернете.
148 | >
149 | > Данный проект не предназначен для использования в целях, нарушающих законы о защите информации или вмешивающихся в механизмы цензуры. Мы не несем ответственности за возможные юридические последствия, связанные с использованием этого скрипта.
150 | >
151 | >Используйте этот инструмент/скрипт исключительно в демонстрационных целях, в качестве примера работы обратного прокси и защиты данных. Настоятельно рекомендуем удалить скрипт после ознакомления. Дальнейшее использование на ваш страх и риск.
152 | >
153 | >Если вы не уверены, нарушает ли использование данного инструмента или его компонентов законодательство вашей страны- откажитесь от любого взаимодействия с данным инструментом.
154 |
155 | -----
156 |
157 | ## Количество звезд по времени
158 | [](https://starchart.cc/cortez24rus/xui-reverse-proxy)
159 |
--------------------------------------------------------------------------------
/adh/AdGuardHome.yaml:
--------------------------------------------------------------------------------
1 | http:
2 | pprof:
3 | port: 6060
4 | enabled: false
5 | address: 127.0.0.1:8081
6 | session_ttl: 720h
7 | users:
8 | - name: username
9 | password: hash
10 | auth_attempts: 5
11 | block_auth_min: 15
12 | http_proxy: ""
13 | language: ""
14 | theme: auto
15 | dns:
16 | bind_hosts:
17 | - 0.0.0.0
18 | port: 53
19 | anonymize_client_ip: false
20 | ratelimit: 20
21 | ratelimit_subnet_len_ipv4: 24
22 | ratelimit_subnet_len_ipv6: 56
23 | ratelimit_whitelist: []
24 | refuse_any: true
25 | upstream_dns:
26 | - https://dns.cloudflare.com/dns-query
27 | - https://dns.google/dns-query
28 | - https://dns10.quad9.net/dns-query
29 | - tls://one.one.one.one
30 | - tls://dns.google
31 | - tls://dns10.quad9.net
32 | upstream_dns_file: ""
33 | bootstrap_dns:
34 | - 9.9.9.10
35 | - 149.112.112.10
36 | - 2620:fe::10
37 | - 2620:fe::fe:10
38 | fallback_dns: []
39 | upstream_mode: parallel
40 | fastest_timeout: 1s
41 | allowed_clients: []
42 | disallowed_clients: []
43 | blocked_hosts:
44 | - version.bind
45 | - id.server
46 | - hostname.bind
47 | trusted_proxies:
48 | - 127.0.0.0/8
49 | - ::1/128
50 | cache_size: 4194304
51 | cache_ttl_min: 0
52 | cache_ttl_max: 0
53 | cache_optimistic: false
54 | bogus_nxdomain: []
55 | aaaa_disabled: false
56 | enable_dnssec: false
57 | edns_client_subnet:
58 | custom_ip: ""
59 | enabled: false
60 | use_custom: false
61 | max_goroutines: 300
62 | handle_ddr: true
63 | ipset: []
64 | ipset_file: ""
65 | bootstrap_prefer_ipv6: false
66 | upstream_timeout: 10s
67 | private_networks: []
68 | use_private_ptr_resolvers: true
69 | local_ptr_upstreams: []
70 | use_dns64: false
71 | dns64_prefixes: []
72 | serve_http3: false
73 | use_http3_upstreams: false
74 | serve_plain_dns: true
75 | hostsfile_enabled: true
76 | tls:
77 | enabled: true
78 | server_name: domain_temp # Укажите имя сервера
79 | force_https: true
80 | port_https: 443
81 | port_dns_over_tls: 853
82 | port_dns_over_quic: 853
83 | port_dnscrypt: 0
84 | dnscrypt_config_file: ""
85 | allow_unencrypted_doh: false # Запрещаем неоплачиваемый DoH
86 | certificate_chain: fullchain.pem
87 | private_key: privkey.pem
88 | certificate_path: fullchain.pem
89 | private_key_path: privkey.pem
90 | strict_sni_check: false
91 | querylog:
92 | dir_path: ""
93 | ignored: []
94 | interval: 2160h
95 | size_memory: 1000
96 | enabled: true
97 | file_enabled: true
98 | statistics:
99 | dir_path: ""
100 | ignored: []
101 | interval: 24h
102 | enabled: true
103 | filters:
104 | - enabled: true
105 | url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
106 | name: AdGuard DNS filter
107 | id: 1
108 | - enabled: false
109 | url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt
110 | name: AdAway Default Blocklist
111 | id: 2
112 | - enabled: true
113 | url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_33.txt
114 | name: Steven Black's List
115 | id: 1725212203
116 | whitelist_filters: []
117 | user_rules: []
118 | dhcp:
119 | enabled: false
120 | interface_name: ""
121 | local_domain_name: lan
122 | dhcpv4:
123 | gateway_ip: ""
124 | subnet_mask: ""
125 | range_start: ""
126 | range_end: ""
127 | lease_duration: 86400
128 | icmp_timeout_msec: 1000
129 | options: []
130 | dhcpv6:
131 | range_start: ""
132 | lease_duration: 86400
133 | ra_slaac_only: false
134 | ra_allow_slaac: false
135 | filtering:
136 | blocking_ipv4: ""
137 | blocking_ipv6: ""
138 | blocked_services:
139 | schedule:
140 | time_zone: Local
141 | ids: []
142 | protection_disabled_until: null
143 | safe_search:
144 | enabled: false
145 | bing: true
146 | duckduckgo: true
147 | google: true
148 | pixabay: true
149 | yandex: true
150 | youtube: true
151 | blocking_mode: default
152 | parental_block_host: family-block.dns.adguard.com
153 | safebrowsing_block_host: standard-block.dns.adguard.com
154 | rewrites: []
155 | safebrowsing_cache_size: 1048576
156 | safesearch_cache_size: 1048576
157 | parental_cache_size: 1048576
158 | cache_time: 30
159 | filters_update_interval: 24
160 | blocked_response_ttl: 10
161 | filtering_enabled: true
162 | parental_enabled: false
163 | safebrowsing_enabled: false
164 | protection_enabled: true
165 | clients:
166 | runtime_sources:
167 | whois: true
168 | arp: true
169 | rdns: true
170 | dhcp: true
171 | hosts: true
172 | persistent: []
173 | log:
174 | enabled: true
175 | file: ""
176 | max_backups: 0
177 | max_size: 100
178 | max_age: 3
179 | compress: false
180 | local_time: false
181 | verbose: false
182 | os:
183 | group: ""
184 | user: ""
185 | rlimit_nofile: 0
186 | schema_version: 28
187 |
--------------------------------------------------------------------------------
/bot/reverse_proxy_bot.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import json
3 | import uuid
4 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
5 | from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler, ContextTypes, MessageHandler
6 | from telegram.ext import filters
7 | from datetime import datetime, timedelta
8 |
9 | # Чтение данных из конфигурационного файла
10 | CONFIG_FILE = '/usr/local/reverse_proxy/reverse_proxy_bot_config.json'
11 |
12 | def load_config():
13 | try:
14 | with open(CONFIG_FILE, 'r') as f:
15 | config = json.load(f)
16 | except FileNotFoundError:
17 | print(f"Файл конфигурации {CONFIG_FILE} не найден!")
18 | exit(1)
19 | except json.JSONDecodeError:
20 | print("Ошибка в формате конфигурационного файла!")
21 | exit(1)
22 | return config
23 |
24 | config = load_config()
25 |
26 | # Вводные данные
27 | DB_PATH = '/etc/x-ui/x-ui.db'
28 | BOT_TOKEN = config['BOT_TOKEN']
29 | BOT_AID = config['BOT_AID']
30 | NAME_MENU = config['NAME_MENU']
31 |
32 | # Функция для подключения к базе данных
33 | def get_db_connection():
34 | conn = sqlite3.connect(DB_PATH)
35 | conn.row_factory = sqlite3.Row
36 | return conn
37 |
38 | # Функция для получения всех remark с up и down
39 | def get_inbounds_remarks():
40 | connection = sqlite3.connect(DB_PATH)
41 | cursor = connection.cursor()
42 |
43 | cursor.execute("SELECT remark, up, down, enable FROM inbounds")
44 | remarks = cursor.fetchall()
45 |
46 | connection.close()
47 | return [(remark, up, down, enable) for remark, up, down, enable in remarks]
48 |
49 | # Функция для получения всех существующих ID
50 | def get_all_ids():
51 | conn = sqlite3.connect(DB_PATH)
52 | cursor = conn.cursor()
53 | cursor.execute("SELECT id, settings FROM inbounds")
54 | result = cursor.fetchall()
55 | conn.close()
56 | return [(id, json.loads(settings)) for id, settings in result]
57 |
58 | # Функция для получения всех пользователей
59 | def get_all_users():
60 | all_ids = get_all_ids()
61 | users = []
62 | for _, settings in all_ids:
63 | for client in settings.get('clients', []):
64 | users.append(client['subId']) # Добавляем subId пользователя в список
65 | return list(set(users)) # Удаляем дубликаты
66 |
67 | # Функция для добавления пользователя
68 | def add_user_to_all_ids(name):
69 | all_ids = get_all_ids() # Предполагается, что эта функция возвращает все id и соответствующие настройки
70 |
71 | for id, settings in all_ids:
72 | # Получаем remark для текущего id
73 | conn = sqlite3.connect(DB_PATH)
74 | cursor = conn.cursor()
75 | cursor.execute("SELECT remark FROM inbounds WHERE id = ?", (id,))
76 | remark = cursor.fetchone()[0] # Получаем значение remark
77 | conn.close()
78 |
79 | # Используем имя для формирования email с remark
80 | email = f"{name}{remark}" # Формируем email на основе имени пользователя и remark
81 |
82 | # Генерируем уникальный UUID для id
83 | new_id = str(uuid.uuid4())
84 |
85 | # Округляем до следующего часа
86 | next_hour = (datetime.now() + timedelta(hours=1)).replace(minute=0, second=0, microsecond=0)
87 |
88 | # Добавляем два дня к следующему часу
89 | expiry_time = int((next_hour + timedelta(days=2)).timestamp() * 1000)
90 |
91 | new_client = {
92 | "id": new_id, # Генерируем уникальный id
93 | "flow": "",
94 | "email": email, # Email теперь на основе имени и remark
95 | "limitIp": 2,
96 | "totalGB": 0,
97 | "expiryTime": expiry_time, # Текущее время + 2 дня в Unix формате
98 | "enable": True,
99 | "tgId": "",
100 | "subId": name, # Используем введённое имя как subId
101 | "reset": 30
102 | }
103 |
104 | settings['clients'].append(new_client)
105 |
106 | # Обновляем настройки в таблице inbounds
107 | conn = sqlite3.connect(DB_PATH)
108 | cursor = conn.cursor()
109 | cursor.execute("UPDATE inbounds SET settings = ? WHERE id = ?", (json.dumps(settings), id))
110 | conn.commit()
111 |
112 | # Добавляем нового клиента в таблицу client_traffics
113 | cursor.execute('''
114 | INSERT INTO client_traffics (inbound_id, enable, email, up, down, expiry_time, total, reset)
115 | VALUES (?, ?, ?, ?, ?, ?, ?, ?)
116 | ''', (id, 1, email, 0, 0, expiry_time, 0, 30)) # Замените значения по необходимости
117 |
118 | conn.commit()
119 | conn.close()
120 |
121 | # Функция для обновления значения enable в базе данных
122 | def toggle_enable(remark):
123 | connection = sqlite3.connect(DB_PATH)
124 | cursor = connection.cursor()
125 |
126 | cursor.execute("SELECT enable FROM inbounds WHERE remark = ?", (remark,))
127 | current_value = cursor.fetchone()
128 |
129 | if current_value:
130 | new_value = 1 if current_value[0] == 0 else 0
131 | cursor.execute("UPDATE inbounds SET enable = ? WHERE remark = ?", (new_value, remark))
132 | connection.commit()
133 |
134 | connection.close()
135 | return new_value
136 |
137 | # Функция для удаления пользователя по subId
138 | def remove_user_from_all_ids(subId):
139 | all_ids = get_all_ids()
140 |
141 | for id, settings in all_ids:
142 | # Фильтруем клиентов, исключая удаляемого
143 | clients_to_remove = [client for client in settings['clients'] if client['subId'] == subId]
144 | settings['clients'] = [client for client in settings['clients'] if client['subId'] != subId]
145 |
146 | # Удаляем пользователей из client_traffics
147 | conn = sqlite3.connect(DB_PATH)
148 | cursor = conn.cursor()
149 |
150 | # Удаляем записи из client_traffics по email
151 | for client in clients_to_remove:
152 | email = client['email']
153 | cursor.execute("DELETE FROM client_traffics WHERE email = ?", (email,))
154 |
155 | # Обновляем inbounds
156 | cursor.execute("UPDATE inbounds SET settings = ? WHERE id = ?", (json.dumps(settings), id))
157 | conn.commit()
158 | conn.close()
159 |
160 | # Функция для получения информации о пользователях из базы данных
161 | def get_users_info():
162 | conn = get_db_connection()
163 | cursor = conn.cursor()
164 |
165 | # Получение значения suburl
166 | cursor.execute("SELECT value FROM settings WHERE key = 'subURI'")
167 | suburl_row = cursor.fetchone()
168 | suburl = suburl_row[0] if suburl_row else "" # Убедимся, что значение suburl получено
169 |
170 | # Запрос для получения всех данных из таблицы inbounds
171 | cursor.execute("SELECT settings FROM inbounds")
172 | inbounds = cursor.fetchall()
173 |
174 | user_traffic = {} # Используем словарь для хранения трафика пользователей
175 |
176 | # Обработка данных
177 | for inbound in inbounds:
178 | settings = json.loads(inbound['settings'])
179 | for client in settings.get('clients', []):
180 | sub_id = client.get('subId')
181 | email = client.get('email')
182 | if sub_id:
183 | # Запрос для получения трафика пользователя
184 | cursor.execute("SELECT up, down FROM client_traffics WHERE email = ?", (email,))
185 | traffic = cursor.fetchone()
186 | up_traffic = traffic[0] / (1024 ** 3) if traffic and traffic[0] is not None else 0 # в гигабайтах
187 | down_traffic = traffic[1] / (1024 ** 3) if traffic and traffic[1] is not None else 0 # в гигабайтах
188 |
189 | # Если sub_id уже есть в словаре, суммируем трафик
190 | if sub_id in user_traffic:
191 | user_traffic[sub_id]['up'] += up_traffic
192 | user_traffic[sub_id]['down'] += down_traffic
193 | else:
194 | # Иначе добавляем нового пользователя в словарь
195 | user_traffic[sub_id] = {
196 | 'up': up_traffic,
197 | 'down': down_traffic,
198 | 'subscription_link': f"🔗 {suburl}{sub_id}" if suburl else f"/{sub_id}"
199 | }
200 | conn.close() # Закрываем соединение после завершения всех операций
201 |
202 | # Форматируем вывод
203 | user_lines = []
204 | for sub_id, traffic_info in user_traffic.items():
205 | user_lines.append(f"👤 {sub_id} - 🔼 Up {traffic_info['up']:.2f} GB / 🔽 Down {traffic_info['down']:.2f} GB\n{traffic_info['subscription_link']}")
206 |
207 | return "\n\n".join(user_lines) if user_lines else "No users"
208 |
209 | def calculate_total_traffic():
210 | connection = sqlite3.connect(DB_PATH)
211 | cursor = connection.cursor()
212 |
213 | cursor.execute("SELECT up, down FROM inbounds")
214 | total_up = total_down = 0
215 |
216 | for up, down in cursor.fetchall():
217 | total_up += up
218 | total_down += down
219 |
220 | connection.close()
221 | # Переводим значения в гигабайты
222 | total_up_gb = total_up / (1024 ** 3)
223 | total_down_gb = total_down / (1024 ** 3)
224 |
225 | return total_up_gb, total_down_gb
226 |
227 | # Функция для обработки команды /start
228 | async def start_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
229 | user_id = update.message.from_user.id if update.message else update.callback_query.from_user.id
230 |
231 | # Проверяем, является ли пользователь администратором
232 | if user_id != BOT_AID:
233 | await (update.message.reply_text("Access denied") if update.message else update.callback_query.edit_message_text("Access denied"))
234 | return
235 | # Сразу открываем основное меню
236 | keyboard = [
237 | [InlineKeyboardButton("📬 Inbounds", callback_data='inbounds')],
238 | [InlineKeyboardButton("🫂 User menu", callback_data='user_menu')]
239 | ]
240 | reply_markup = InlineKeyboardMarkup(keyboard)
241 | if update.message:
242 | await update.message.reply_text(NAME_MENU, reply_markup=reply_markup)
243 | else:
244 | await update.callback_query.edit_message_text(NAME_MENU, reply_markup=reply_markup)
245 |
246 | # Функция для обработки нажатия кнопок
247 | async def show_inbounds_menu(query):
248 | total_up, total_down = calculate_total_traffic()
249 | remarks = get_inbounds_remarks()
250 | if remarks:
251 | header = f"📬 Inbounds 📬\n🔼 Total Up {total_up:.2f} GB / 🔽 Total Down {total_down:.2f} GB\n"
252 | keyboard = [
253 | [InlineKeyboardButton(
254 | f"{remark} - {up / (1024 ** 3):.2f} GB / {down / (1024 ** 3):.2f} GB {'🟢' if enable == 1 else '⭕️'}",
255 | callback_data=f"select_{remark}"
256 | )]
257 | for remark, up, down, enable in remarks
258 | ]
259 | keyboard.append([InlineKeyboardButton("🔙 Return", callback_data='start_menu')])
260 | reply_markup = InlineKeyboardMarkup(keyboard)
261 | await query.edit_message_text(header, reply_markup=reply_markup)
262 | else:
263 | await query.edit_message_text("No inbounds available")
264 |
265 | async def button_click(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
266 | query = update.callback_query
267 | if query.from_user.id != BOT_AID:
268 | await query.answer("Access denied", show_alert=True)
269 | return
270 |
271 | if query.data == 'user_menu':
272 | await show_user_menu(query)
273 |
274 | elif query.data == 'show_users':
275 | users_info = get_users_info()
276 | keyboard = [
277 | [InlineKeyboardButton("🔙 Return", callback_data='user_menu')]
278 | ]
279 | reply_markup = InlineKeyboardMarkup(keyboard)
280 | await query.edit_message_text(text=f"🚦 Traffic / 💵 Subscription\n\n{users_info}", reply_markup=reply_markup)
281 |
282 | elif query.data == 'inbounds':
283 | await show_inbounds_menu(query)
284 |
285 | elif query.data.startswith("select_"):
286 | remark = query.data.split("select_")[1]
287 | toggle_enable(remark)
288 | await show_inbounds_menu(query)
289 |
290 | elif query.data == 'start_menu':
291 | await start_menu(update, context)
292 |
293 | elif query.data == 'add_user':
294 | await query.message.reply_text("Please enter a username to add")
295 | context.user_data['action'] = 'add_user'
296 |
297 | elif query.data == 'delete_user':
298 | users = get_all_users()
299 | if users:
300 | await show_delete_user_menu(query, users)
301 | else:
302 | await query.edit_message_text("No users available")
303 |
304 | elif query.data.startswith('remove_'):
305 | subId = query.data.split('_')[1]
306 | remove_user_from_all_ids(subId)
307 | users = get_all_users()
308 | await show_delete_user_menu(query, users)
309 |
310 | elif query.data == 'list_users':
311 | await list_users(update, context)
312 |
313 | elif query.data.startswith("toggle_"):
314 | await toggle_user_enable(update, context)
315 |
316 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup
317 |
318 | async def list_users(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
319 | all_ids = get_all_ids()
320 | users_keyboard = []
321 | row = []
322 | seen_sub_ids = set() # Множество для отслеживания уже добавленных subId
323 |
324 | for id, settings in all_ids:
325 | for client in settings.get('clients', []):
326 | sub_id = client.get('subId')
327 | enable_status = client.get('enable', False)
328 | emoji = "🟢" if enable_status else "⭕️"
329 |
330 | # Проверяем, был ли sub_id уже добавлен
331 | if sub_id not in seen_sub_ids:
332 | seen_sub_ids.add(sub_id) # Добавляем sub_id в множество
333 |
334 | # Добавляем кнопку в строку
335 | row.append(InlineKeyboardButton(f"{sub_id} {emoji}", callback_data=f"toggle_{sub_id}"))
336 |
337 | # Если в строке 2 кнопки, добавляем ее в клавиатуру и очищаем
338 | if len(row) == 2:
339 | users_keyboard.append(row)
340 | row = []
341 |
342 | # Добавляем последнюю строку, если она не пуста
343 | if row:
344 | users_keyboard.append(row)
345 |
346 | # Кнопка возврата внизу
347 | users_keyboard.append([InlineKeyboardButton("🔙 Return", callback_data='user_menu')])
348 |
349 | reply_markup = InlineKeyboardMarkup(users_keyboard)
350 | await update.callback_query.edit_message_text("🔄 Switch User Status", reply_markup=reply_markup)
351 |
352 |
353 | # Изменение состояния enable при нажатии на кнопку
354 | async def toggle_user_enable(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
355 | sub_id = update.callback_query.data.split("toggle_")[1]
356 |
357 | # Выводим subId для отладки
358 | print(f"Toggle request for subId: {sub_id}")
359 |
360 | connection = sqlite3.connect(DB_PATH)
361 | cursor = connection.cursor()
362 |
363 | # Получаем текущие настройки для subId
364 | cursor.execute("SELECT settings FROM inbounds WHERE id = ?", (sub_id,))
365 | result = cursor.fetchone()
366 |
367 | if result:
368 | settings = json.loads(result[0])
369 | found = False
370 |
371 | for client in settings['clients']:
372 | # Сравниваем subId для поиска
373 | if client['subId'] == sub_id:
374 | client['enable'] = not client['enable'] # Переключаем состояние enable
375 | found = True
376 | break
377 |
378 | if found:
379 | # Обновляем настройки в базе данных
380 | cursor.execute("UPDATE inbounds SET settings = ? WHERE id = ?", (json.dumps(settings), sub_id))
381 | connection.commit()
382 | await update.callback_query.answer(f"Статус пользователя {sub_id} изменен на {'включен' if client['enable'] else 'выключен'}")
383 | else:
384 | await update.callback_query.answer("Пользователь не найден в списке клиентов.")
385 | else:
386 | await update.callback_query.answer("Пользователь не найден в базе данных.")
387 |
388 | connection.close()
389 |
390 | # Обновляем список пользователей
391 | await list_users(update, context)
392 |
393 | # Изменение состояния enable при нажатии на кнопку
394 | async def toggle_user_enable(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
395 | sub_id = update.callback_query.data.split("toggle_")[1]
396 |
397 | # Выводим subId для отладки
398 | print(f"Toggle request for subId: {sub_id}")
399 |
400 | connection = sqlite3.connect(DB_PATH)
401 | cursor = connection.cursor()
402 |
403 | # Получаем текущие настройки для subId
404 | cursor.execute("SELECT id, settings FROM inbounds")
405 | all_ids = cursor.fetchall()
406 |
407 | for id, settings_json in all_ids:
408 | settings = json.loads(settings_json)
409 |
410 | # Находим клиента с соответствующим subId
411 | for client in settings.get('clients', []):
412 | if client.get('subId') == sub_id:
413 | # Переключаем состояние enable
414 | client['enable'] = not client.get('enable', False)
415 | print(f"Toggling enable for subId: {sub_id} to {client['enable']}")
416 |
417 | # Обновляем настройки в базе данных
418 | cursor.execute("UPDATE inbounds SET settings = ? WHERE id = ?", (json.dumps(settings), id))
419 | connection.commit()
420 | break # Выходим из цикла после изменения
421 |
422 | connection.close()
423 |
424 | # Обновляем меню пользователей
425 | await list_users(update, context)
426 |
427 | async def show_user_menu(query):
428 | keyboard = [
429 | [InlineKeyboardButton("✅ Add user", callback_data='add_user')],
430 | [InlineKeyboardButton("❌ Delete user", callback_data='delete_user')],
431 | [InlineKeyboardButton("🔄 Switch User Status", callback_data='list_users')],
432 | [InlineKeyboardButton("🚦 Traffic / 💵 Subscription", callback_data='show_users')],
433 | [InlineKeyboardButton("🔙 Return", callback_data='start_menu')]
434 | ]
435 | reply_markup = InlineKeyboardMarkup(keyboard)
436 | await query.edit_message_text("🫂 User menu 🫂", reply_markup=reply_markup)
437 |
438 | async def show_delete_user_menu(query, users):
439 | keyboard = []
440 | for i, user in enumerate(users):
441 | # Добавляем пользователей в две колонки
442 | if i % 2 == 0:
443 | keyboard.append([InlineKeyboardButton(user, callback_data=f'remove_{user}')])
444 | else:
445 | keyboard[-1].append(InlineKeyboardButton(user, callback_data=f'remove_{user}'))
446 | keyboard.append([InlineKeyboardButton("🔙 Return", callback_data='user_menu')])
447 | reply_markup = InlineKeyboardMarkup(keyboard)
448 | await query.edit_message_text("❌ Select the user to delete ❌", reply_markup=reply_markup)
449 |
450 | async def message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
451 | action = context.user_data.get('action')
452 | if action == 'add_user':
453 | name = update.message.text
454 | add_user_to_all_ids(name)
455 | await update.message.reply_text(f"User {name} added")
456 | context.user_data['action'] = None
457 |
458 | if __name__ == '__main__':
459 | application = ApplicationBuilder().token(BOT_TOKEN).build()
460 |
461 | # Регистрация обработчиков команд и сообщений
462 | application.add_handler(CommandHandler("start", start_menu))
463 | application.add_handler(CallbackQueryHandler(button_click))
464 | application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, message_handler))
465 |
466 | # Запуск бота
467 | application.run_polling()
468 |
--------------------------------------------------------------------------------
/database/x-ui.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/database/x-ui.db
--------------------------------------------------------------------------------
/database/x-ui.db_old:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/database/x-ui.db_old
--------------------------------------------------------------------------------
/media/reverse_proxy_manager.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/media/reverse_proxy_manager.png
--------------------------------------------------------------------------------
/media/reverse_proxy_manager_RU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/media/reverse_proxy_manager_RU.png
--------------------------------------------------------------------------------
/media/xui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/media/xui.png
--------------------------------------------------------------------------------
/media/xui_rp_install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/media/xui_rp_install.png
--------------------------------------------------------------------------------
/media/xui_rp_install_RU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cortez24rus/xui-reverse-proxy/cbfcfd33b7b14fdd331007c61bd79b0518d5706e/media/xui_rp_install_RU.png
--------------------------------------------------------------------------------
/other/check_domain_ip.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###################################
4 | ### Obtaining your external IP address
5 | ###################################
6 | check_ip() {
7 | IP4_REGEX="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"
8 |
9 | IP4=$(ip route get 8.8.8.8 2>/dev/null | grep -Po -- 'src \K\S*')
10 |
11 | if [[ ! $IP4 =~ $IP4_REGEX ]]; then
12 | IP4=$(curl -s --max-time 5 ipinfo.io/ip 2>/dev/null)
13 | fi
14 |
15 | if [[ ! $IP4 =~ $IP4_REGEX ]]; then
16 | echo "Не удалось получить внешний IP."
17 | return 1
18 | fi
19 | echo "$IP4"
20 | }
21 |
22 | ###################################
23 | ### Obtaining a domain IP address
24 | ###################################
25 | get_domain_ips() {
26 | IPS=($(dig @1.1.1.1 +short "$DOMAIN"))
27 | echo "${IPS[@]}"
28 | }
29 |
30 | ###################################
31 | ### Checking if the IP is in range
32 | ###################################
33 | ip_in_range() {
34 | local IP=$1
35 | local RANGE=$2
36 | ipcalc -n -c "$RANGE" | grep -q "$IP"
37 | }
38 |
39 | ###################################
40 | ### IP checks
41 | ###################################
42 | check_ip_in_cloudflare() {
43 | DOMAIN_IP=$1
44 | CLOUDFLARE_IPS=($(curl -s https://www.cloudflare.com/ips-v4))
45 |
46 | for RANGE in "${CLOUDFLARE_IPS[@]}"; do
47 | if ip_in_range "$DOMAIN_IP" "$RANGE"; then
48 | return 0
49 | fi
50 | done
51 | return 1
52 | }
53 |
54 | ###################################
55 | ### Domain address verification
56 | ###################################
57 | check_domain_ip() {
58 | local MY_IP
59 | local DOMAIN_IPS
60 | MY_IP=$(check_ip)
61 | DOMAIN_IPS=($(get_domain_ips))
62 |
63 | if [[ $? -ne 0 ]]; then
64 | echo " Не удалось получить внешний IP, завершение выполнения"
65 | exit 1
66 | fi
67 |
68 | echo " IP-адреса домена $DOMAIN: ${DOMAIN_IPS[@]}"
69 |
70 | if echo "${DOMAIN_IPS[@]}" | grep -qw "$MY_IP"; then
71 | echo " Ваш IP совпадает с одним из IP домена $DOMAIN (Status Dns only)"
72 | echo
73 | return 0
74 | fi
75 |
76 | for IP in "${DOMAIN_IPS[@]}"; do
77 | if check_ip_in_cloudflare "$IP"; then
78 | echo " IP-адрес $IP входит в диапазоны Cloudflare (Status Proxied)"
79 | echo
80 | return 0
81 | fi
82 | done
83 |
84 | echo " Ни один из IP-адресов ${DOMAIN_IPS[@]} не входит в диапазоны Cloudflare."
85 | echo "|-------------------------------------------------------------------------|"
86 | exit 1
87 | }
88 |
89 | final() {
90 | echo " Отработал на ура"
91 | }
92 |
93 | main() {
94 | apt install -y ipcalc > /dev/null 2>&1
95 | echo
96 | echo "|-------------------------------------------------------------------------|"
97 | DOMAIN="$@"
98 | check_domain_ip
99 | final
100 | echo "|-------------------------------------------------------------------------|"
101 | echo
102 | }
103 |
104 | main "$@"
105 |
--------------------------------------------------------------------------------
/other/flag_final.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | declare -A defaults
4 | declare -A args
5 | defaults_file="/usr/local/xui-rp/reinstall_defaults.conf"
6 |
7 | # Функция для отображения справки
8 | show_help() {
9 | echo ""
10 | echo "Usage: $0 [-f|--utils ] [-d|--dns ] [-a|--addu ] [-r|--autoupd ]"
11 | echo " [-b|--bbr ] [-i|--ipv6 ] [-w|--warp ] [-c|--cert ]"
12 | echo " [-m|--mon ] [-n|--nginx ] [-p|--panel ] [-u|--ufw ]"
13 | echo " [-s|--ssh ] [-t|--tgbot ] [-h|--help]"
14 | echo ""
15 | echo " -f, --utils Enable or disable utilities (default: ${defaults[utils]})"
16 | echo " -d, --dns Enable or disable DNS encryption (default: ${defaults[dns]})"
17 | echo " -a, --addu Enable or disable user addition (default: ${defaults[addu]})"
18 | echo " -r, --autoupd Enable or disable automatic updates (default: ${defaults[autoupd]})"
19 | echo " -b, --bbr Enable or disable BBR (default: ${defaults[bbr]})"
20 | echo " -i, --ipv6 Enable or disable IPv6 (default: ${defaults[ipv6]})"
21 | echo " -w, --warp Enable or disable Warp (default: ${defaults[warp]})"
22 | echo " -c, --cert Enable or disable certificate issuance (default: ${defaults[cert]})"
23 | echo " -m, --mon Enable or disable Monitoring (default: ${defaults[mon]})"
24 | echo " -n, --nginx Enable or disable NGINX installation (default: ${defaults[nginx]})"
25 | echo " -p, --panel Enable or disable panel installation (default: ${defaults[panel]})"
26 | echo " -u, --ufw Enable or disable UFW (default: ${defaults[ufw]})"
27 | echo " -s, --ssh Enable or disable SSH (default: ${defaults[ssh]})"
28 | echo " -t, --tgbot Enable or disable Telegram bot (default: ${defaults[tgbot]})"
29 | echo " -h, --help Display this help message"
30 | echo ""
31 | exit 0
32 | }
33 |
34 | # Функция для чтения значений из файла
35 | read_defaults_from_file() {
36 | if [[ -f $defaults_file ]]; then
37 | # Чтение и выполнение строк из файла
38 | while IFS= read -r line; do
39 | # Пропускаем пустые строки и комментарии
40 | [[ -z "$line" || "$line" =~ ^# ]] && continue
41 | eval "$line"
42 | done < $defaults_file
43 | else
44 | # Если файл не найден, используем значения по умолчанию
45 | defaults[utils]=true
46 | defaults[dns]=true
47 | defaults[addu]=true
48 | defaults[autoupd]=true
49 | defaults[bbr]=true
50 | defaults[ipv6]=true
51 | defaults[warp]=true
52 | defaults[cert]=true
53 | defaults[mon]=false
54 | defaults[nginx]=true
55 | defaults[panel]=true
56 | defaults[ufw]=true
57 | defaults[ssh]=true
58 | defaults[tgbot]=false
59 | fi
60 | }
61 |
62 | # Функция для записи значений в файл
63 | write_defaults_to_file() {
64 | cat > ${defaults_file}<] [-d|--dns ] [-a|--addu ]"
225 | echo " [-r|--autoupd ] [-b|--bbr ] [-i|--ipv6 ] [-w|--warp ]"
226 | echo " [-c|--cert ] [-m|--mon ] [-n|--nginx ] [-p|--panel ]"
227 | echo " [-f|--firewall ] [-s|--ssh ] [-t|--tgbot ] [-g|--generate ]"
228 | echo " [-x|--skip-check ] [-h|--help]"
229 | echo
230 | echo " -u, --utils Additional utilities (default: ${defaults[utils]})"
231 | echo " Дополнительные утилиты"
232 | echo " -d, --dns DNS encryption (default: ${defaults[dns]})"
233 | echo " Шифрование DNS"
234 | echo " -a, --addu User addition (default: ${defaults[addu]})"
235 | echo " Добавление пользователя"
236 | echo " -r, --autoupd Automatic updates (default: ${defaults[autoupd]})"
237 | echo " Автоматические обновления"
238 | echo " -b, --bbr BBR (TCP Congestion Control) (default: ${defaults[bbr]})"
239 | echo " BBR (управление перегрузкой TCP)"
240 | echo " -i, --ipv6 Disable IPv6 support (default: ${defaults[ipv6]})"
241 | echo " Отключить поддержку IPv6 "
242 | echo " -w, --warp WARP (default: ${defaults[warp]})"
243 | echo " WARP"
244 | echo " -c, --cert Certificate issuance for domain (default: ${defaults[cert]})"
245 | echo " Выпуск сертификатов для домена"
246 | echo " -m, --mon Monitoring services (node_exporter) (default: ${defaults[mon]})"
247 | echo " Сервисы мониторинга (node_exporter)"
248 | echo " -n, --nginx NGINX installation (default: ${defaults[nginx]})"
249 | echo " Установка NGINX"
250 | echo " -p, --panel Panel installation for user management (default: ${defaults[panel]})"
251 | echo " Установка панели для управления пользователями"
252 | echo " -f, --firewall Firewall configuration (default: ${defaults[firewall]})"
253 | echo " Настройка файрвола"
254 | echo " -s, --ssh SSH access (default: ${defaults[ssh]})"
255 | echo " SSH доступ"
256 | echo " -t, --tgbot Telegram bot integration (default: ${defaults[tgbot]})"
257 | echo " Интеграция Telegram бота"
258 | echo " -g, --generate Generate a random string for configuration (default: ${defaults[generate]})"
259 | echo " Генерация случайных путей для конфигурации"
260 | echo " -x, --skip-check Disable the check functionality (default: ${defaults[skip-check]})"
261 | echo " Отключение проверки"
262 | echo " -h, --help Display this help message "
263 | echo " Показать это сообщение помощи"
264 | echo
265 | exit 0
266 | }
267 |
268 | ###################################
269 | ### Reading values from file
270 | ###################################
271 | read_defaults_from_file() {
272 | if [[ -f $defaults_file ]]; then
273 | # Чтение и выполнение строк из файла
274 | while IFS= read -r line; do
275 | # Пропускаем пустые строки и комментарии
276 | [[ -z "$line" || "$line" =~ ^# ]] && continue
277 | eval "$line"
278 | done < $defaults_file
279 | else
280 | # Если файл не найден, используем значения по умолчанию
281 | defaults[utils]=true
282 | defaults[dns]=true
283 | defaults[addu]=true
284 | defaults[autoupd]=true
285 | defaults[bbr]=true
286 | defaults[ipv6]=true
287 | defaults[warp]=true
288 | defaults[cert]=true
289 | defaults[mon]=false
290 | defaults[nginx]=true
291 | defaults[panel]=true
292 | defaults[firewall]=true
293 | defaults[ssh]=true
294 | defaults[tgbot]=false
295 | defaults[generate]=true
296 | defaults[skip-check]=false
297 | fi
298 | }
299 |
300 | ###################################
301 | ### Writing values to a file
302 | ###################################
303 | write_defaults_to_file() {
304 | cat > ${defaults_file}< >(tee -a "$LOGFILE") 2>&1
489 | }
490 |
491 | ###################################
492 | ### Language selection
493 | ###################################
494 | select_language() {
495 | L=E
496 | hint " $(text 0) \n" # Показывает информацию о доступных языках
497 | reading " $(text 1) " LANGUAGE # Запрашивает выбор языка
498 |
499 | # Устанавливаем язык в зависимости от выбора
500 | case "$LANGUAGE" in
501 | 1) L=E ;; # Если выбран английский
502 | 2) L=R ;; # Если выбран русский
503 | # 3) L=C ;; # Если выбран китайский
504 | # 4) L=F ;; # Если выбран персидский
505 | *) L=E ;; # По умолчанию — английский
506 | esac
507 | }
508 |
509 | ###################################
510 | ### Checking the operating system
511 | ###################################
512 | check_operating_system() {
513 | if [ -s /etc/os-release ]; then
514 | SYS="$(grep -i pretty_name /etc/os-release | cut -d \" -f2)"
515 | elif [ -x "$(type -p hostnamectl)" ]; then
516 | SYS="$(hostnamectl | grep -i system | cut -d : -f2)"
517 | elif [ -x "$(type -p lsb_release)" ]; then
518 | SYS="$(lsb_release -sd)"
519 | elif [ -s /etc/lsb-release ]; then
520 | SYS="$(grep -i description /etc/lsb-release | cut -d \" -f2)"
521 | elif [ -s /etc/redhat-release ]; then
522 | SYS="$(grep . /etc/redhat-release)"
523 | elif [ -s /etc/issue ]; then
524 | SYS="$(grep . /etc/issue | cut -d '\' -f1 | sed '/^[ ]*$/d')"
525 | fi
526 |
527 | REGEX=("debian" "ubuntu" "centos|red hat|kernel|alma|rocky")
528 | RELEASE=("Debian" "Ubuntu" "CentOS")
529 | EXCLUDE=("---")
530 | MAJOR=("10" "20" "7")
531 | PACKAGE_UPDATE=("apt -y update" "apt -y update" "yum -y update --skip-broken")
532 | PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install")
533 | PACKAGE_UNINSTALL=("apt -y autoremove" "apt -y autoremove" "yum -y autoremove")
534 |
535 | for int in "${!REGEX[@]}"; do
536 | [[ "${SYS,,}" =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && break
537 | done
538 |
539 | # Проверка на кастомизированные системы от различных производителей
540 | if [ -z "$SYSTEM" ]; then
541 | [ -x "$(type -p yum)" ] && int=2 && SYSTEM='CentOS' || error " $(text 5) "
542 | fi
543 |
544 | # Определение основной версии Linux
545 | MAJOR_VERSION=$(sed "s/[^0-9.]//g" <<< "$SYS" | cut -d. -f1)
546 |
547 | # Сначала исключаем системы, указанные в EXCLUDE, затем для оставшихся делаем сравнение по основной версии
548 | for ex in "${EXCLUDE[@]}"; do [[ ! "${SYS,,}" =~ $ex ]]; done &&
549 | [[ "$MAJOR_VERSION" -lt "${MAJOR[int]}" ]] && error " $(text 71) "
550 | }
551 |
552 | ###################################
553 | ### Checking and installing dependencies
554 | ###################################
555 | check_dependencies() {
556 | # Зависимости, необходимые для трех основных систем
557 | [ "${SYSTEM}" = 'CentOS' ] && ${PACKAGE_INSTALL[int]} vim-common epel-release
558 | DEPS_CHECK=("ping" "wget" "curl" "systemctl" "ip" "sudo")
559 | DEPS_INSTALL=("iputils-ping" "wget" "curl" "systemctl" "iproute2" "sudo")
560 |
561 | for g in "${!DEPS_CHECK[@]}"; do
562 | [ ! -x "$(type -p ${DEPS_CHECK[g]})" ] && [[ ! "${DEPS[@]}" =~ "${DEPS_INSTALL[g]}" ]] && DEPS+=(${DEPS_INSTALL[g]})
563 | done
564 |
565 | if [ "${#DEPS[@]}" -ge 1 ]; then
566 | info "\n $(text 72) ${DEPS[@]} \n"
567 | ${PACKAGE_UPDATE[int]}
568 | ${PACKAGE_INSTALL[int]} ${DEPS[@]}
569 | else
570 | info "\n $(text 73) \n"
571 | fi
572 | }
573 |
574 | ###################################
575 | ### Root check
576 | ###################################
577 | check_root() {
578 | if [[ $EUID -ne 0 ]]; then
579 | error " $(text 8) "
580 | fi
581 | }
582 |
583 | ###################################
584 | ### Banner
585 | ###################################
586 | banner_1() {
587 | echo
588 | echo " █░█ █░░█ ░▀░ ░░ █▀▀█ █▀▀ ▀█░█▀ █▀▀ █▀▀█ █▀▀ █▀▀ ░░ █▀▀█ █▀▀█ █▀▀█ █░█ █░░█ "
589 | echo " ▄▀▄ █░░█ ▀█▀ ▀▀ █▄▄▀ █▀▀ ░█▄█░ █▀▀ █▄▄▀ ▀▀█ █▀▀ ▀▀ █░░█ █▄▄▀ █░░█ ▄▀▄ █▄▄█ "
590 | echo " ▀░▀ ░▀▀▀ ▀▀▀ ░░ ▀░▀▀ ▀▀▀ ░░▀░░ ▀▀▀ ▀░▀▀ ▀▀▀ ▀▀▀ ░░ █▀▀▀ ▀░▀▀ ▀▀▀▀ ▀░▀ ▄▄▄█ "
591 | echo
592 | echo
593 | }
594 |
595 | ###################################
596 | ### Installation request
597 | ###################################
598 | start_installation() {
599 | warning " $(text 5) "
600 | echo
601 | info " $(text 6) "
602 | warning " apt-get update && apt-get full-upgrade -y && reboot "
603 | echo
604 | reading " $(text 8) " ANSWER_START
605 | case "${ANSWER_START,,}" in
606 | y|"")
607 | ;;
608 | *)
609 | error " $(text 9) "
610 | ;;
611 | esac
612 | }
613 |
614 | ###################################
615 | ### Obtaining your external IP address
616 | ###################################
617 | check_ip() {
618 | IP4_REGEX="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"
619 |
620 | IP4=$(ip route get 8.8.8.8 2>/dev/null | grep -Po -- 'src \K\S*')
621 |
622 | if [[ ! $IP4 =~ $IP4_REGEX ]]; then
623 | IP4=$(curl -s --max-time 5 ipinfo.io/ip 2>/dev/null)
624 | fi
625 |
626 | if [[ ! $IP4 =~ $IP4_REGEX ]]; then
627 | echo "Не удалось получить внешний IP."
628 | return 1
629 | fi
630 | }
631 |
632 | ###################################
633 | ### Request and response from Cloudflare API
634 | ###################################
635 | get_test_response() {
636 | testdomain=$(echo "${DOMAIN}" | rev | cut -d '.' -f 1-2 | rev)
637 |
638 | if [[ "$CFTOKEN" =~ [A-Z] ]]; then
639 | test_response=$(curl --silent --request GET --url https://api.cloudflare.com/client/v4/zones --header "Authorization: Bearer ${CFTOKEN}" --header "Content-Type: application/json")
640 | else
641 | test_response=$(curl --silent --request GET --url https://api.cloudflare.com/client/v4/zones --header "X-Auth-Key: ${CFTOKEN}" --header "X-Auth-Email: ${EMAIL}" --header "Content-Type: application/json")
642 | fi
643 | }
644 |
645 | ###################################
646 | ### Domain validation in cloudflare
647 | ###################################
648 | check_cf_token() {
649 | while ! echo "$test_response" | grep -qE "\"${testdomain}\"|\"#dns_records:edit\"|\"#dns_records:read\"|\"#zone:read\""; do
650 | local temp_domain
651 | DOMAIN=""
652 | SUBDOMAIN=""
653 |
654 | while [[ -z "$temp_domain" ]]; do
655 | reading " $(text 13) " temp_domain
656 | echo
657 | done
658 |
659 | temp_domain=$(echo "$temp_domain" | sed -E 's/^https?:\/\///' | sed -E 's/(:[0-9]+)?(\/[a-zA-Z0-9_\-\/]+)?$//')
660 |
661 | if [[ "$temp_domain" =~ ${regex[domain]} ]]; then
662 | SUBDOMAIN="$temp_domain" # Весь домен сохраняем в SUBDOMAIN
663 | DOMAIN="${BASH_REMATCH[2]}" # Извлекаем домен второго уровня
664 | else
665 | DOMAIN="$temp_domain" # Если это домен второго уровня, то просто сохраняем
666 | SUBDOMAIN="www.$temp_domain" # Для домена второго уровня подставляем www в SUBDOMAIN
667 | fi
668 |
669 | while [[ -z $EMAIL ]]; do
670 | reading " $(text 15) " EMAIL
671 | echo
672 | done
673 |
674 | while [[ -z $CFTOKEN ]]; do
675 | reading " $(text 16) " CFTOKEN
676 | done
677 |
678 | [[ ${args[skip-check]} == "false" ]] && get_test_response
679 | info " $(text 17) "
680 | done
681 | }
682 |
683 | ###################################
684 | ### Processing paths with a loop
685 | ###################################
686 | validate_path() {
687 | local VARIABLE_NAME="$1"
688 | local PATH_VALUE
689 |
690 | # Проверка на пустое значение
691 | while true; do
692 | case "$VARIABLE_NAME" in
693 | CDNGRPC)
694 | reading " $(text 20) " PATH_VALUE
695 | ;;
696 | CDNSPLIT)
697 | reading " $(text 21) " PATH_VALUE
698 | ;;
699 | CDNHTTPU)
700 | reading " $(text 22) " PATH_VALUE
701 | ;;
702 | CDNWS)
703 | reading " $(text 23) " PATH_VALUE
704 | ;;
705 | METRICS)
706 | reading " $(text 24) " PATH_VALUE
707 | ;;
708 | ADGUARDPATH)
709 | reading " $(text 25) " PATH_VALUE
710 | ;;
711 | WEB_BASE_PATH)
712 | reading " $(text 26) " PATH_VALUE
713 | ;;
714 | SUB_PATH)
715 | reading " $(text 27) " PATH_VALUE
716 | ;;
717 | SUB_JSON_PATH)
718 | reading " $(text 28) " PATH_VALUE
719 | ;;
720 | esac
721 |
722 | if [[ -z "$PATH_VALUE" ]]; then
723 | warning " $(text 29) "
724 | echo
725 | elif [[ $PATH_VALUE =~ ['{}\$/\\'] ]]; then
726 | warning " $(text 30) "
727 | echo
728 | else
729 | break
730 | fi
731 | done
732 |
733 | # Экранируем пробелы в пути
734 | local ESCAPED_PATH=$(echo "$PATH_VALUE" | sed 's/ /\\ /g')
735 |
736 | # Присваиваем значение переменной
737 | case "$VARIABLE_NAME" in
738 | CDNGRPC)
739 | export CDNGRPC="$ESCAPED_PATH"
740 | ;;
741 | CDNSPLIT)
742 | export CDNSPLIT="$ESCAPED_PATH"
743 | ;;
744 | CDNHTTPU)
745 | export CDNHTTPU="$ESCAPED_PATH"
746 | ;;
747 | CDNWS)
748 | export CDNWS="$ESCAPED_PATH"
749 | ;;
750 | METRICS)
751 | export METRICS="$ESCAPED_PATH"
752 | ;;
753 | ADGUARDPATH)
754 | export ADGUARDPATH="$ESCAPED_PATH"
755 | ;;
756 | WEB_BASE_PATH)
757 | export WEB_BASE_PATH="$ESCAPED_PATH"
758 | ;;
759 | SUB_PATH)
760 | export SUB_PATH="$ESCAPED_PATH"
761 | ;;
762 | SUB_JSON_PATH)
763 | export SUB_JSON_PATH="$ESCAPED_PATH"
764 | ;;
765 | esac
766 | }
767 |
768 | ###################################
769 | ### DNS Selection
770 | ###################################
771 | choise_dns () {
772 | while true; do
773 | hint " $(text 31) \n" && reading " $(text 1) " CHOISE_DNS
774 | case $CHOISE_DNS in
775 | 1)
776 | info " $(text 32) "
777 | break
778 | ;;
779 | 2)
780 | info " $(text 25) "
781 | if [[ ${args[generate]} == "true" ]]; then
782 | ADGUARDPATH=$(eval ${generate[path]})
783 | else
784 | echo
785 | tilda "$(text 10)"
786 | validate_path ADGUARDPATH
787 | fi
788 | echo
789 | break
790 | ;;
791 | *)
792 | info " $(text 33) "
793 | ;;
794 | esac
795 | done
796 | }
797 |
798 | ###################################
799 | ### Data entry
800 | ###################################
801 | data_entry() {
802 | tilda "$(text 10)"
803 | # reading " $(text 70) " SECRET_PASSWORD
804 |
805 | # tilda "$(text 10)"
806 |
807 | reading " $(text 11) " USERNAME
808 | echo
809 | reading " $(text 12) " PASSWORD
810 | [[ ${args[addu]} == "true" ]] && add_user
811 |
812 | check_cf_token
813 |
814 | tilda "$(text 10)"
815 |
816 | choise_dns
817 |
818 | # reading " $(text 19) " REALITY
819 |
820 | if [[ ${args[generate]} == "true" ]]; then
821 | # echo
822 | # CDNGRPC=$(eval ${generate[path]})
823 | # CDNSPLIT=$(eval ${generate[path]})
824 | # CDNHTTPU=$(eval ${generate[path]})
825 | # CDNWS=$(eval ${generate[path]})
826 | WEB_BASE_PATH=$(eval ${generate[path]})
827 | SUB_PATH=$(eval ${generate[path]})
828 | SUB_JSON_PATH=$(eval ${generate[path]})
829 | else
830 | # validate_path CDNGRPC
831 | # echo
832 | # validate_path CDNSPLIT
833 | # echo
834 | # validate_path CDNHTTPU
835 | # echo
836 | # validate_path CDNWS
837 | # tilda "$(text 10)"
838 | validate_path WEB_BASE_PATH
839 | echo
840 | validate_path SUB_PATH
841 | echo
842 | validate_path SUB_JSON_PATH
843 | fi
844 |
845 | if [[ ${args[mon]} == "true" ]]; then
846 | if [[ ${args[generate]} == "true" ]]; then
847 | METRICS=$(eval ${generate[path]})
848 | else
849 | echo
850 | validate_path METRICS
851 | fi
852 | fi
853 |
854 | if [[ ${args[ssh]} == "true" ]]; then
855 | tilda "$(text 10)"
856 | reading " $(text 54) " ANSWER_SSH
857 | if [[ "${ANSWER_SSH,,}" == "y" ]]; then
858 | info " $(text 48) "
859 | out_data " $(text 49) "
860 | echo
861 | out_data " $(text 50) "
862 | out_data " $(text 51) "
863 | echo
864 | out_data " $(text 52)" "type \$env:USERPROFILE\.ssh\id_rsa.pub | ssh -p 22 ${USERNAME}@${IP4} \"cat >> ~/.ssh/authorized_keys\""
865 | out_data " $(text 53)" "ssh-copy-id -p 22 ${USERNAME}@${IP4}"
866 | echo
867 |
868 | # Цикл проверки наличия ключей
869 | while true; do
870 | if [[ -s "/home/${USERNAME}/.ssh/authorized_keys" || -s "/root/.ssh/authorized_keys" ]]; then
871 | info " $(text 56) " # Ключи найдены
872 | SSH_OK=true
873 | break
874 | else
875 | warning " $(text 55) " # Ключи отсутствуют
876 | echo
877 | reading " $(text 54) " ANSWER_SSH
878 | if [[ "${ANSWER_SSH,,}" != "y" ]]; then
879 | warning " $(text 9) " # Настройка отменена
880 | SSH_OK=false
881 | break
882 | fi
883 | fi
884 | done
885 | tilda "$(text 10)"
886 | else
887 | warning " $(text 9) " # Настройка пропущена
888 | SSH_OK=false
889 | fi
890 | fi
891 |
892 | if [[ ${args[tgbot]} == "true" ]]; then
893 | reading " $(text 35) " ADMIN_ID
894 | echo
895 | reading " $(text 34) " BOT_TOKEN
896 | tilda "$(text 10)"
897 | fi
898 |
899 | SUB_URI=https://${DOMAIN}/${SUB_PATH}/
900 | SUB_JSON_URI=https://${DOMAIN}/${SUB_JSON_PATH}/
901 | }
902 |
903 | ###################################
904 | ### Install NGINX
905 | ###################################
906 | nginx_gpg() {
907 | case "$SYSTEM" in
908 | Debian)
909 | ${PACKAGE_INSTALL[int]} debian-archive-keyring
910 | curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
911 | | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
912 | gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
913 | echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
914 | http://nginx.org/packages/debian `lsb_release -cs` nginx" \
915 | | tee /etc/apt/sources.list.d/nginx.list
916 | echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
917 | | tee /etc/apt/preferences.d/99nginx
918 | ;;
919 |
920 | Ubuntu)
921 | ${PACKAGE_INSTALL[int]} ubuntu-keyring
922 | curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
923 | | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
924 | gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
925 | echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
926 | http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
927 | | tee /etc/apt/sources.list.d/nginx.list
928 | echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
929 | | tee /etc/apt/preferences.d/99nginx
930 | ;;
931 |
932 | CentOS|Fedora)
933 | ${PACKAGE_INSTALL[int]} yum-utils
934 | cat < /etc/yum.repos.d/nginx.repo
935 | [nginx-stable]
936 | name=nginx stable repo
937 | baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/
938 | gpgcheck=1
939 | enabled=1
940 | gpgkey=https://nginx.org/keys/nginx_signing.key
941 | module_hotfixes=true
942 |
943 | [nginx-mainline]
944 | name=nginx mainline repo
945 | baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/
946 | gpgcheck=1
947 | enabled=0
948 | gpgkey=https://nginx.org/keys/nginx_signing.key
949 | module_hotfixes=true
950 | EOL
951 | ;;
952 | esac
953 | ${PACKAGE_UPDATE[int]}
954 | ${PACKAGE_INSTALL[int]} nginx
955 | systemctl daemon-reload
956 | systemctl start nginx
957 | systemctl enable nginx
958 | systemctl restart nginx
959 | systemctl status nginx --no-pager
960 | }
961 |
962 | ###################################
963 | ### Installing packages
964 | ###################################
965 | installation_of_utilities() {
966 | info " $(text 36) "
967 | case "$SYSTEM" in
968 | Debian|Ubuntu)
969 | DEPS_PACK_CHECK=("jq" "ufw" "zip" "wget" "gpg" "nano" "cron" "sqlite3" "certbot" "openssl" "netstat" "htpasswd" "update-ca-certificates" "add-apt-repository" "unattended-upgrades" "certbot-dns-cloudflare")
970 | DEPS_PACK_INSTALL=("jq" "ufw" "zip" "wget" "gnupg2" "nano" "cron" "sqlite3" "certbot" "openssl" "net-tools" "apache2-utils" "ca-certificates" "software-properties-common" "unattended-upgrades" "python3-certbot-dns-cloudflare")
971 |
972 | for g in "${!DEPS_PACK_CHECK[@]}"; do
973 | [ ! -x "$(type -p ${DEPS_PACK_CHECK[g]})" ] && [[ ! "${DEPS_PACK[@]}" =~ "${DEPS_PACK_INSTALL[g]}" ]] && DEPS_PACK+=(${DEPS_PACK_INSTALL[g]})
974 | done
975 |
976 | if [ "${#DEPS_PACK[@]}" -ge 1 ]; then
977 | info " $(text 77) ": ${DEPS_PACK[@]}
978 | ${PACKAGE_UPDATE[int]}
979 | ${PACKAGE_INSTALL[int]} ${DEPS_PACK[@]}
980 | else
981 | info " $(text 78) "
982 | fi
983 | ;;
984 |
985 |
986 | CentOS|Fedora)
987 | DEPS_PACK_CHECK=("jq" "zip" "tar" "wget" "gpg" "nano" "crontab" "sqlite3" "openssl" "netstat" "nslookup" "htpasswd" "certbot" "update-ca-certificates" "certbot-dns-cloudflare")
988 | DEPS_PACK_INSTALL=("jq" "zip" "tar" "wget" "gnupg2" "nano" "cronie" "sqlite" "openssl" "net-tools" "bind-utils" "httpd-tools" "certbot" "ca-certificates" "python3-certbot-dns-cloudflare")
989 |
990 | for g in "${!DEPS_PACK_CHECK[@]}"; do
991 | [ ! -x "$(type -p ${DEPS_PACK_CHECK[g]})" ] && [[ ! "${DEPS_PACK[@]}" =~ "${DEPS_PACK_INSTALL[g]}" ]] && DEPS_PACK+=(${DEPS_PACK_INSTALL[g]})
992 | done
993 |
994 | if [ "${#DEPS_PACK[@]}" -ge 1 ]; then
995 | info " $(text 77) ": ${DEPS_PACK[@]}
996 | ${PACKAGE_UPDATE[int]}
997 | ${PACKAGE_INSTALL[int]} ${DEPS_PACK[@]}
998 | else
999 | info " $(text 78) "
1000 | fi
1001 | ;;
1002 | esac
1003 |
1004 | nginx_gpg
1005 | ${PACKAGE_INSTALL[int]} systemd-resolved
1006 | tilda "$(text 10)"
1007 | }
1008 |
1009 | ###################################
1010 | ### DNS Systemd-resolved
1011 | ###################################
1012 | dns_systemd_resolved() {
1013 | tee /etc/systemd/resolved.conf <> /etc/apt/apt.conf.d/50unattended-upgrades
1136 | echo unattended-upgrades unattended-upgrades/enable_auto_updates boolean true | debconf-set-selections
1137 | dpkg-reconfigure -f noninteractive unattended-upgrades
1138 | systemctl restart unattended-upgrades
1139 | ;;
1140 |
1141 | CentOS|Fedora)
1142 | cat > /etc/dnf/automatic.conf <> /etc/sysctl.conf
1169 | fi
1170 |
1171 | if [[ ! "$(sysctl net.ipv4.tcp_congestion_control)" == *"bbr" ]]; then
1172 | echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.conf
1173 | fi
1174 | sysctl -p
1175 | }
1176 |
1177 | ###################################
1178 | ### Disabling IPv6
1179 | ###################################
1180 | disable_ipv6() {
1181 | info " $(text 42) "
1182 | interface_name=$(ifconfig -s | awk 'NR==2 {print $1}')
1183 | if [[ ! "$(sysctl net.ipv6.conf.all.disable_ipv6)" == *"= 1" ]]; then
1184 | echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
1185 | fi
1186 |
1187 | if [[ ! "$(sysctl net.ipv6.conf.default.disable_ipv6)" == *"= 1" ]]; then
1188 | echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
1189 | fi
1190 | if [[ ! "$(sysctl net.ipv6.conf.lo.disable_ipv6)" == *"= 1" ]]; then
1191 | echo "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf
1192 | fi
1193 |
1194 | if [[ ! "$(sysctl net.ipv6.conf.$interface_name.disable_ipv6)" == *"= 1" ]]; then
1195 | echo "net.ipv6.conf.$interface_name.disable_ipv6 = 1" >> /etc/sysctl.conf
1196 | fi
1197 | sysctl -p
1198 | tilda "$(text 10)"
1199 | }
1200 |
1201 | ###################################
1202 | ### WARP
1203 | ###################################
1204 | warp() {
1205 | info " $(text 43) "
1206 |
1207 | mkdir -p /usr/local/reverse_proxy/
1208 | mkdir -p /etc/systemd/system/warp-svc.service.d
1209 | cd /usr/local/reverse_proxy/
1210 |
1211 | case "$SYSTEM" in
1212 | Debian|Ubuntu)
1213 | while ! wget --progress=dot:mega --timeout=30 --tries=10 --retry-connrefused "https://pkg.cloudflareclient.com/pool/$(grep "VERSION_CODENAME=" /etc/os-release | cut -d "=" -f 2)/main/c/cloudflare-warp/cloudflare-warp_2024.6.497-1_amd64.deb"; do
1214 | warning " $(text 38) "
1215 | sleep 3
1216 | done
1217 | apt install -y ./cloudflare-warp_2024.6.497-1_amd64.deb
1218 | ;;
1219 |
1220 | CentOS|Fedora)
1221 | while ! wget --progress=dot:mega --timeout=30 --tries=10 --retry-connrefused "https://pkg.cloudflareclient.com/rpm/x86_64/cloudflare-warp-2024.6.497-1.x86_64.rpm"; do
1222 | warning " $(text 38) "
1223 | sleep 3
1224 | done
1225 | sudo yum localinstall -y cloudflare-warp-2024.6.497-1.x86_64.rpm
1226 | ;;
1227 | esac
1228 |
1229 | rm -rf cloudflare-warp_*
1230 | cd ~
1231 |
1232 | cat > /etc/systemd/system/warp-svc.service.d/override.conf < ${CF_CREDENTIALS_PATH} < ${CF_CREDENTIALS_PATH} <> /etc/letsencrypt/renewal/${DOMAIN}.conf
1301 | # echo ""
1302 | # openssl dhparam -out /etc/nginx/dhparam.pem 2048
1303 | # else
1304 | # echo "renew_hook = cat /etc/letsencrypt/live/${DOMAIN}/fullchain.pem /etc/letsencrypt/live/${DOMAIN}/privkey.pem > /etc/haproxy/certs/${DOMAIN}.pem && systemctl restart haproxy" >> /etc/letsencrypt/renewal/${DOMAIN}.conf
1305 | # echo ""
1306 | # openssl dhparam -out /etc/haproxy/dhparam.pem 2048
1307 | # fi
1308 | }
1309 |
1310 | ###################################
1311 | ### Node exporter
1312 | ###################################
1313 | monitoring() {
1314 | info " $(text 66) "
1315 | bash <(curl -Ls https://github.com/cortez24rus/grafana-prometheus/raw/refs/heads/main/prometheus_node_exporter.sh)
1316 |
1317 | COMMENT_METRIC="location /${METRICS} {
1318 | auth_basic \"Restricted Content\";
1319 | auth_basic_user_file /etc/nginx/.htpasswd;
1320 | proxy_pass http://127.0.0.1:9100/metrics;
1321 | proxy_set_header Host \$host;
1322 | proxy_set_header X-Real-IP \$remote_addr;
1323 | proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
1324 | proxy_set_header X-Forwarded-Proto \$scheme;
1325 | break;
1326 | }"
1327 |
1328 | tilda "$(text 10)"
1329 | }
1330 |
1331 | ###################################
1332 | ### NGINX
1333 | ###################################
1334 | nginx_setup() {
1335 | info " $(text 45) "
1336 |
1337 | mkdir -p /etc/nginx/stream-enabled/
1338 | mkdir -p /etc/nginx/conf.d/
1339 | rm -rf /etc/nginx/conf.d/default.conf
1340 | touch /etc/nginx/.htpasswd
1341 | htpasswd -nb "$USERNAME" "$PASSWORD" > /etc/nginx/.htpasswd
1342 | openssl dhparam -out /etc/nginx/dhparam.pem 2048
1343 |
1344 | case "$SYSTEM" in
1345 | Debian|Ubuntu)
1346 | USERNGINX="www-data"
1347 | ;;
1348 |
1349 | CentOS|Fedora)
1350 | USERNGINX="nginx"
1351 | ;;
1352 | esac
1353 |
1354 | nginx_conf
1355 | stream_conf
1356 | local_conf
1357 | random_site
1358 |
1359 | systemctl daemon-reload
1360 | systemctl restart nginx
1361 | nginx -s reload
1362 |
1363 | tilda "$(text 10)"
1364 | }
1365 |
1366 | ###################################
1367 | ### http conf
1368 | ###################################
1369 | nginx_conf() {
1370 | cat > /etc/nginx/nginx.conf < /etc/nginx/stream-enabled/stream.conf < /etc/nginx/conf.d/local.conf <|\.\.\.|\.\.\/|\/\/\/)"){set \$hack 1;}
1512 | error_page 400 402 403 500 501 502 503 504 =404 /404;
1513 | proxy_intercept_errors on;
1514 |
1515 | if (\$host = ${IP4}) {
1516 | return 444;
1517 | }
1518 | # PANEL
1519 | location /${WEB_BASE_PATH} {
1520 | if (\$hack = 1) {return 404;}
1521 | proxy_redirect off;
1522 | proxy_set_header Host \$host;
1523 | proxy_set_header X-Real-IP \$remote_addr;
1524 | proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
1525 | proxy_set_header X-Real-IP \$remote_addr;
1526 | proxy_set_header Range \$http_range;
1527 | proxy_set_header If-Range \$http_if_range;
1528 | proxy_pass http://127.0.0.1:36075/${WEB_BASE_PATH};
1529 | break;
1530 | }
1531 | # SUB
1532 | location /${SUB_PATH} {
1533 | if (\$hack = 1) {return 404;}
1534 | proxy_redirect off;
1535 | proxy_set_header Host \$host;
1536 | proxy_set_header X-Real-IP \$remote_addr;
1537 | proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
1538 | proxy_pass http://127.0.0.1:36074/${SUB_PATH};
1539 | break;
1540 | }
1541 | # SUB JSON
1542 | location /${SUB_JSON_PATH} {
1543 | if (\$hack = 1) {return 404;}
1544 | proxy_redirect off;
1545 | proxy_set_header Host \$host;
1546 | proxy_set_header X-Real-IP \$remote_addr;
1547 | proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
1548 | proxy_pass http://127.0.0.1:36074/${SUB_JSON_PATH};
1549 | break;
1550 | }
1551 | # Node Exporter
1552 | ${COMMENT_METRIC}
1553 | # Adguard Home
1554 | ${COMMENT_AGH}
1555 | }
1556 | EOF
1557 | }
1558 |
1559 | ###################################
1560 | ### Selecting a random site
1561 | ###################################
1562 | random_site() {
1563 | info " $(text 79) "
1564 | mkdir -p /var/www/html/ /usr/local/reverse_proxy/
1565 |
1566 | cd /usr/local/reverse_proxy/ || echo "Не удалось перейти в /usr/local/reverse_proxy/"
1567 |
1568 | if [[ ! -d "simple-web-templates-main" ]]; then
1569 | while ! wget -q --progress=dot:mega --timeout=30 --tries=10 --retry-connrefused "https://github.com/cortez24rus/simple-web-templates/archive/refs/heads/main.zip"; do
1570 | warning " $(text 38) "
1571 | sleep 3
1572 | done
1573 | unzip -q main.zip &>/dev/null && rm -f main.zip
1574 | fi
1575 |
1576 | cd simple-web-templates-main || echo "Не удалось перейти в папку с шаблонами"
1577 |
1578 | rm -rf assets ".gitattributes" "README.md" "_config.yml"
1579 |
1580 | RandomHTML=$(ls -d */ | shuf -n1) # Обновил для выбора случайного подкаталога
1581 | info " $(text 80) ${RandomHTML}"
1582 |
1583 | # Если шаблон существует, копируем его в /var/www/html
1584 | if [[ -d "${RandomHTML}" && -d "/var/www/html/" ]]; then
1585 | echo "Копируем шаблон в /var/www/html/..."
1586 | rm -rf /var/www/html/* # Очищаем старую папку
1587 | cp -a "${RandomHTML}/." /var/www/html/ || echo "Ошибка при копировании шаблона"
1588 | else
1589 | echo "Ошибка при извлечении шаблона!"
1590 | fi
1591 |
1592 | cd ~
1593 | }
1594 |
1595 | ###################################
1596 | ### Key generation
1597 | ###################################
1598 | generate_keys() {
1599 | # Генерация пары ключей X25519 с использованием xray
1600 | local KEY_PAIR=$(/usr/local/x-ui/bin/xray-linux-amd64 x25519)
1601 | local PRIVATE_KEY=$(echo "$KEY_PAIR" | grep "Private key:" | awk '{print $3}')
1602 | local PUBLIC_KEY=$(echo "$KEY_PAIR" | grep "Public key:" | awk '{print $3}')
1603 |
1604 | # Возвращаем ключи в виде строки, разделенной пробелом
1605 | echo "$PRIVATE_KEY $PUBLIC_KEY"
1606 | }
1607 |
1608 | ###################################
1609 | ### Settings reality (Steal Oneself)
1610 | ###################################
1611 | settings_steal() {
1612 | read PRIVATE_KEY0 PUBLIC_KEY0 <<< "$(generate_keys)"
1613 | STREAM_SETTINGS_STEAL=$(cat < /dev/null 2>&1
1783 |
1784 | settings_steal
1785 | settings_xtls
1786 | sniffing_inbounds
1787 | database_change
1788 |
1789 | x-ui stop
1790 |
1791 | rm -rf /etc/x-ui/x-ui.db.backup
1792 | [ -f /etc/x-ui/x-ui.db ] && mv /etc/x-ui/x-ui.db /etc/x-ui/x-ui.db.backup
1793 | mv x-ui.db /etc/x-ui/
1794 |
1795 | x-ui start
1796 | echo -e "20\n1" | x-ui > /dev/null 2>&1
1797 | tilda "$(text 10)"
1798 | }
1799 |
1800 | ###################################
1801 | ### Firewall
1802 | ###################################
1803 | enabling_security() {
1804 | info " $(text 47) "
1805 | BLOCK_ZONE_IP=$(echo ${IP4} | cut -d '.' -f 1-3).0/22
1806 |
1807 | case "$SYSTEM" in
1808 | Debian|Ubuntu )
1809 | ufw --force reset
1810 | ufw allow 22/tcp
1811 | ufw allow 443/tcp
1812 | ufw insert 1 deny from "$BLOCK_ZONE_IP"
1813 | ufw --force enable
1814 | ;;
1815 |
1816 | CentOS|Fedora )
1817 | systemctl enable --now firewalld
1818 | firewall-cmd --permanent --zone=public --add-port=22/tcp
1819 | firewall-cmd --permanent --zone=public --add-port=80/tcp
1820 | firewall-cmd --permanent --zone=public --add-port=443/tcp
1821 | firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='$BLOCK_ZONE_IP' reject"
1822 | firewall-cmd --reload
1823 | ;;
1824 | esac
1825 |
1826 | tilda "$(text 10)"
1827 | }
1828 |
1829 | ###################################
1830 | ### SSH
1831 | ###################################
1832 | ssh_setup() {
1833 | if [[ "${ANSWER_SSH,,}" == "y" ]]; then
1834 | info " $(text 48) "
1835 | sed -i -e "
1836 | s/#Port/Port/g;
1837 | s/Port 22/Port 22/g;
1838 | s/#PermitRootLogin/PermitRootLogin/g;
1839 | s/PermitRootLogin yes/PermitRootLogin prohibit-password/g;
1840 | s/#PubkeyAuthentication/PubkeyAuthentication/g;
1841 | s/PubkeyAuthentication no/PubkeyAuthentication yes/g;
1842 | s/#PasswordAuthentication/PasswordAuthentication/g;
1843 | s/PasswordAuthentication yes/PasswordAuthentication no/g;
1844 | s/#PermitEmptyPasswords/PermitEmptyPasswords/g;
1845 | s/PermitEmptyPasswords yes/PermitEmptyPasswords no/g;
1846 | " /etc/ssh/sshd_config
1847 |
1848 | # Настройка баннера
1849 | cat > /etc/motd </dev/null
18 | systemctl stop reverse_proxy_bot.service >/dev/null
19 | systemctl daemon-reload >/dev/null
20 |
21 | # Создание директории и виртуального окружения
22 | mkdir -p /usr/local/reverse_proxy/
23 | python3 -m venv /usr/local/reverse_proxy/reverse_proxy_env || { echo "Ошибка создания виртуального окружения"; exit 1; }
24 |
25 | # Путь к конфигурационному файлу
26 | CONFIG_FILE="/usr/local/reverse_proxy/reverse_proxy_bot_config.json"
27 |
28 | # Создаем или обновляем конфигурационный файл
29 | cat > $CONFIG_FILE < /etc/systemd/system/reverse_proxy_bot.service </dev/null && rm -f main.zip
37 | fi
38 |
39 | cd simple-web-templates-main || { msg_err "Не удалось перейти в папку с шаблонами"; exit 1; }
40 |
41 | msg_inf "Удаляем ненужные файлы..."
42 | rm -rf assets ".gitattributes" "README.md" "_config.yml"
43 |
44 | RandomHTML=$(ls -d */ | shuf -n1) # Обновил для выбора случайного подкаталога
45 | msg_inf "Random template name: ${RandomHTML}"
46 |
47 | # Если шаблон существует, копируем его в /var/www/html
48 | if [[ -d "${RandomHTML}" && -d "/var/www/html/" ]]; then
49 | msg_inf "Копируем шаблон в /var/www/html/..."
50 | rm -rf /var/www/html/* # Очищаем старую папку
51 | cp -a "${RandomHTML}/." /var/www/html/ || { msg_err "Ошибка при копировании шаблона"; exit 1; }
52 | msg_ok "Шаблон успешно извлечен и установлен!"
53 | else
54 | msg_err "Ошибка при извлечении шаблона!"
55 | exit 1
56 | fi
57 |
58 | cd ~ || { msg_err "Не удалось вернуться в домашнюю директорию"; exit 1; }
59 |
--------------------------------------------------------------------------------
/warp/xui-rp-warp.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Установка ключа и репозитория Cloudflare WARP
4 | # curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
5 | # echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(grep "VERSION_CODENAME=" /etc/os-release | cut -d "=" -f 2) main" | tee /etc/apt/sources.list.d/cloudflare-client.list
6 | # apt-get update && apt-get install cloudflare-warp -y
7 |
8 | export DEBIAN_FRONTEND=noninteractive
9 |
10 | mkdir -p /usr/local/xui-rp/
11 |
12 | echo "Попытка скачать пакет..."
13 | while ! wget --progress=dot:mega --timeout=30 --tries=10 --retry-connrefused "https://pkg.cloudflareclient.com/pool/$(grep "VERSION_CODENAME=" /etc/os-release | cut -d "=" -f 2)/main/c/cloudflare-warp/cloudflare-warp_2024.6.497-1_amd64.deb" -O /usr/local/xui-rp/cloudflare-warp_2024.6.497-1_amd64.deb; do
14 | echo "Не удалось скачать. Повторная попытка через 3 секунды..."
15 | sleep 3
16 | done
17 | echo "Скачивание завершено успешно."
18 |
19 | cd /usr/local/xui-rp/
20 | apt install -y ./cloudflare-warp_2024.6.497-1_amd64.deb
21 | rm -rf cloudflare-warp_*
22 | cd ~/
23 |
24 | # Создание директории для конфигурации
25 | mkdir -p /etc/systemd/system/warp-svc.service.d
26 |
27 | # Настройка уровня логирования
28 | cat > /etc/systemd/system/warp-svc.service.d/override.conf <