├── LICENSE ├── README.en.md ├── README.md ├── data ├── app-config.json ├── backup │ ├── backup_script.sh │ └── backup_script_tg.sh ├── caddy │ ├── caddyfile │ ├── caddyfile-node │ └── caddyfile-protection ├── docker │ ├── caddy-compose.yml │ ├── caddy-protection-compose.yml │ ├── node-compose.yml │ ├── panel-compose.yml │ ├── panel.env │ ├── subscription-compose.yml │ └── subscription-protection-compose.yml └── site │ ├── assets │ ├── main.js │ └── style.css │ └── index.html ├── install.sh ├── remnasetup.sh └── scripts ├── backups ├── auto_backup.sh ├── backup.sh └── restore.sh ├── common ├── colors.sh ├── functions.sh └── languages.sh ├── remnanode ├── install-bbr.sh ├── install-caddy.sh ├── install-full.sh ├── install-ipv6.sh ├── install-node.sh ├── install-warp.sh └── update.sh └── remnawave ├── install-caddy.sh ├── install-full.sh ├── install-panel.sh ├── install-subscription.sh ├── update-full.sh ├── update-panel.sh └── update-subscription.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Capybara 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. -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # RemnaSetup 🛠️ 2 | 3 |
4 | 5 | [English](README.en.md) | [Русский](README.md) 6 | 7 | ![RemnaSetup](https://img.shields.io/badge/RemnaSetup-2.5-blue) 8 | ![License](https://img.shields.io/badge/License-MIT-green) 9 | ![Platform](https://img.shields.io/badge/Platform-Ubuntu-orange) 10 | 11 | **Universal script for automatic installation, configuration, and updating of Remnawave and Remnanode infrastructure** 12 | 13 | [![Stars](https://img.shields.io/github/stars/Capybara-z/RemnaSetup?style=social)](https://github.com/Capybara-z/RemnaSetup) 14 | [![Forks](https://img.shields.io/github/forks/Capybara-z/RemnaSetup?style=social)](https://github.com/Capybara-z/RemnaSetup) 15 | 16 |
17 | 18 | --- 19 | 20 | ## 🚀 Features 21 | 22 |
23 | 24 | ### 🔥 Main Components 25 | 26 |
27 | 28 | 29 | 30 | 40 | 50 | 51 |
31 | 32 | ### 🎯 Remnawave 33 | - Installation and configuration of control panel 34 | - Installation of subscription page 35 | - Integration with Caddy for request proxying 36 | - Protection of panel and subscriptions 37 | - Automatic component updates 38 | 39 | 41 | 42 | ### 🌐 Remnanode 43 | - Installation and configuration of node 44 | - Integration with Caddy for self-steal 45 | - Network optimization through BBR 46 | - WARP-NATIVE (by distillium) integration 47 | - Automatic component updates 48 | 49 |
52 | 53 |
54 | 55 | --- 56 | 57 | ### 🗄️ Remnawave Backup/Restore 58 | 59 | - 💾 Create Remnawave backup 60 | - ♻️ Restore Remnawave from archive 61 | - 📂 Archives stored in /opt/backups 62 | - 📋 Backup types: manual, automatic, with Telegram sending 63 | - 🕒 Automatic backup with configurable schedule 64 | 65 | --- 66 | 67 |
68 | 69 | ### ⚡ Additional Features 70 | - **Modular structure** with separate scripts 71 | - **Interactive menu** with component selection 72 | - **Automatic updates** of all components 73 | - **Existing installation checks** before installation 74 | - **Reinstallation capability** with data preservation 75 | - **Enhanced error handling** and logging 76 | - **Remnawave backup and restore** through separate menu 77 | 78 | --- 79 | 80 | ## 📋 Menu Options 81 | 82 |
83 | 84 | ### 🎮 Interactive Menu 85 | 86 |
87 | 88 | 89 | 90 | 102 | 113 | 114 |
91 | 92 | ### 1️⃣ Remnawave 93 | - 📦 Full installation (Remnawave + Subscription Page + Caddy) 94 | - 🚀 Install Remnawave 95 | - 📄 Install Subscription Page 96 | - ⚙️ Install Caddy 97 | - 🔄 Update (Remnawave + Subscription Page) 98 | - 🔄 Update Remnawave 99 | - 🔄 Update Subscription Page 100 | 101 | 103 | 104 | ### 2️⃣ Remnanode 105 | - 📦 Full installation (Remnanode + Caddy + Tblocker + BBR + WARP) 106 | - 🚀 Install Remnanode 107 | - ⚙️ Install Caddy + self-steal 108 | - ⚡ Install BBR 109 | - 🌐 Install WARP-NATIVE (by distillium) 110 | - 🔄 Update Remnanode 111 | 112 |
115 | 116 |
117 | 118 | --- 119 | 120 | ### 3️⃣ Remnawave Backup/Restore 121 | 122 | - 💾 Create Remnawave backup 123 | - ♻️ Restore Remnawave from archive 124 | - 📂 Archives stored in /opt/backups 125 | - 🕒 Automatic backup with configurable schedule 126 | - 📤 Send backups to Telegram bot 127 | - 🗑️ Automatic cleanup of old backups 128 | - 🛡️ All actions through convenient menu 129 | 130 | --- 131 | 132 |
133 | 134 | --- 135 | 136 | ## 🖥️ Quick Start 137 | 138 | - Option 1 139 | ```bash 140 | bash <(curl -fsSL raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh) 141 | ``` 142 | - Option 2 143 | ```bash 144 | curl -fsSL https://raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh -o install.sh && chmod +x install.sh && sudo bash ./install.sh 145 | ``` 146 | 147 | --- 148 | 149 | ## 💡 How It Works 150 | 151 |
152 | 153 | ### 🔄 Installation Process 154 | 155 |
156 | 157 | 1. **🎯 Select option** in main menu 158 | 2. **📝 Enter data**: 159 | - 🌐 Domains for panel and subscriptions 160 | - 🔌 Ports for services 161 | - 🔑 Database credentials 162 | - 📊 Metrics settings 163 | - 🌐 WARP parameters 164 | 3. **🗄️ Backup and restore** 165 | 4. **⚡ Automation**: 166 | - ✅ Check existing installations 167 | - 📦 Install/update components 168 | - ⚙️ Configure settings 169 | - 🚀 Start services 170 | - 📋 View logs 171 | 172 | --- 173 | 174 | ## 🛡️ Security 175 | 176 |
177 | 178 | ### 🔒 Security Measures 179 | 180 |
181 | 182 | - 🔐 Use of sudo only for installation 183 | - 🔑 Manual entry of sensitive data 184 | - 🗑️ Temporary file cleanup 185 | - 📝 Secure configuration storage 186 | - 🔒 Access rights verification 187 | - 🛡️ Input data validation 188 | 189 | --- 190 | 191 | ## ⭐️ Project Support 192 | 193 |
194 | 195 | If the script was helpful — give it a ⭐️ on [GitHub](https://github.com/Capybara-z/RemnaSetup)! 196 | 197 | [![Star](https://img.shields.io/github/stars/Capybara-z/RemnaSetup?style=social)](https://github.com/Capybara-z/RemnaSetup) 198 | 199 | ### 📱 Contacts 200 | Telegram: [@KaTTuBaRa](https://t.me/KaTTuBaRa) 201 | 202 |
203 | 204 | --- 205 | 206 | ## 📄 License 207 | 208 | MIT 209 | 210 | --- 211 | 212 |
213 | 214 | **RemnaSetup** — your universal assistant for quick start and maintenance of Remnawave and RemnaNode infrastructure! 🚀 215 | 216 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RemnaSetup 🛠️ 2 | 3 |
4 | 5 | [English](README.en.md) | [Русский](README.md) 6 | 7 | ![RemnaSetup](https://img.shields.io/badge/RemnaSetup-2.5-blue) 8 | ![License](https://img.shields.io/badge/License-MIT-green) 9 | ![Platform](https://img.shields.io/badge/Platform-Ubuntu-orange) 10 | 11 | **Универсальный скрипт для автоматической установки, настройки и обновления инфраструктуры Remnawave и Remnanode** 12 | 13 | [![Stars](https://img.shields.io/github/stars/Capybara-z/RemnaSetup?style=social)](https://github.com/Capybara-z/RemnaSetup) 14 | [![Forks](https://img.shields.io/github/forks/Capybara-z/RemnaSetup?style=social)](https://github.com/Capybara-z/RemnaSetup) 15 | 16 |
17 | 18 | --- 19 | 20 | ## 🚀 Возможности 21 | 22 |
23 | 24 | ### 🔥 Основные компоненты 25 | 26 |
27 | 28 | 29 | 30 | 40 | 50 | 51 |
31 | 32 | ### 🎯 Remnawave 33 | - Установка и настройка панели управления 34 | - Установка страницы подписок 35 | - Интеграция с Caddy для проксирования запросов 36 | - Защита панели и подпсиок 37 | - Автоматическое обновление компонентов 38 | 39 | 41 | 42 | ### 🌐 Remnanode 43 | - Установка и настройка ноды 44 | - Интеграция с Caddy для self-steal 45 | - Оптимизация сети через BBR 46 | - Интеграция с WARP-NATIVE (by distillium) 47 | - Автоматическое обновление компонентов 48 | 49 |
52 | 53 |
54 | 55 | --- 56 | 57 | ### 🗄️ Бэкап/Восстановление Remnawave 58 | 59 | - 💾 Создание резервной копии Remnawave 60 | - ♻️ Восстановление Remnawave из архива 61 | - 📂 Архивы хранятся в /opt/backups 62 | - 📋 Типы бэкапов: ручной, автоматический, с отправкой в Telegram 63 | - 🕒 Автоматический бэкап с настраиваемым расписанием 64 | 65 | --- 66 | 67 |
68 | 69 | ### ⚡ Дополнительные возможности 70 | - **Модульная структура** с разделением на отдельные скрипты 71 | - **Интерактивное меню** с возможностью выбора компонентов 72 | - **Автоматическое обновление** всех компонентов 73 | - **Проверка существующих установок** перед установкой 74 | - **Возможность переустановки** с сохранением данных 75 | - **Улучшенная обработка ошибок** и логирование 76 | - **Бэкап и восстановление Remnawave** через отдельное меню 77 | 78 | --- 79 | 80 | ## 📋 Опции меню 81 | 82 |
83 | 84 | ### 🎮 Интерактивное меню 85 | 86 |
87 | 88 | 89 | 90 | 102 | 113 | 114 |
91 | 92 | ### 1️⃣ Remnawave 93 | - 📦 Полная установка (Remnawave + Страница подписок + Caddy) 94 | - 🚀 Установка Remnawave 95 | - 📄 Установка Страницы подписок 96 | - ⚙️ Установка Caddy 97 | - 🔄 Обновление (Remnawave + Страницы подписок) 98 | - 🔄 Обновление Remnawave 99 | - 🔄 Обновление Страницы подписок 100 | 101 | 103 | 104 | ### 2️⃣ Remnanode 105 | - 📦 Полная установка (Remnanode + Caddy + Tblocker + BBR + WARP-NATIVE (by distillium)) 106 | - 🚀 Установка Remnanode 107 | - ⚙️ Установка Caddy + self-steal 108 | - ⚡ Установка BBR 109 | - 🌐 Установка WARP-NATIVE (by distillium) 110 | - 🔄 Обновление Remnanode 111 | 112 |
115 | 116 |
117 | 118 | --- 119 | 120 | ### 3️⃣ Бэкап/Восстановление Remnawave 121 | 122 | - 💾 Создание резервной копии Remnawave 123 | - ♻️ Восстановление Remnawave из архива 124 | - 📂 Архивы хранятся в /opt/backups 125 | - 🕒 Автоматический бэкап с настраиваемым расписанием 126 | - 📤 Отправка бэкапов в Telegram бота 127 | - 🗑️ Автоматическая очистка старых бэкапов 128 | - 🛡️ Все действия через удобное меню 129 | 130 | --- 131 | 132 |
133 | 134 | --- 135 | 136 | ## 🖥️ Быстрый старт 137 | 138 | - Вариант 1 139 | ```bash 140 | bash <(curl -fsSL raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh) 141 | ``` 142 | - Вариант 2 143 | ```bash 144 | curl -fsSL https://raw.githubusercontent.com/Capybara-z/RemnaSetup/refs/heads/main/install.sh -o install.sh && chmod +x install.sh && sudo bash ./install.sh 145 | ``` 146 | 147 | --- 148 | 149 | ## 💡 Как это работает 150 | 151 |
152 | 153 | ### 🔄 Процесс установки 154 | 155 |
156 | 157 | 1. **🎯 Выбор опции** в главном меню 158 | 2. **📝 Ввод данных**: 159 | - 🌐 Домены для панели и подписок 160 | - 🔌 Порты для сервисов 161 | - 🔑 Учетные данные для базы данных 162 | - 📊 Настройки метрик 163 | - 🌐 Параметры WARP 164 | 3. **🗄️ Бэкап и восстановление** 165 | 4. **⚡ Автоматизация**: 166 | - ✅ Проверка существующих установок 167 | - 📦 Установка/обновление компонентов 168 | - ⚙️ Настройка конфигураций 169 | - 🚀 Запуск сервисов 170 | - 📋 Просмотр логов 171 | 172 | --- 173 | 174 | ## 🛡️ Безопасность 175 | 176 |
177 | 178 | ### 🔒 Меры безопасности 179 | 180 |
181 | 182 | - 🔐 Использование sudo только для установки 183 | - 🔑 Ручной ввод чувствительных данных 184 | - 🗑️ Удаление временных файлов 185 | - 📝 Защищенное хранение конфигураций 186 | - 🔒 Проверка прав доступа 187 | - 🛡️ Валидация вводимых данных 188 | 189 | --- 190 | 191 | ## ⭐️ Поддержка проекта 192 | 193 |
194 | 195 | Если скрипт был полезен — поставьте ⭐️ на [GitHub](https://github.com/Capybara-z/RemnaSetup)! 196 | 197 | [![Star](https://img.shields.io/github/stars/Capybara-z/RemnaSetup?style=social)](https://github.com/Capybara-z/RemnaSetup) 198 | 199 | ### 📱 Контакты 200 | Telegram: [@KaTTuBaRa](https://t.me/KaTTuBaRa) 201 | 202 |
203 | 204 | --- 205 | 206 | ## 📄 Лицензия 207 | 208 | MIT 209 | 210 | --- 211 | 212 |
213 | 214 | **RemnaSetup** — ваш универсальный помощник для быстрого старта и поддержки инфраструктуры Remnawave и RemnaNode! 🚀 215 | 216 |
-------------------------------------------------------------------------------- /data/app-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "additionalLocales": [ 4 | "ru" 5 | ] 6 | }, 7 | "platforms": { 8 | "android": [ 9 | { 10 | "id": "happ", 11 | "name": "Happ", 12 | "isFeatured": true, 13 | "urlScheme": "happ://add/", 14 | "installationStep": { 15 | "buttons": [ 16 | { 17 | "buttonLink": "https://play.google.com/store/apps/details?id=com.happproxy", 18 | "buttonText": { 19 | "en": "Open in Google Play", 20 | "ru": "Открыть в Google Play" 21 | } 22 | }, 23 | { 24 | "buttonLink": "https://github.com/Happ-proxy/happ-android/releases/latest/download/Happ.apk", 25 | "buttonText": { 26 | "en": "Download APK", 27 | "ru": "Скачать APK" 28 | } 29 | } 30 | ], 31 | "description": { 32 | "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.", 33 | "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает." 34 | } 35 | }, 36 | "addSubscriptionStep": { 37 | "description": { 38 | "en": "Click the button below to add subscription", 39 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку" 40 | } 41 | }, 42 | "connectAndUseStep": { 43 | "description": { 44 | "en": "Open the app and connect to the server", 45 | "ru": "Откройте приложение и подключитесь к серверу" 46 | } 47 | } 48 | }, 49 | { 50 | "id": "clash-meta", 51 | "name": "Clash Meta", 52 | "isFeatured": false, 53 | "urlScheme": "clash://install-config?url=", 54 | "installationStep": { 55 | "buttons": [ 56 | { 57 | "buttonLink": "https://github.com/MetaCubeX/ClashMetaForAndroid/releases/download/v2.11.7/cmfa-2.11.7-meta-universal-release.apk", 58 | "buttonText": { 59 | "en": "Download APK", 60 | "ru": "Скачать APK" 61 | } 62 | }, 63 | { 64 | "buttonLink": "https://f-droid.org/packages/com.github.metacubex.clash.meta/", 65 | "buttonText": { 66 | "en": "Open in F-Droid", 67 | "ru": "Открыть в F-Droid" 68 | } 69 | } 70 | ], 71 | "description": { 72 | "en": "Download and install Clash Meta APK", 73 | "ru": "Скачайте и установите Clash Meta APK" 74 | } 75 | }, 76 | "addSubscriptionStep": { 77 | "description": { 78 | "en": "Tap the button to import configuration", 79 | "ru": "Нажмите кнопку, чтобы импортировать конфигурацию" 80 | } 81 | }, 82 | "connectAndUseStep": { 83 | "description": { 84 | "en": "Open Clash Meta and tap on Connect", 85 | "ru": "Откройте Clash Meta и нажмите Подключиться" 86 | } 87 | } 88 | } 89 | ], 90 | "ios": [ 91 | { 92 | "id": "happ", 93 | "name": "Happ", 94 | "isFeatured": true, 95 | "urlScheme": "happ://add/", 96 | "installationStep": { 97 | "buttons": [ 98 | { 99 | "buttonLink": "https://apps.apple.com/us/app/happ-proxy-utility/id6504287215", 100 | "buttonText": { 101 | "en": "Open in App Store [EU]", 102 | "ru": "Открыть в App Store [EU]" 103 | } 104 | }, 105 | { 106 | "buttonLink": "https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973", 107 | "buttonText": { 108 | "en": "Open in App Store [RU]", 109 | "ru": "Открыть в App Store [RU]" 110 | } 111 | } 112 | ], 113 | "description": { 114 | "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.", 115 | "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль." 116 | } 117 | }, 118 | "addSubscriptionStep": { 119 | "description": { 120 | "en": "Click the button below — the app will open and the subscription will be added automatically", 121 | "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически." 122 | } 123 | }, 124 | "connectAndUseStep": { 125 | "description": { 126 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.", 127 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов." 128 | } 129 | } 130 | }, 131 | { 132 | "id": "streisand", 133 | "name": "Streisand", 134 | "isFeatured": false, 135 | "urlScheme": "streisand://import/", 136 | "installationStep": { 137 | "buttons": [ 138 | { 139 | "buttonLink": "https://apps.apple.com/ru/app/streisand/id6450534064", 140 | "buttonText": { 141 | "en": "Open in App Store", 142 | "ru": "Открыть в App Store" 143 | } 144 | } 145 | ], 146 | "description": { 147 | "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.", 148 | "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль." 149 | } 150 | }, 151 | "addSubscriptionStep": { 152 | "description": { 153 | "en": "Click the button below — the app will open and the subscription will be added automatically", 154 | "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически." 155 | } 156 | }, 157 | "connectAndUseStep": { 158 | "description": { 159 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.", 160 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов." 161 | } 162 | } 163 | } 164 | ], 165 | "linux": [ 166 | { 167 | "id": "koala-clash", 168 | "name": "Koala Clash", 169 | "isFeatured": false, 170 | "urlScheme": "koala-clash://install-config?url=", 171 | "installationStep": { 172 | "buttons": [ 173 | { 174 | "buttonLink": "https://github.com/coolcoala/clash-verge-rev-lite/releases", 175 | "buttonText": { 176 | "en": "Linux", 177 | "ru": "Linux" 178 | } 179 | } 180 | ], 181 | "description": { 182 | "en": "Choose the version for your device, click the button below and install the app.", 183 | "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение." 184 | } 185 | }, 186 | "addSubscriptionStep": { 187 | "description": { 188 | "en": "Click the button below to add subscription", 189 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку" 190 | } 191 | }, 192 | "additionalAfterAddSubscriptionStep": { 193 | "buttons": [], 194 | "title": { 195 | "en": "If the subscription is not added", 196 | "ru": "Если подписка не добавилась" 197 | }, 198 | "description": { 199 | "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the Profiles section and paste the link in the text field, then click the Import button.", 200 | "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт." 201 | } 202 | }, 203 | "connectAndUseStep": { 204 | "description": { 205 | "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.", 206 | "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ." 207 | } 208 | } 209 | } 210 | ], 211 | "macos": [ 212 | { 213 | "id": "happ", 214 | "name": "Happ", 215 | "isFeatured": true, 216 | "urlScheme": "happ://add/", 217 | "installationStep": { 218 | "buttons": [ 219 | { 220 | "buttonLink": "https://apps.apple.com/us/app/happ-proxy-utility/id6504287215", 221 | "buttonText": { 222 | "en": "Open in App Store [EU]", 223 | "ru": "Открыть в App Store [EU]" 224 | } 225 | }, 226 | { 227 | "buttonLink": "https://apps.apple.com/ru/app/happ-proxy-utility-plus/id6746188973", 228 | "buttonText": { 229 | "en": "Open in App Store [RU]", 230 | "ru": "Открыть в App Store [RU]" 231 | } 232 | } 233 | ], 234 | "description": { 235 | "en": "Open the page in App Store and install the app. Launch it, in the VPN configuration permission window click Allow and enter your passcode.", 236 | "ru": "Откройте страницу в App Store и установите приложение. Запустите его, в окне разрешения VPN-конфигурации нажмите Allow и введите свой пароль." 237 | } 238 | }, 239 | "addSubscriptionStep": { 240 | "description": { 241 | "en": "Click the button below — the app will open and the subscription will be added automatically", 242 | "ru": "Нажмите кнопку ниже — приложение откроется, и подписка добавится автоматически." 243 | } 244 | }, 245 | "connectAndUseStep": { 246 | "description": { 247 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, choose another server from the server list.", 248 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов." 249 | } 250 | } 251 | } 252 | ], 253 | "windows": [ 254 | { 255 | "id": "happ", 256 | "name": "Happ", 257 | "isFeatured": true, 258 | "urlScheme": "happ://add/", 259 | "installationStep": { 260 | "buttons": [ 261 | { 262 | "buttonLink": "https://github.com/Happ-proxy/happ-desktop/releases/latest/download/setup-Happ.x86.exe", 263 | "buttonText": { 264 | "en": "Windows", 265 | "ru": "Windows" 266 | } 267 | } 268 | ], 269 | "description": { 270 | "en": "Click the button below and install the application.", 271 | "ru": "Нажмите на кнопку ниже и установите приложение." 272 | } 273 | }, 274 | "addSubscriptionStep": { 275 | "description": { 276 | "en": "Click the button below to add subscription", 277 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку" 278 | } 279 | }, 280 | "connectAndUseStep": { 281 | "description": { 282 | "en": "Select the desired country and click on the connection circle on the right.", 283 | "ru": "Выберите нужную страну и нажмите справа на круг подключения." 284 | } 285 | }, 286 | "additionalAfterAddSubscriptionStep": { 287 | "title": { 288 | "en": "If the subscription is not added", 289 | "ru": "Если подписка не добавилась" 290 | }, 291 | "description": { 292 | "en": "\nIf nothing happens after clicking the button, add a subscription manually. Click on this page the Get link button in the upper right corner, copy the link. In the application, paste the link if asked, or click \"From buffer\" at the bottom left", 293 | "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В приложении вставьте ссылку если просит, или слева внизу нажать \"Из буфера\"" 294 | }, 295 | "buttons": [] 296 | } 297 | }, 298 | { 299 | "id": "koala-clash", 300 | "name": "Koala Clash", 301 | "isFeatured": false, 302 | "urlScheme": "koala-clash://install-config?url=", 303 | "installationStep": { 304 | "buttons": [ 305 | { 306 | "buttonLink": "https://github.com/coolcoala/clash-verge-rev-lite/releases/download/v0.2.6/Koala.Clash_x64-setup.exe", 307 | "buttonText": { 308 | "en": "Windows", 309 | "ru": "Windows" 310 | } 311 | } 312 | ], 313 | "description": { 314 | "en": "Choose the version for your device, click the button below and install the app.", 315 | "ru": "Выберите подходящую версию для вашего устройства, нажмите на кнопку ниже и установите приложение." 316 | } 317 | }, 318 | "addSubscriptionStep": { 319 | "description": { 320 | "en": "Click the button below to add subscription", 321 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку" 322 | } 323 | }, 324 | "additionalAfterAddSubscriptionStep": { 325 | "buttons": [], 326 | "title": { 327 | "en": "If the subscription is not added", 328 | "ru": "Если подписка не добавилась" 329 | }, 330 | "description": { 331 | "en": "If nothing happens after clicking the button, add the subscription manually. Click the Get Link button in the top right corner of this page, copy the link. In Koala Clash, go to the Profiles section and paste the link in the text field, then click the Import button.", 332 | "ru": "Если после нажатия на кнопку ничего не произошло, добавьте подписку вручную. Нажмите на этой страницу кнопку Получить ссылку в правом верхнем углу, скопируйте ссылку. В Koala Clash перейдите в раздел Профили и вставьте ссылку в текстовое поле, затем нажмите на кнопку Импорт." 333 | } 334 | }, 335 | "connectAndUseStep": { 336 | "description": { 337 | "en": "You can select a server in the Proxy section, and enable VPN in the Settings section. Set the TUN Mode switch to ON.", 338 | "ru": "Выбрать сервер можно в разделе Прокси, включить VPN можно в разделе Настройки. Установите переключатель TUN Mode в положение ВКЛ." 339 | } 340 | } 341 | }, 342 | { 343 | "id": "hiddify", 344 | "name": "Hiddify", 345 | "isFeatured": false, 346 | "urlScheme": "hiddify://import/", 347 | "installationStep": { 348 | "buttons": [ 349 | { 350 | "buttonLink": "https://github.com/hiddify/hiddify-app/releases/download/v2.5.7/Hiddify-Windows-Setup-x64.exe", 351 | "buttonText": { 352 | "en": "Windows", 353 | "ru": "Windows" 354 | } 355 | } 356 | ], 357 | "description": { 358 | "en": "In the main section, click the large power button in the center to connect to VPN. If needed, select a different server in the Proxy section", 359 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. При необходимости выберите другой сервер в разделе Прокси." 360 | } 361 | }, 362 | "addSubscriptionStep": { 363 | "description": { 364 | "en": "Click the button below to add subscription", 365 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку" 366 | } 367 | }, 368 | "connectAndUseStep": { 369 | "description": { 370 | "en": "In the main section, click the large power button in the center to connect to VPN. Don't forget to select a server from the server list. If needed, select a different server from the server list.", 371 | "ru": "В главном разделе нажмите большую кнопку включения в центре для подключения к VPN. Не забудьте выбрать сервер в списке серверов. При необходимости выберите другой сервер из списка серверов." 372 | } 373 | } 374 | } 375 | ], 376 | "androidTV": [ 377 | { 378 | "id": "happ", 379 | "name": "Happ", 380 | "isFeatured": true, 381 | "urlScheme": "happ://add/", 382 | "installationStep": { 383 | "buttons": [ 384 | { 385 | "buttonLink": "https://play.google.com/store/apps/details?id=com.happproxy", 386 | "buttonText": { 387 | "en": "Open in Google Play", 388 | "ru": "Открыть в Google Play" 389 | } 390 | }, 391 | { 392 | "buttonLink": "https://github.com/Happ-proxy/happ-android/releases/latest/download/Happ.apk", 393 | "buttonText": { 394 | "en": "Download APK", 395 | "ru": "Скачать APK" 396 | } 397 | } 398 | ], 399 | "description": { 400 | "en": "Open the page in Google Play and install the app. Or install the app directly from the APK file if Google Play is not working.", 401 | "ru": "Откройте страницу в Google Play и установите приложение. Или установите приложение из APK файла напрямую, если Google Play не работает." 402 | } 403 | }, 404 | "additionalBeforeAddSubscriptionStep": { 405 | "buttons": [ 406 | { 407 | "buttonLink": "https://www.happ.su/main/ru/faq/android-tv", 408 | "buttonText": { 409 | "en": "In Russian", 410 | "ru": "На русском" 411 | } 412 | }, 413 | { 414 | "buttonLink": "https://www.happ.su/main/faq/android-tv", 415 | "buttonText": { 416 | "en": "In English", 417 | "ru": "На английском" 418 | } 419 | } 420 | ], 421 | "description": { 422 | "en": "Detailed instructions to help you set up Happ on your device.", 423 | "ru": "Подробные инструкции, чтобы помочь вам настроить Happ на вашем устройстве." 424 | }, 425 | "title": { 426 | "en": "Installation instructions", 427 | "ru": "Инструкции по установке" 428 | } 429 | }, 430 | "addSubscriptionStep": { 431 | "description": { 432 | "en": "Click the button below to add subscription, if you opened the subscription page on your TV", 433 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре" 434 | } 435 | }, 436 | "connectAndUseStep": { 437 | "description": { 438 | "en": "Open the app and connect to the server", 439 | "ru": "Откройте приложение и подключитесь к серверу" 440 | } 441 | } 442 | } 443 | ], 444 | "appleTV": [ 445 | { 446 | "id": "happ", 447 | "name": "Happ", 448 | "isFeatured": true, 449 | "urlScheme": "happ://add/", 450 | "installationStep": { 451 | "buttons": [ 452 | { 453 | "buttonLink": "https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274", 454 | "buttonText": { 455 | "en": "App Store", 456 | "ru": "App Store" 457 | } 458 | } 459 | ], 460 | "description": { 461 | "en": "Open the page in the App Store on your Apple TV and install the app. Launch it, allow VPN configuration if prompted, and enter your passcode if required.", 462 | "ru": "Откройте страницу в App Store на Apple TV и установите приложение. Запустите его, предоставьте разрешение на VPN-конфигурацию, если потребуется, и введите свой пароль." 463 | } 464 | }, 465 | "additionalBeforeAddSubscriptionStep": { 466 | "buttons": [ 467 | { 468 | "buttonLink": "https://www.happ.su/main/ru/faq/android-tv", 469 | "buttonText": { 470 | "en": "In Russian", 471 | "ru": "На русском" 472 | } 473 | }, 474 | { 475 | "buttonLink": "https://www.happ.su/main/faq/android-tv", 476 | "buttonText": { 477 | "en": "In English", 478 | "ru": "На английском" 479 | } 480 | } 481 | ], 482 | "description": { 483 | "en": "Detailed instructions to help you set up Happ on your device.", 484 | "ru": "Подробные инструкции, чтобы помочь вам настроить Happ на вашем устройстве." 485 | }, 486 | "title": { 487 | "en": "Installation instructions", 488 | "ru": "Инструкции по установке" 489 | } 490 | }, 491 | "addSubscriptionStep": { 492 | "description": { 493 | "en": "Click the button below to add subscription, if you opened the subscription page on your TV", 494 | "ru": "Нажмите кнопку ниже, чтобы добавить подписку, если вы открыли страницу подписки на телевизоре" 495 | } 496 | }, 497 | "connectAndUseStep": { 498 | "description": { 499 | "en": "Open the app and connect to the server", 500 | "ru": "Откройте приложение и подключитесь к серверу" 501 | } 502 | } 503 | } 504 | ] 505 | } 506 | } 507 | -------------------------------------------------------------------------------- /data/backup/backup_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BACKUP_DIR="/opt/backups" 4 | DATE=$(date +%F_%H-%M-%S) 5 | DB_VOLUME="remnawave-db-data" 6 | REMWAVE_DIR="/opt/remnawave" 7 | DB_TAR="remnawave-db-backup-$DATE.tar.gz" 8 | FINAL_ARCHIVE="remnawave-backup-$DATE.7z" 9 | TMP_DIR="$BACKUP_DIR/tmp-$DATE" 10 | 11 | PASSWORD="" 12 | 13 | mkdir -p "$BACKUP_DIR" 14 | mkdir -p "$TMP_DIR" 15 | 16 | if docker volume inspect $DB_VOLUME &>/dev/null; then 17 | docker run --rm \ 18 | -v ${DB_VOLUME}:/volume \ 19 | -v "$TMP_DIR":/backup \ 20 | alpine \ 21 | tar czf /backup/$DB_TAR -C /volume . 22 | fi 23 | 24 | cp "$REMWAVE_DIR/.env" "$TMP_DIR/" 25 | cp "$REMWAVE_DIR/docker-compose.yml" "$TMP_DIR/" 26 | 27 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$PASSWORD" "$BACKUP_DIR/$FINAL_ARCHIVE" "$TMP_DIR/*" >/dev/null 2>&1 28 | 29 | rm -rf "$TMP_DIR" 30 | 31 | find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' -mtime +3 -delete 32 | 33 | exit 0 -------------------------------------------------------------------------------- /data/backup/backup_script_tg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BACKUP_DIR="/opt/backups" 4 | DATE=$(date +%F_%H-%M-%S) 5 | DB_VOLUME="remnawave-db-data" 6 | REMWAVE_DIR="/opt/remnawave" 7 | DB_TAR="remnawave-db-backup-$DATE.tar.gz" 8 | FINAL_ARCHIVE="remnawave-backup-$DATE.7z" 9 | TMP_DIR="$BACKUP_DIR/tmp-$DATE" 10 | 11 | BOT_TOKEN="" 12 | CHAT_ID="" 13 | PASSWORD="" 14 | LANGUAGE="" 15 | 16 | START_TIME=$(date +%s) 17 | 18 | mkdir -p "$BACKUP_DIR" 19 | mkdir -p "$TMP_DIR" 20 | 21 | if docker volume inspect $DB_VOLUME &>/dev/null; then 22 | docker run --rm \ 23 | -v ${DB_VOLUME}:/volume \ 24 | -v "$TMP_DIR":/backup \ 25 | alpine \ 26 | tar czf /backup/$DB_TAR -C /volume . 27 | fi 28 | 29 | cp "$REMWAVE_DIR/.env" "$TMP_DIR/" 30 | cp "$REMWAVE_DIR/docker-compose.yml" "$TMP_DIR/" 31 | 32 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$PASSWORD" "$BACKUP_DIR/$FINAL_ARCHIVE" "$TMP_DIR/*" >/dev/null 2>&1 33 | 34 | rm -rf "$TMP_DIR" 35 | 36 | find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' -mtime +3 -delete 37 | 38 | END_TIME=$(date +%s) 39 | DURATION_SEC=$((END_TIME - START_TIME)) 40 | DURATION=$(date -u -d @${DURATION_SEC} +%H:%M:%S) 41 | ARCHIVE_SIZE=$(du -h "$BACKUP_DIR/$FINAL_ARCHIVE" | awk '{print $1}') 42 | FREE_SPACE=$(df -h "$BACKUP_DIR" | awk 'NR==2{print $4}') 43 | 44 | if [ "$LANGUAGE" = "en" ]; then 45 | MSG=$(cat <:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.break-all{word-break:break-all}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-slate-800\/30{border-color:#1e293b4d}.border-slate-800\/40{border-color:#1e293b66}.border-slate-800\/50{border-color:#1e293b80}.border-yellow-500\/10{border-color:#eab3081a}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-red-500\/10{background-color:#ef44441a}.bg-slate-900\/40{background-color:#0f172a66}.bg-slate-950\/30{background-color:#0206174d}.bg-slate-950\/60{background-color:#02061799}.bg-\[url\(\'https\:\/\/images\.unsplash\.com\/photo-1528818955841-a7f1425131b5\?auto\=format\&fit\=crop\&q\=80\'\)\]{background-image:url(https://images.unsplash.com/photo-1528818955841-a7f1425131b5?auto=format&fit=crop&q=80)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-slate-950{--tw-gradient-from: #020617 var(--tw-gradient-from-position);--tw-gradient-to: rgb(2 6 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-yellow-500\/5{--tw-gradient-from: rgb(234 179 8 / .05) var(--tw-gradient-from-position);--tw-gradient-to: rgb(234 179 8 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-slate-900{--tw-gradient-to: rgb(15 23 42 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #0f172a var(--tw-gradient-via-position), var(--tw-gradient-to)}.via-yellow-500\/10{--tw-gradient-to: rgb(234 179 8 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(234 179 8 / .1) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-slate-800{--tw-gradient-to: #1e293b var(--tw-gradient-to-position)}.to-yellow-500\/5{--tw-gradient-to: rgb(234 179 8 / .05) var(--tw-gradient-to-position)}.bg-cover{background-size:cover}.bg-center{background-position:center}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity))}.text-emerald-300{--tw-text-opacity: 1;color:rgb(110 231 183 / var(--tw-text-opacity))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-orange-500{--tw-text-opacity: 1;color:rgb(249 115 22 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity))}.opacity-\[0\.03\]{opacity:.03}.mix-blend-overlay{mix-blend-mode:overlay}.blur-3xl{--tw-blur: blur(64px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur{--tw-backdrop-blur: blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}} 2 | -------------------------------------------------------------------------------- /data/site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 503 - Критическая нагрузка системы 8 | 9 | 10 | 11 | 12 |
13 | 14 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | echo "This script must be run as root" 5 | echo "Этот скрипт должен быть запущен с правами root" 6 | exit 1 7 | fi 8 | 9 | TEMP_DIR=$(mktemp -d) 10 | 11 | if [ -d "/opt/remnasetup" ]; then 12 | echo "Removing existing RemnaSetup installation..." 13 | echo "Удаление существующей установки RemnaSetup..." 14 | rm -rf /opt/remnasetup 15 | fi 16 | 17 | if ! command -v curl &> /dev/null; then 18 | echo "Installing curl..." 19 | echo "Установка curl..." 20 | if command -v apt-get &> /dev/null; then 21 | apt update -y && apt install -y curl 22 | elif command -v yum &> /dev/null; then 23 | yum install -y curl 24 | elif command -v dnf &> /dev/null; then 25 | dnf install -y curl 26 | else 27 | echo "Failed to install curl. Please install it manually." 28 | echo "Не удалось установить curl. Пожалуйста, установите его вручную." 29 | exit 1 30 | fi 31 | fi 32 | 33 | cd "$TEMP_DIR" || exit 1 34 | 35 | echo "Downloading RemnaSetup..." 36 | echo "Загрузка RemnaSetup..." 37 | curl -L https://github.com/Capybara-z/RemnaSetup/archive/refs/heads/main.zip -o remnasetup.zip 38 | 39 | if [ ! -f remnasetup.zip ]; then 40 | echo "Error: Failed to download archive" 41 | echo "Ошибка: Не удалось загрузить архив" 42 | rm -rf "$TEMP_DIR" 43 | exit 1 44 | fi 45 | 46 | if ! command -v unzip &> /dev/null; then 47 | echo "Installing unzip..." 48 | echo "Установка unzip..." 49 | if command -v apt-get &> /dev/null; then 50 | echo "Updating package list..." 51 | echo "Обновление списка пакетов..." 52 | sudo apt update -y && sudo apt install -y unzip 53 | elif command -v yum &> /dev/null; then 54 | sudo yum install -y unzip 55 | elif command -v dnf &> /dev/null; then 56 | sudo dnf install -y unzip 57 | else 58 | echo "Failed to install unzip. Please install it manually." 59 | echo "Не удалось установить unzip. Пожалуйста, установите его вручную." 60 | rm -rf "$TEMP_DIR" 61 | exit 1 62 | fi 63 | fi 64 | 65 | echo "Extracting files..." 66 | echo "Распаковка файлов..." 67 | unzip -q remnasetup.zip 68 | 69 | if [ ! -d "RemnaSetup-main" ]; then 70 | echo "Error: Failed to extract archive" 71 | echo "Ошибка: Не удалось распаковать архив" 72 | rm -rf "$TEMP_DIR" 73 | exit 1 74 | fi 75 | 76 | mkdir -p /opt/remnasetup 77 | 78 | echo "Installing RemnaSetup to /opt/remnasetup..." 79 | echo "Установка RemnaSetup в /opt/remnasetup..." 80 | cp -r RemnaSetup-main/* /opt/remnasetup/ 81 | 82 | if [ ! -f "/opt/remnasetup/remnasetup.sh" ]; then 83 | echo "Error: Failed to copy files" 84 | echo "Ошибка: Не удалось скопировать файлы" 85 | rm -rf "$TEMP_DIR" 86 | exit 1 87 | fi 88 | 89 | echo "Setting permissions..." 90 | echo "Установка прав доступа..." 91 | chown -R $SUDO_USER:$SUDO_USER /opt/remnasetup 92 | chmod -R 755 /opt/remnasetup 93 | chmod +x /opt/remnasetup/remnasetup.sh 94 | chmod +x /opt/remnasetup/scripts/common/*.sh 95 | chmod +x /opt/remnasetup/scripts/remnawave/*.sh 96 | chmod +x /opt/remnasetup/scripts/remnanode/*.sh 97 | chmod +x /opt/remnasetup/scripts/backups/*.sh 98 | 99 | rm -rf "$TEMP_DIR" 100 | 101 | cd /opt/remnasetup || exit 1 102 | 103 | echo "Starting RemnaSetup..." 104 | echo "Запуск RemnaSetup..." 105 | bash /opt/remnasetup/remnasetup.sh 106 | -------------------------------------------------------------------------------- /remnasetup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="/opt/remnasetup" 4 | 5 | . /opt/remnasetup/scripts/common/colors.sh 6 | . /opt/remnasetup/scripts/common/functions.sh 7 | . /opt/remnasetup/scripts/common/languages.sh 8 | 9 | menu() { 10 | echo -e "${BOLD_MAGENTA}$1${RESET}" 11 | } 12 | 13 | question() { 14 | echo -e "${BOLD_CYAN}$1${RESET}" 15 | } 16 | 17 | info() { 18 | echo -e "${BOLD_CYAN}[INFO]${RESET} $1" 19 | } 20 | 21 | warn() { 22 | echo -e "${BOLD_YELLOW}[WARN]${RESET} $1" 23 | } 24 | 25 | error() { 26 | echo -e "${BOLD_RED}[ERROR]${RESET} $1" 27 | } 28 | 29 | success() { 30 | echo -e "${BOLD_GREEN}[SUCCESS]${RESET} $1" 31 | } 32 | 33 | print_header() { 34 | clear 35 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 36 | echo -e "\033[1;32m" 37 | echo -e "┌───────────────────────────────────────────────────────────────────┐" 38 | echo -e "│ ██████╗ █████╗ ██████╗ ██╗ ██╗██████╗ █████╗ ██████╗ █████╗ │" 39 | echo -e "│ ██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗ │" 40 | echo -e "│ ██║ ███████║██████╔╝ ╚████╔╝ ██████╔╝███████║██████╔╝███████║ │" 41 | echo -e "│ ██║ ██╔══██║██╔═══╝ ╚██╔╝ ██╔══██╗██╔══██║██╔══██╗██╔══██║ │" 42 | echo -e "│ ╚██████╗██║ ██║██║ ██║ ██████╔╝██║ ██║██║ ██║██║ ██║ │" 43 | echo -e "│ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ │" 44 | echo -e "└───────────────────────────────────────────────────────────────────┘" 45 | echo -e "\033[0m" 46 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 47 | if [ "$LANGUAGE" = "en" ]; then 48 | echo -e "${GREEN}RemnaSetup by capybara${RESET}" 49 | echo -e "${CYAN}Project: https://github.com/Capybara-z/RemnaSetup${RESET}" 50 | echo -e "${YELLOW}Contacts: @KaTTuBaRa${RESET}" 51 | echo -e "${CYAN}Version: 2.5${RESET}" 52 | echo 53 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 54 | echo -e "${YELLOW}Made with support from:${RESET}" 55 | echo -e "${CYAN}GitHub SoloBot: https://github.com/Vladless/Solo_bot${RESET}" 56 | echo -e "${YELLOW}Contacts: @Vladless${RESET}" 57 | else 58 | echo -e "${GREEN}RemnaSetup by capybara${RESET}" 59 | echo -e "${CYAN}Проект: https://github.com/Capybara-z/RemnaSetup${RESET}" 60 | echo -e "${YELLOW}Контакты: @KaTTuBaRa${RESET}" 61 | echo -e "${CYAN}Версия: 2.5${RESET}" 62 | echo 63 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 64 | echo -e "${YELLOW}Сделано при поддержке проекта:${RESET}" 65 | echo -e "${CYAN}GitHub SoloBot: https://github.com/Vladless/Solo_bot${RESET}" 66 | echo -e "${YELLOW}Контакты: @Vladless${RESET}" 67 | fi 68 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 69 | echo 70 | } 71 | 72 | display_remnawave_menu() { 73 | clear 74 | print_header 75 | menu "$(get_string "remnawave_menu")" 76 | if [ "$LANGUAGE" = "en" ]; then 77 | echo -e "${BLUE}1. Full installation (Remnawave + Subscription Page + Caddy)${RESET}" 78 | echo -e "${BLUE}2. Install Remnawave${RESET}" 79 | echo -e "${BLUE}3. Install Subscription Page${RESET}" 80 | echo -e "${BLUE}4. Install Caddy${RESET}" 81 | echo -e "${BLUE}5. Update (Remnawave + Subscription Page)${RESET}" 82 | echo -e "${BLUE}6. Update Remnawave${RESET}" 83 | echo -e "${BLUE}7. Update Subscription Page${RESET}" 84 | echo -e "${BLUE}8. Back${RESET}" 85 | else 86 | echo -e "${BLUE}1. Полная установка (Remnawave + Страница подписок + Caddy)${RESET}" 87 | echo -e "${BLUE}2. Установка Remnawave${RESET}" 88 | echo -e "${BLUE}3. Установка Страницы подписок${RESET}" 89 | echo -e "${BLUE}4. Установка Caddy${RESET}" 90 | echo -e "${BLUE}5. Обновление (Remnawave + Страницы подписок)${RESET}" 91 | echo -e "${BLUE}6. Обновление Remnawave${RESET}" 92 | echo -e "${BLUE}7. Обновление Страницы подписок${RESET}" 93 | echo -e "${BLUE}8. Назад${RESET}" 94 | fi 95 | echo 96 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " REMNAWAVE_OPTION 97 | echo 98 | } 99 | 100 | display_remnanode_menu() { 101 | clear 102 | print_header 103 | menu "$(get_string "remnanode_menu")" 104 | if [ "$LANGUAGE" = "en" ]; then 105 | echo -e "${BLUE}1. Full installation (Remnanode + Caddy + BBR + WARP-NATIVE (by distillium))${RESET}" 106 | echo -e "${BLUE}2. Install Remnanode only${RESET}" 107 | echo -e "${BLUE}3. Install Caddy + self-steal only${RESET}" 108 | echo -e "${BLUE}4. IPv6 Management${RESET}" 109 | echo -e "${BLUE}5. Install BBR only${RESET}" 110 | echo -e "${BLUE}6. Install WARP-NATIVE (by distillium)${RESET}" 111 | echo -e "${BLUE}7. Update Remnanode${RESET}" 112 | echo -e "${BLUE}8. Back${RESET}" 113 | else 114 | echo -e "${BLUE}1. Полная установка (Remnanode + Caddy + BBR + WARP-NATIVE (by distillium))${RESET}" 115 | echo -e "${BLUE}2. Только Remnanode${RESET}" 116 | echo -e "${BLUE}3. Только Caddy + self-steal${RESET}" 117 | echo -e "${BLUE}4. Управление IPv6${RESET}" 118 | echo -e "${BLUE}5. Только BBR${RESET}" 119 | echo -e "${BLUE}6. Установить WARP-NATIVE (by distillium)${RESET}" 120 | echo -e "${BLUE}7. Обновить Remnanode${RESET}" 121 | echo -e "${BLUE}8. Назад${RESET}" 122 | fi 123 | echo 124 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " REMNANODE_OPTION 125 | echo 126 | } 127 | 128 | display_backup_menu() { 129 | clear 130 | print_header 131 | menu "$(get_string "backup_menu")" 132 | if [ "$LANGUAGE" = "en" ]; then 133 | echo -e "${BLUE}1. Create Remnawave backup${RESET}" 134 | echo -e "${BLUE}2. Restore from Remnawave backup${RESET}" 135 | echo -e "${BLUE}3. Configure automatic backup${RESET}" 136 | echo -e "${RED}0. Exit${RESET}" 137 | else 138 | echo -e "${BLUE}1. Создать резервную копию Remnawave${RESET}" 139 | echo -e "${BLUE}2. Восстановить из резервной копии Remnawave${RESET}" 140 | echo -e "${BLUE}3. Настроить автоматическое резервное копирование${RESET}" 141 | echo -e "${RED}0. Выход${RESET}" 142 | fi 143 | echo 144 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " BACKUP_OPTION 145 | echo 146 | } 147 | 148 | display_main_menu() { 149 | clear 150 | print_header 151 | menu "$(get_string "main_menu")" 152 | if [ "$LANGUAGE" = "en" ]; then 153 | echo -e "${BLUE}1. Install/Update Remnawave${RESET}" 154 | echo -e "${BLUE}2. Install/Update Remnanode${RESET}" 155 | echo -e "${BLUE}3. Remnawave Backup and Restore${RESET}" 156 | echo -e "${RED}0. Exit${RESET}" 157 | else 158 | echo -e "${BLUE}1. Установка/Обновление Remnawave${RESET}" 159 | echo -e "${BLUE}2. Установка/Обновление Remnanode${RESET}" 160 | echo -e "${BLUE}3. Резервное копирование и восстановление Remnawave${RESET}" 161 | echo -e "${RED}0. Выход${RESET}" 162 | fi 163 | echo 164 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_option"):${RESET}") " MAIN_OPTION 165 | echo 166 | } 167 | 168 | run_script() { 169 | local script="$1" 170 | if [ -f "$script" ]; then 171 | bash "$script" 172 | local result=$? 173 | if [ $result -ne 0 ]; then 174 | warn "$(get_string "script_error")" 175 | read -n 1 -s -r -p "$(get_string "press_any_key")" 176 | fi 177 | else 178 | error "$(get_string "script_not_found"): $script" 179 | read -n 1 -s -r -p "$(get_string "press_any_key")" 180 | fi 181 | } 182 | 183 | main() { 184 | select_language 185 | while true; do 186 | display_main_menu 187 | case $MAIN_OPTION in 188 | 1) 189 | while true; do 190 | display_remnawave_menu 191 | case $REMNAWAVE_OPTION in 192 | 1) run_script "${SCRIPT_DIR}/scripts/remnawave/install-full.sh" ;; 193 | 2) run_script "${SCRIPT_DIR}/scripts/remnawave/install-panel.sh" ;; 194 | 3) run_script "${SCRIPT_DIR}/scripts/remnawave/install-subscription.sh" ;; 195 | 4) run_script "${SCRIPT_DIR}/scripts/remnawave/install-caddy.sh" ;; 196 | 5) run_script "${SCRIPT_DIR}/scripts/remnawave/update-full.sh" ;; 197 | 6) run_script "${SCRIPT_DIR}/scripts/remnawave/update-panel.sh" ;; 198 | 7) run_script "${SCRIPT_DIR}/scripts/remnawave/update-subscription.sh" ;; 199 | 8) break ;; 200 | *) warn "$(get_string "invalid_choice")" ;; 201 | esac 202 | done 203 | ;; 204 | 2) 205 | while true; do 206 | display_remnanode_menu 207 | case $REMNANODE_OPTION in 208 | 1) run_script "${SCRIPT_DIR}/scripts/remnanode/install-full.sh" ;; 209 | 2) run_script "${SCRIPT_DIR}/scripts/remnanode/install-node.sh" ;; 210 | 3) run_script "${SCRIPT_DIR}/scripts/remnanode/install-caddy.sh" ;; 211 | 4) run_script "${SCRIPT_DIR}/scripts/remnanode/install-ipv6.sh" ;; 212 | 5) run_script "${SCRIPT_DIR}/scripts/remnanode/install-bbr.sh" ;; 213 | 6) run_script "${SCRIPT_DIR}/scripts/remnanode/install-warp.sh" ;; 214 | 7) run_script "${SCRIPT_DIR}/scripts/remnanode/update.sh" ;; 215 | 8) break ;; 216 | *) warn "$(get_string "invalid_choice")" ;; 217 | esac 218 | done 219 | ;; 220 | 3) 221 | while true; do 222 | display_backup_menu 223 | case $BACKUP_OPTION in 224 | 1) run_script "${SCRIPT_DIR}/scripts/backups/backup.sh" ;; 225 | 2) run_script "${SCRIPT_DIR}/scripts/backups/restore.sh" ;; 226 | 3) run_script "${SCRIPT_DIR}/scripts/backups/auto_backup.sh" ;; 227 | 0) break ;; 228 | *) warn "$(get_string "invalid_choice")" ;; 229 | esac 230 | done 231 | ;; 232 | 0) 233 | info "$(get_string "exiting")" 234 | echo -e "${RESET}" 235 | exit 0 236 | ;; 237 | *) 238 | warn "$(get_string "invalid_choice")" 239 | ;; 240 | esac 241 | done 242 | } 243 | 244 | main -------------------------------------------------------------------------------- /scripts/backups/auto_backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | BACKUP_DIR="/opt/backups" 8 | AUTO_BACKUP_DIR="$BACKUP_DIR/auto_backup" 9 | SCRIPT_DIR="/opt/remnasetup/data/backup" 10 | 11 | mkdir -p "$AUTO_BACKUP_DIR" 12 | 13 | LANGUAGE_FILE="/opt/remnasetup/.language" 14 | if [ -f "$LANGUAGE_FILE" ]; then 15 | LANGUAGE=$(cat "$LANGUAGE_FILE") 16 | else 17 | LANGUAGE="ru" 18 | fi 19 | 20 | check_time_format() { 21 | local time=$1 22 | if [[ ! $time =~ ^([01]?[0-9]|2[0-3]):[0-5][0-9]$ ]]; then 23 | return 1 24 | fi 25 | return 0 26 | } 27 | 28 | get_hours_word() { 29 | local hours=$1 30 | local last_digit=$((hours % 10)) 31 | local last_two_digits=$((hours % 100)) 32 | 33 | if [ $last_two_digits -ge 11 ] && [ $last_two_digits -le 19 ]; then 34 | get_string "auto_backup_hours_5_20" 35 | elif [ $last_digit -eq 1 ]; then 36 | get_string "auto_backup_hours_1" 37 | elif [ $last_digit -ge 2 ] && [ $last_digit -le 4 ]; then 38 | get_string "auto_backup_hours_2_4" 39 | else 40 | get_string "auto_backup_hours_5_20" 41 | fi 42 | } 43 | 44 | get_days_word() { 45 | local days=$1 46 | local last_digit=$((days % 10)) 47 | local last_two_digits=$((days % 100)) 48 | 49 | if [ $last_two_digits -ge 11 ] && [ $last_two_digits -le 19 ]; then 50 | get_string "auto_backup_days_5_20" 51 | elif [ $last_digit -eq 1 ]; then 52 | get_string "auto_backup_days_1" 53 | elif [ $last_digit -ge 2 ] && [ $last_digit -le 4 ]; then 54 | get_string "auto_backup_days_2_4" 55 | else 56 | get_string "auto_backup_days_5_20" 57 | fi 58 | } 59 | 60 | ensure_cron_installed() { 61 | if ! command -v crontab &>/dev/null; then 62 | info "$(get_string auto_backup_cron_not_found)" 63 | if command -v apt-get &>/dev/null; then 64 | sudo apt-get update 65 | sudo apt-get install -y cron 66 | sudo systemctl enable cron 67 | sudo systemctl start cron 68 | elif command -v yum &>/dev/null; then 69 | sudo yum install -y cronie 70 | sudo systemctl enable crond 71 | sudo systemctl start crond 72 | elif command -v apk &>/dev/null; then 73 | sudo apk add dcron 74 | sudo rc-update add crond 75 | sudo service crond start 76 | else 77 | error "$(get_string auto_backup_cron_install_failed)" 78 | exit 1 79 | fi 80 | success "$(get_string auto_backup_cron_installed)" 81 | fi 82 | } 83 | 84 | cleanup_old_crons() { 85 | info "$(get_string "auto_backup_cleanup_old_crons")" 86 | if crontab -l 2>/dev/null | grep -q "$AUTO_BACKUP_DIR/backup.sh"; then 87 | crontab -l 2>/dev/null | grep -v "$AUTO_BACKUP_DIR/backup.sh" | crontab - 88 | info "$(get_string "auto_backup_old_crons_removed")" 89 | else 90 | info "$(get_string "auto_backup_no_old_crons")" 91 | fi 92 | } 93 | 94 | ensure_backup_dependencies() { 95 | for cmd in docker tar 7z; do 96 | if ! command -v $cmd &>/dev/null; then 97 | if command -v apt-get &>/dev/null; then 98 | sudo apt-get update 99 | if [ "$cmd" = "7z" ]; then 100 | sudo apt-get install -y p7zip-full 101 | else 102 | sudo apt-get install -y $cmd 103 | fi 104 | elif command -v yum &>/dev/null; then 105 | sudo yum install -y $cmd 106 | elif command -v apk &>/dev/null; then 107 | sudo apk add $cmd 108 | else 109 | exit 1 110 | fi 111 | if ! command -v $cmd &>/dev/null; then 112 | exit 1 113 | fi 114 | fi 115 | done 116 | } 117 | 118 | while true; do 119 | question "$(get_string "auto_backup_select_mode")" 120 | case $REPLY in 121 | [Yy]* ) BACKUP_MODE="daily"; break;; 122 | [Nn]* ) BACKUP_MODE="hourly"; break;; 123 | * ) warn "$(get_string "auto_backup_please_answer_yn")";; 124 | esac 125 | done 126 | 127 | if [ "$BACKUP_MODE" = "daily" ]; then 128 | info "$(get_string "auto_backup_current_time" "$(date +%H:%M)")" 129 | while true; do 130 | question "$(get_string "auto_backup_enter_time")" 131 | if check_time_format "$REPLY"; then 132 | BACKUP_TIME="$REPLY" 133 | break 134 | else 135 | warn "$(get_string "auto_backup_invalid_time")" 136 | fi 137 | done 138 | 139 | HOUR=${BACKUP_TIME%%:*} 140 | MINUTE=${BACKUP_TIME#*:} 141 | CRON_SCHEDULE="$MINUTE $HOUR * * *" 142 | else 143 | while true; do 144 | question "$(get_string "auto_backup_enter_interval")" 145 | if [[ "$REPLY" =~ ^[1-9]$|^1[0-9]$|^2[0-3]$ ]]; then 146 | INTERVAL_HOURS="$REPLY" 147 | break 148 | else 149 | warn "$(get_string "auto_backup_enter_number")" 150 | fi 151 | done 152 | CRON_SCHEDULE="0 */$INTERVAL_HOURS * * *" 153 | fi 154 | 155 | question "$(get_string "auto_backup_enter_storage")" 156 | STORAGE_DAYS="$REPLY" 157 | STORAGE_DAYS=${STORAGE_DAYS:-3} 158 | 159 | while true; do 160 | question "$(get_string "auto_backup_enter_password")" 161 | PASSWORD="$REPLY" 162 | if [ ${#PASSWORD} -ge 8 ]; then 163 | break 164 | else 165 | warn "$(get_string "auto_backup_password_short")" 166 | fi 167 | done 168 | 169 | while true; do 170 | question "$(get_string "auto_backup_use_telegram")" 171 | case $REPLY in 172 | [Yy]* ) USE_TELEGRAM=true; break;; 173 | [Nn]* ) USE_TELEGRAM=false; break;; 174 | * ) warn "$(get_string "auto_backup_please_answer_yn")";; 175 | esac 176 | done 177 | 178 | if [ "$USE_TELEGRAM" = true ]; then 179 | question "$(get_string "auto_backup_enter_bot_token")" 180 | BOT_TOKEN="$REPLY" 181 | 182 | question "$(get_string "auto_backup_enter_chat_id")" 183 | CHAT_ID="$REPLY" 184 | 185 | cp "$SCRIPT_DIR/backup_script_tg.sh" "$AUTO_BACKUP_DIR/backup.sh" 186 | sed -i "s/BOT_TOKEN=\"\"/BOT_TOKEN=\"$BOT_TOKEN\"/" "$AUTO_BACKUP_DIR/backup.sh" 187 | sed -i "s/CHAT_ID=\"\"/CHAT_ID=\"$CHAT_ID\"/" "$AUTO_BACKUP_DIR/backup.sh" 188 | sed -i "s/LANGUAGE=\"\"/LANGUAGE=\"$LANGUAGE\"/" "$AUTO_BACKUP_DIR/backup.sh" 189 | else 190 | cp "$SCRIPT_DIR/backup_script.sh" "$AUTO_BACKUP_DIR/backup.sh" 191 | fi 192 | 193 | sed -i "s/PASSWORD=\"\"/PASSWORD=\"$PASSWORD\"/" "$AUTO_BACKUP_DIR/backup.sh" 194 | sed -i "s/-mtime +3/-mtime +$STORAGE_DAYS/" "$AUTO_BACKUP_DIR/backup.sh" 195 | 196 | chmod +x "$AUTO_BACKUP_DIR/backup.sh" 197 | 198 | ensure_cron_installed 199 | 200 | ensure_backup_dependencies 201 | 202 | cleanup_old_crons 203 | 204 | (crontab -l 2>/dev/null; echo "$CRON_SCHEDULE $AUTO_BACKUP_DIR/backup.sh") | crontab - 205 | 206 | success "$(get_string "auto_backup_configured")" 207 | if [ "$BACKUP_MODE" = "daily" ]; then 208 | success "$(get_string "auto_backup_daily_at" "$BACKUP_TIME")" 209 | else 210 | HOURS_WORD=$(get_hours_word "$INTERVAL_HOURS") 211 | success "$(get_string "auto_backup_every_hours" "$INTERVAL_HOURS" "$HOURS_WORD")" 212 | fi 213 | DAYS_WORD=$(get_days_word "$STORAGE_DAYS") 214 | success "$(get_string "auto_backup_storage_days" "$STORAGE_DAYS" "$DAYS_WORD")" 215 | if [ "$USE_TELEGRAM" = true ]; then 216 | success "$(get_string "auto_backup_telegram_configured")" 217 | fi 218 | 219 | read -n 1 -s -r -p "$(get_string "auto_backup_press_key")" 220 | exit 0 -------------------------------------------------------------------------------- /scripts/backups/backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | BACKUP_DIR="/opt/backups" 8 | DATE=$(date +%F_%H-%M-%S) 9 | DB_VOLUME="remnawave-db-data" 10 | REMWAVE_DIR="/opt/remnawave" 11 | 12 | DB_TAR="remnawave-db-backup-$DATE.tar.gz" 13 | FINAL_ARCHIVE="remnawave-backup-$DATE.7z" 14 | TMP_DIR="$BACKUP_DIR/tmp-$DATE" 15 | 16 | mkdir -p "$BACKUP_DIR" 17 | 18 | while true; do 19 | question "$(get_string "backup_enter_password")" 20 | ARCHIVE_PASSWORD="$REPLY" 21 | if [ ${#ARCHIVE_PASSWORD} -ge 8 ]; then 22 | break 23 | else 24 | warn "$(get_string "backup_password_short")" 25 | fi 26 | done 27 | 28 | echo 29 | 30 | for cmd in docker tar 7z; do 31 | if ! command -v $cmd &>/dev/null; then 32 | warn "$(get_string "backup_cmd_not_found" "$cmd")" 33 | if command -v apt-get &>/dev/null; then 34 | sudo apt-get update 35 | if [ "$cmd" = "7z" ]; then 36 | sudo apt-get install -y p7zip-full 37 | else 38 | sudo apt-get install -y $cmd 39 | fi 40 | elif command -v yum &>/dev/null; then 41 | sudo yum install -y $cmd 42 | elif command -v apk &>/dev/null; then 43 | sudo apk add $cmd 44 | else 45 | error "$(get_string "backup_install_failed" "$cmd")" 46 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1 47 | fi 48 | if ! command -v $cmd &>/dev/null; then 49 | error "$(get_string "backup_cmd_not_installed" "$cmd")" 50 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1 51 | fi 52 | fi 53 | done 54 | 55 | if ! docker volume inspect $DB_VOLUME &>/dev/null; then 56 | error "$(get_string "backup_volume_not_found" "$DB_VOLUME")" 57 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1 58 | fi 59 | 60 | if [ ! -d "$REMWAVE_DIR" ]; then 61 | error "$(get_string "backup_dir_not_found" "$REMWAVE_DIR")" 62 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1 63 | fi 64 | 65 | if [ ! -f "$REMWAVE_DIR/.env" ]; then 66 | error "$(get_string "backup_env_not_found" "$REMWAVE_DIR")" 67 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1 68 | fi 69 | 70 | if [ ! -f "$REMWAVE_DIR/docker-compose.yml" ]; then 71 | error "$(get_string "backup_compose_not_found" "$REMWAVE_DIR")" 72 | read -n 1 -s -r -p "$(get_string "backup_press_key")"; exit 1 73 | fi 74 | 75 | mkdir -p "$TMP_DIR" 76 | 77 | info "$(get_string "backup_volume" "$DB_VOLUME")" 78 | docker run --rm \ 79 | -v ${DB_VOLUME}:/volume \ 80 | -v "$TMP_DIR":/backup \ 81 | alpine \ 82 | tar czf /backup/$DB_TAR -C /volume . 83 | 84 | info "$(get_string "backup_copying_configs")" 85 | cp "$REMWAVE_DIR/.env" "$TMP_DIR/" 86 | cp "$REMWAVE_DIR/docker-compose.yml" "$TMP_DIR/" 87 | 88 | info "$(get_string "backup_creating_archive")" 89 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$ARCHIVE_PASSWORD" "$BACKUP_DIR/$FINAL_ARCHIVE" "$TMP_DIR/*" >/dev/null 2>&1 90 | if [ $? -ne 0 ]; then 91 | error "$(get_string "backup_archive_error")" 92 | ls -l "$TMP_DIR" 93 | rm -rf "$TMP_DIR" 94 | exit 1 95 | fi 96 | 97 | rm -rf "$TMP_DIR" 98 | 99 | success "$(get_string "backup_ready" "$BACKUP_DIR/$FINAL_ARCHIVE")" 100 | read -n 1 -s -r -p "$(get_string "backup_press_key")" 101 | exit 0 -------------------------------------------------------------------------------- /scripts/backups/restore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | BACKUP_DIR="/opt/backups" 8 | WORK_DIR="$BACKUP_DIR/restore_work" 9 | DATE=$(date +%F_%H-%M-%S) 10 | DB_VOLUME="remnawave-db-data" 11 | REDIS_VOLUME="remnawave-redis-data" 12 | REMWAVE_DIR="/opt/remnawave" 13 | PANEL_CONTAINER="remnawave" 14 | DB_CONTAINER="remnawave-db" 15 | 16 | info "$(get_string "restore_start")" 17 | 18 | get_telegram_backups() { 19 | local bot_token=$1 20 | local chat_id=$2 21 | local temp_file="/tmp/telegram_backups.json" 22 | 23 | curl -s "https://api.telegram.org/bot${bot_token}/getUpdates?chat_id=${chat_id}&limit=5" > "$temp_file" 24 | 25 | local backups=() 26 | while IFS= read -r line; do 27 | if [[ $line =~ remnawave-backup-[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}\.7z ]]; then 28 | backups+=("$line") 29 | fi 30 | done < <(jq -r '.result[].message.document.file_name' "$temp_file" 2>/dev/null) 31 | 32 | rm -f "$temp_file" 33 | echo "${backups[@]}" 34 | } 35 | 36 | download_telegram_backup() { 37 | local bot_token=$1 38 | local chat_id=$2 39 | local file_name=$3 40 | local temp_file="/tmp/telegram_file.json" 41 | 42 | curl -s "https://api.telegram.org/bot${bot_token}/getUpdates?chat_id=${chat_id}&limit=5" > "$temp_file" 43 | local file_id=$(jq -r --arg name "$file_name" '.result[].message.document | select(.file_name == $name) | .file_id' "$temp_file") 44 | 45 | if [ -n "$file_id" ]; then 46 | local file_path=$(curl -s "https://api.telegram.org/bot${bot_token}/getFile?file_id=${file_id}" | jq -r '.result.file_path') 47 | curl -s "https://api.telegram.org/file/bot${bot_token}/${file_path}" -o "$BACKUP_DIR/$file_name" 48 | rm -f "$temp_file" 49 | return 0 50 | fi 51 | 52 | rm -f "$temp_file" 53 | return 1 54 | } 55 | 56 | while true; do 57 | question "$(get_string "restore_select_source")" 58 | SOURCE="$REPLY" 59 | if [[ "$SOURCE" == "y" || "$SOURCE" == "Y" ]]; then 60 | break 61 | elif [[ "$SOURCE" == "n" || "$SOURCE" == "N" ]]; then 62 | break 63 | else 64 | warn "$(get_string "restore_please_answer_yn")" 65 | fi 66 | done 67 | 68 | mkdir -p "$BACKUP_DIR" 69 | 70 | if [ "$SOURCE" = "telegram" ]; then 71 | question "$(get_string "restore_enter_bot_token")" 72 | BOT_TOKEN="$REPLY" 73 | 74 | question "$(get_string "restore_enter_chat_id")" 75 | CHAT_ID="$REPLY" 76 | 77 | while true; do 78 | info "$(get_string "restore_send_backup")" 79 | read -n 1 -s -r 80 | info "$(get_string "restore_getting_backups")" 81 | mapfile -t TG_BACKUPS < <(get_telegram_backups "$BOT_TOKEN" "$CHAT_ID") 82 | if [ ${#TG_BACKUPS[@]} -eq 0 ]; then 83 | warn "$(get_string "restore_no_backups")" 84 | else 85 | break 86 | fi 87 | done 88 | 89 | echo "$(get_string "restore_available_backups"):" 90 | for i in "${!TG_BACKUPS[@]}"; do 91 | echo "$((i+1)). ${TG_BACKUPS[$i]}" 92 | done 93 | 94 | while true; do 95 | question "$(get_string "restore_enter_backup_number")" 96 | if [[ "$REPLY" =~ ^[0-9]+$ ]] && (( REPLY >= 1 && REPLY <= ${#TG_BACKUPS[@]} )); then 97 | SELECTED_BACKUP="${TG_BACKUPS[$((REPLY-1))]}" 98 | info "$(get_string "restore_selected_backup" "$SELECTED_BACKUP")" 99 | break 100 | else 101 | warn "$(get_string "restore_invalid_choice")" 102 | fi 103 | done 104 | 105 | info "$(get_string "restore_downloading_archive")" 106 | if ! download_telegram_backup "$BOT_TOKEN" "$CHAT_ID" "$SELECTED_BACKUP"; then 107 | error "$(get_string "restore_download_failed")" 108 | read -n 1 -s -r -p "$(get_string "restore_press_key")"; exit 1 109 | fi 110 | ARCHIVE_PATH="$BACKUP_DIR/$SELECTED_BACKUP" 111 | else 112 | mapfile -t ARCHIVES < <(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' | sort) 113 | 114 | if [[ ${#ARCHIVES[@]} -eq 0 ]]; then 115 | info "$(get_string "restore_no_local_backups" "$BACKUP_DIR")" 116 | read -n 1 -s -r -p "$(get_string "restore_press_key_continue")" 117 | echo 118 | 119 | while true; do 120 | mapfile -t ARCHIVES < <(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'remnawave-backup-*.7z' | sort) 121 | if [[ ${#ARCHIVES[@]} -eq 0 ]]; then 122 | warn "$(get_string "restore_no_backups_found" "$BACKUP_DIR")" 123 | read -n 1 -s KEY 124 | if [[ "$KEY" == "n" || "$KEY" == "N" ]]; then 125 | info "$(get_string "restore_exit")" 126 | exit 0 127 | fi 128 | else 129 | break 130 | fi 131 | done 132 | fi 133 | 134 | echo "$(get_string "restore_available_local_backups"):" 135 | for i in "${!ARCHIVES[@]}"; do 136 | echo "$((i+1)). ${ARCHIVES[$i]}" 137 | done 138 | 139 | while true; do 140 | question "$(get_string "restore_enter_backup_number")" 141 | if [[ "$REPLY" =~ ^[0-9]+$ ]] && (( REPLY >= 1 && REPLY <= ${#ARCHIVES[@]} )); then 142 | ARCHIVE_PATH="${ARCHIVES[$((REPLY-1))]}" 143 | info "$(get_string "restore_selected_backup" "$ARCHIVE_PATH")" 144 | break 145 | else 146 | warn "$(get_string "restore_invalid_choice")" 147 | fi 148 | done 149 | fi 150 | 151 | while true; do 152 | question "$(get_string "restore_enter_password")" 153 | PASSWORD="$REPLY" 154 | if [ ${#PASSWORD} -ge 8 ]; then 155 | break 156 | else 157 | warn "$(get_string "restore_password_short")" 158 | fi 159 | done 160 | 161 | if ! command -v docker &>/dev/null; then 162 | warn "$(get_string "restore_docker_not_found")" 163 | sudo curl -fsSL https://get.docker.com | sh 164 | sudo systemctl start docker 165 | sudo systemctl enable docker 166 | fi 167 | 168 | if ! command -v 7z &>/dev/null; then 169 | warn "$(get_string "restore_7z_not_found")" 170 | sudo apt-get install -y p7zip-full 171 | fi 172 | 173 | if [ ! -d "$REMWAVE_DIR" ]; then 174 | info "$(get_string "restore_creating_directory" "$REMWAVE_DIR")" 175 | mkdir -p "$REMWAVE_DIR" 176 | fi 177 | 178 | info "$(get_string "restore_checking_archive")" 179 | TMP_RESTORE_DIR="$WORK_DIR/unpack" 180 | mkdir -p "$TMP_RESTORE_DIR" 181 | 7z x -p"$PASSWORD" "$ARCHIVE_PATH" -o"$TMP_RESTORE_DIR" >/dev/null 2>&1 182 | if [ $? -ne 0 ]; then 183 | error "$(get_string "restore_invalid_password")" 184 | read -n 1 -s -r -p "$(get_string "restore_press_key")"; exit 1 185 | fi 186 | 187 | info "$(get_string "restore_backup_before")" 188 | RESERVE_ARCHIVE="remnawave-backup-before-restore-$DATE.7z" 189 | 190 | if [ -f "$REMWAVE_DIR/.env" ] && [ -f "$REMWAVE_DIR/docker-compose.yml" ]; then 191 | cp "$REMWAVE_DIR/.env" "$BACKUP_DIR/" 192 | cp "$REMWAVE_DIR/docker-compose.yml" "$BACKUP_DIR/" 193 | fi 194 | 195 | if docker volume inspect $DB_VOLUME &>/dev/null; then 196 | docker run --rm \ 197 | -v ${DB_VOLUME}:/volume \ 198 | -v "$BACKUP_DIR":/backup \ 199 | alpine \ 200 | tar czf /backup/db_backup.tar.gz -C /volume . 201 | fi 202 | 203 | 7z a -t7z -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -p"$PASSWORD" "$BACKUP_DIR/$RESERVE_ARCHIVE" "$BACKUP_DIR/db_backup.tar.gz" "$BACKUP_DIR/.env" "$BACKUP_DIR/docker-compose.yml" >/dev/null 2>&1 204 | rm -f "$BACKUP_DIR/db_backup.tar.gz" "$BACKUP_DIR/.env" "$BACKUP_DIR/docker-compose.yml" 205 | 206 | if [ "$SOURCE" = "telegram" ]; then 207 | curl -F "chat_id=$CHAT_ID" \ 208 | -F document=@"$BACKUP_DIR/$RESERVE_ARCHIVE" \ 209 | "https://api.telegram.org/bot$BOT_TOKEN/sendDocument" >/dev/null 2>&1 210 | fi 211 | 212 | success "$(get_string "restore_backup_saved" "$BACKUP_DIR/$RESERVE_ARCHIVE")" 213 | 214 | if [ -d "$REMWAVE_DIR" ]; then 215 | info "$(get_string "restore_stopping_containers")" 216 | cd "$REMWAVE_DIR" && docker compose down 217 | 218 | info "$(get_string "restore_removing_old_data")" 219 | docker volume rm $DB_VOLUME $REDIS_VOLUME 2>/dev/null || true 220 | rm -f "$REMWAVE_DIR/.env" "$REMWAVE_DIR/docker-compose.yml" 221 | fi 222 | 223 | info "$(get_string "restore_restoring_configs")" 224 | cp "$TMP_RESTORE_DIR/.env" "$REMWAVE_DIR/" 225 | cp "$TMP_RESTORE_DIR/docker-compose.yml" "$REMWAVE_DIR/" 226 | 227 | info "$(get_string "restore_starting_containers")" 228 | cd "$REMWAVE_DIR" && docker compose up -d 229 | sleep 10 230 | 231 | info "$(get_string "restore_stopping_containers_again")" 232 | docker compose down 233 | 234 | info "$(get_string "restore_clearing_database")" 235 | docker run --rm \ 236 | -v ${DB_VOLUME}:/volume \ 237 | alpine \ 238 | sh -c "rm -rf /volume/*" 239 | 240 | info "$(get_string "restore_restoring_database")" 241 | DB_BACKUP_FILE=$(ls "$TMP_RESTORE_DIR"/remnawave-db-backup-*.tar.gz 2>/dev/null | head -n1) 242 | docker run --rm \ 243 | -v ${DB_VOLUME}:/volume \ 244 | -v "$TMP_RESTORE_DIR":/backup \ 245 | alpine \ 246 | tar xzf /backup/$(basename "$DB_BACKUP_FILE") -C /volume 247 | 248 | info "$(get_string "restore_removing_temp")" 249 | rm -rf "$WORK_DIR" 250 | 251 | info "$(get_string "restore_starting_remnawave")" 252 | cd "$REMWAVE_DIR" && docker compose up -d 253 | 254 | success "$(get_string "restore_complete")" 255 | read -n 1 -s -r -p "$(get_string "restore_press_key")" 256 | exit 0 -------------------------------------------------------------------------------- /scripts/common/colors.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RESET='\033[0m' 4 | 5 | RED='\033[0;31m' 6 | GREEN='\033[0;32m' 7 | YELLOW='\033[0;33m' 8 | BLUE='\033[0;34m' 9 | MAGENTA='\033[0;35m' 10 | CYAN='\033[0;36m' 11 | 12 | BOLD_RED='\033[1;31m' 13 | BOLD_GREEN='\033[1;32m' 14 | BOLD_YELLOW='\033[1;33m' 15 | BOLD_BLUE='\033[1;34m' 16 | BOLD_MAGENTA='\033[1;35m' 17 | BOLD_CYAN='\033[1;36m' -------------------------------------------------------------------------------- /scripts/common/functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/languages.sh" 5 | 6 | info() { 7 | echo -e "${BOLD_CYAN}[INFO]${RESET} $1" 8 | } 9 | 10 | warn() { 11 | echo -e "${BOLD_YELLOW}[WARN]${RESET} $1" 12 | } 13 | 14 | error() { 15 | echo -e "${BOLD_RED}[ERROR]${RESET} $1" 16 | } 17 | 18 | success() { 19 | echo -e "${BOLD_GREEN}[SUCCESS]${RESET} $1" 20 | } 21 | 22 | menu() { 23 | echo -e "${BOLD_MAGENTA}$1${RESET}" 24 | read -p "$(echo -e "${BOLD_CYAN}$(get_string "select_menu_option"):${RESET}") " choice 25 | echo "$choice" 26 | } 27 | 28 | question() { 29 | read -p "$(echo -e "${BOLD_CYAN}$1${RESET}") " REPLY 30 | } 31 | 32 | command_exists() { 33 | command -v "$1" >/dev/null 2>&1 34 | } 35 | 36 | check_root() { 37 | if [ "$(id -u)" != "0" ]; then 38 | error "$(get_string "root_required")" 39 | exit 1 40 | fi 41 | } 42 | 43 | check_directory() { 44 | if [ ! -d "$1" ]; then 45 | error "$(get_string "directory_not_exist" "$1")" 46 | exit 1 47 | fi 48 | } 49 | 50 | check_file() { 51 | if [ ! -f "$1" ]; then 52 | error "$(get_string "file_not_exist" "$1")" 53 | exit 1 54 | fi 55 | } 56 | 57 | create_directory() { 58 | if [ ! -d "$1" ]; then 59 | mkdir -p "$1" 60 | fi 61 | } 62 | 63 | backup_file() { 64 | if [ -f "$1" ]; then 65 | cp "$1" "$1.bak" 66 | fi 67 | } 68 | 69 | restore_file() { 70 | if [ -f "$1.bak" ]; then 71 | mv "$1.bak" "$1" 72 | fi 73 | } 74 | 75 | export -f info 76 | export -f warn 77 | export -f error 78 | export -f success 79 | export -f menu 80 | export -f question 81 | export -f command_exists 82 | export -f check_root 83 | export -f check_directory 84 | export -f check_file 85 | export -f create_directory 86 | export -f backup_file 87 | export -f restore_file 88 | -------------------------------------------------------------------------------- /scripts/remnanode/install-bbr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | check_bbr() { 8 | if sysctl net.ipv4.tcp_congestion_control | grep -q "bbr"; then 9 | info "$(get_string "install_bbr_already_configured")" 10 | read -n 1 -s -r -p "$(get_string "install_bbr_press_key")" 11 | exit 0 12 | return 1 13 | fi 14 | return 0 15 | } 16 | 17 | install_bbr() { 18 | info "$(get_string "install_bbr_installing")" 19 | echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf 20 | echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf 21 | sudo sysctl -p 22 | success "$(get_string "install_bbr_installed")" 23 | } 24 | 25 | main() { 26 | if ! check_bbr; then 27 | return 0 28 | fi 29 | 30 | install_bbr 31 | read -n 1 -s -r -p "$(get_string "install_bbr_press_key")" 32 | exit 0 33 | } 34 | 35 | main 36 | -------------------------------------------------------------------------------- /scripts/remnanode/install-caddy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | check_caddy() { 8 | if command -v caddy >/dev/null 2>&1; then 9 | info "$(get_string "install_caddy_node_already_installed")" 10 | while true; do 11 | question "$(get_string "install_caddy_node_update_config")" 12 | UPDATE_CONFIG="$REPLY" 13 | if [[ "$UPDATE_CONFIG" == "y" || "$UPDATE_CONFIG" == "Y" ]]; then 14 | return 0 15 | elif [[ "$UPDATE_CONFIG" == "n" || "$UPDATE_CONFIG" == "N" ]]; then 16 | info "$(get_string "install_caddy_node_already_installed")" 17 | read -n 1 -s -r -p "$(get_string "install_caddy_node_press_key")" 18 | exit 0 19 | return 1 20 | else 21 | warn "$(get_string "install_caddy_node_please_enter_yn")" 22 | fi 23 | done 24 | fi 25 | return 0 26 | } 27 | 28 | install_caddy() { 29 | info "$(get_string "install_caddy_node_installing")" 30 | sudo apt update -y 31 | sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https 32 | curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg 33 | curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list 34 | sudo apt update 35 | sudo apt install -y caddy 36 | 37 | success "$(get_string "install_caddy_node_installed")" 38 | } 39 | 40 | setup_site() { 41 | info "$(get_string "install_caddy_node_setup_site")" 42 | sudo chmod -R 777 /var 43 | sudo mkdir -p /var/www/site 44 | 45 | RANDOM_META_ID=$(openssl rand -hex 16) 46 | RANDOM_CLASS=$(openssl rand -hex 8) 47 | RANDOM_COMMENT=$(openssl rand -hex 12) 48 | 49 | META_NAMES=("render-id" "view-id" "page-id" "config-id") 50 | RANDOM_META_NAME=${META_NAMES[$RANDOM % ${#META_NAMES[@]}]} 51 | 52 | sudo cp -r "/opt/remnasetup/data/site/"* /var/www/site/ 53 | 54 | sudo sed -i "/\n " /var/www/site/index.html 55 | sudo sed -i "s//dev/null 2>&1; then 97 | install_caddy 98 | setup_site 99 | fi 100 | 101 | update_caddy_config 102 | 103 | success "$(get_string "install_caddy_node_installation_complete")" 104 | read -n 1 -s -r -p "$(get_string "install_caddy_node_press_key")" 105 | exit 0 106 | } 107 | 108 | main 109 | -------------------------------------------------------------------------------- /scripts/remnanode/install-full.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | check_docker() { 8 | if command -v docker >/dev/null 2>&1; then 9 | return 0 10 | else 11 | return 1 12 | fi 13 | } 14 | 15 | install_docker() { 16 | info "$(get_string "install_full_node_installing_docker")" 17 | sudo curl -fsSL https://get.docker.com | sh || { 18 | error "$(get_string "install_full_node_docker_error")" 19 | exit 1 20 | } 21 | success "$(get_string "install_full_node_docker_installed_success")" 22 | } 23 | 24 | check_components() { 25 | if command -v docker >/dev/null 2>&1; then 26 | info "$(get_string "install_full_node_docker_installed")" 27 | else 28 | info "$(get_string "install_full_node_docker_not_installed")" 29 | fi 30 | 31 | if [ -f "/opt/remnanode/docker-compose.yml" ]; then 32 | info "$(get_string "install_full_node_remnanode_installed")" 33 | while true; do 34 | question "$(get_string "install_full_node_update_remnanode")" 35 | UPDATE_NODE="$REPLY" 36 | if [[ "$UPDATE_NODE" == "y" || "$UPDATE_NODE" == "Y" ]]; then 37 | UPDATE_REMNANODE=true 38 | break 39 | elif [[ "$UPDATE_NODE" == "n" || "$UPDATE_NODE" == "N" ]]; then 40 | SKIP_REMNANODE=true 41 | break 42 | else 43 | warn "$(get_string "install_full_node_please_enter_yn")" 44 | fi 45 | done 46 | fi 47 | 48 | if command -v caddy >/dev/null 2>&1; then 49 | info "$(get_string "install_full_node_caddy_installed")" 50 | while true; do 51 | question "$(get_string "install_full_node_update_caddy")" 52 | UPDATE_CADDY="$REPLY" 53 | if [[ "$UPDATE_CADDY" == "y" || "$UPDATE_CADDY" == "Y" ]]; then 54 | UPDATE_CADDY=true 55 | break 56 | elif [[ "$UPDATE_CADDY" == "n" || "$UPDATE_CADDY" == "N" ]]; then 57 | SKIP_CADDY=true 58 | break 59 | else 60 | warn "$(get_string "install_full_node_please_enter_yn")" 61 | fi 62 | done 63 | fi 64 | 65 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then 66 | info "$(get_string "warp_native_already_installed")" 67 | while true; do 68 | question "$(get_string "warp_native_reconfigure")" 69 | RECONFIGURE="$REPLY" 70 | if [[ "$RECONFIGURE" == "y" || "$RECONFIGURE" == "Y" ]]; then 71 | SKIP_WARP=false 72 | break 73 | elif [[ "$RECONFIGURE" == "n" || "$RECONFIGURE" == "N" ]]; then 74 | SKIP_WARP=true 75 | info "$(get_string "warp_native_skip_installation")" 76 | break 77 | else 78 | warn "$(get_string "warp_native_please_enter_yn")" 79 | fi 80 | done 81 | else 82 | SKIP_WARP=false 83 | fi 84 | 85 | if sysctl net.ipv4.tcp_congestion_control | grep -q bbr; then 86 | info "$(get_string "install_full_node_bbr_configured")" 87 | SKIP_BBR=true 88 | fi 89 | } 90 | 91 | request_data() { 92 | if [[ "$SKIP_CADDY" != "true" ]]; then 93 | while true; do 94 | question "$(get_string "install_full_node_enter_domain")" 95 | DOMAIN="$REPLY" 96 | if [[ "$DOMAIN" == "n" || "$DOMAIN" == "N" ]]; then 97 | while true; do 98 | question "$(get_string "install_full_node_confirm_skip_caddy")" 99 | CONFIRM="$REPLY" 100 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then 101 | SKIP_CADDY=true 102 | break 103 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then 104 | break 105 | else 106 | warn "$(get_string "install_full_node_please_enter_yn")" 107 | fi 108 | done 109 | if [[ "$SKIP_CADDY" == "true" ]]; then 110 | break 111 | fi 112 | elif [[ -n "$DOMAIN" ]]; then 113 | break 114 | fi 115 | warn "$(get_string "install_full_node_domain_empty")" 116 | done 117 | 118 | if [[ "$SKIP_CADDY" != "true" ]]; then 119 | while true; do 120 | question "$(get_string "install_full_node_enter_port")" 121 | MONITOR_PORT="$REPLY" 122 | if [[ "$MONITOR_PORT" == "n" || "$MONITOR_PORT" == "N" ]]; then 123 | while true; do 124 | question "$(get_string "install_full_node_confirm_skip_caddy")" 125 | CONFIRM="$REPLY" 126 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then 127 | SKIP_CADDY=true 128 | break 129 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then 130 | break 131 | else 132 | warn "$(get_string "install_full_node_please_enter_yn")" 133 | fi 134 | done 135 | if [[ "$SKIP_CADDY" == "true" ]]; then 136 | break 137 | fi 138 | fi 139 | MONITOR_PORT=${MONITOR_PORT:-8443} 140 | if [[ "$MONITOR_PORT" =~ ^[0-9]+$ ]]; then 141 | break 142 | fi 143 | warn "$(get_string "install_full_node_port_must_be_number")" 144 | done 145 | fi 146 | fi 147 | 148 | if [[ "$SKIP_REMNANODE" != "true" ]]; then 149 | while true; do 150 | question "$(get_string "install_full_node_enter_app_port")" 151 | APP_PORT="$REPLY" 152 | if [[ "$APP_PORT" == "n" || "$APP_PORT" == "N" ]]; then 153 | while true; do 154 | question "$(get_string "install_full_node_confirm_skip_remnanode")" 155 | CONFIRM="$REPLY" 156 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then 157 | SKIP_REMNANODE=true 158 | break 159 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then 160 | break 161 | else 162 | warn "$(get_string "install_full_node_please_enter_yn")" 163 | fi 164 | done 165 | if [[ "$SKIP_REMNANODE" == "true" ]]; then 166 | break 167 | fi 168 | fi 169 | APP_PORT=${APP_PORT:-3001} 170 | if [[ "$APP_PORT" =~ ^[0-9]+$ ]]; then 171 | break 172 | fi 173 | warn "$(get_string "install_full_node_port_must_be_number")" 174 | done 175 | 176 | if [[ "$SKIP_REMNANODE" != "true" ]]; then 177 | while true; do 178 | question "$(get_string "install_full_node_enter_ssl_cert")" 179 | SSL_CERT_FULL="$REPLY" 180 | if [[ "$SSL_CERT_FULL" == "n" || "$SSL_CERT_FULL" == "N" ]]; then 181 | while true; do 182 | question "$(get_string "install_full_node_confirm_skip_remnanode")" 183 | CONFIRM="$REPLY" 184 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then 185 | SKIP_REMNANODE=true 186 | break 187 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then 188 | break 189 | else 190 | warn "$(get_string "install_full_node_please_enter_yn")" 191 | fi 192 | done 193 | if [[ "$SKIP_REMNANODE" == "true" ]]; then 194 | break 195 | fi 196 | elif [[ -n "$SSL_CERT_FULL" ]]; then 197 | break 198 | fi 199 | warn "$(get_string "install_full_node_ssl_cert_empty")" 200 | done 201 | fi 202 | fi 203 | 204 | if [[ "$SKIP_WARP" != "true" ]]; then 205 | while true; do 206 | question "$(get_string "install_full_node_install_warp_native")" 207 | INSTALL_WARP="$REPLY" 208 | if [[ "$INSTALL_WARP" == "n" || "$INSTALL_WARP" == "N" ]]; then 209 | while true; do 210 | question "$(get_string "install_full_node_confirm_skip_warp")" 211 | CONFIRM="$REPLY" 212 | if [[ "$CONFIRM" == "y" || "$CONFIRM" == "Y" ]]; then 213 | SKIP_WARP=true 214 | break 215 | elif [[ "$CONFIRM" == "n" || "$CONFIRM" == "N" ]]; then 216 | break 217 | else 218 | warn "$(get_string "install_full_node_please_enter_yn")" 219 | fi 220 | done 221 | if [[ "$SKIP_WARP" == "true" ]]; then 222 | break 223 | fi 224 | elif [[ "$INSTALL_WARP" == "y" || "$INSTALL_WARP" == "Y" ]]; then 225 | break 226 | else 227 | warn "$(get_string "install_full_node_please_enter_yn")" 228 | fi 229 | done 230 | fi 231 | 232 | if [[ "$SKIP_BBR" != "true" ]]; then 233 | while true; do 234 | question "$(get_string "install_full_node_need_bbr")" 235 | BBR_ANSWER="$REPLY" 236 | if [[ "$BBR_ANSWER" == "n" || "$BBR_ANSWER" == "N" ]]; then 237 | SKIP_BBR=true 238 | break 239 | elif [[ "$BBR_ANSWER" == "y" || "$BBR_ANSWER" == "Y" ]]; then 240 | SKIP_BBR=false 241 | break 242 | else 243 | warn "$(get_string "install_full_node_please_enter_yn")" 244 | fi 245 | done 246 | fi 247 | } 248 | 249 | RESTORE_DNS_REQUIRED=false 250 | 251 | restore_dns() { 252 | if [[ "$RESTORE_DNS_REQUIRED" == true && -f /etc/resolv.conf.backup ]]; then 253 | cp /etc/resolv.conf.backup /etc/resolv.conf 254 | success "$(get_string "warp_native_dns_restored")" 255 | RESTORE_DNS_REQUIRED=false 256 | fi 257 | } 258 | 259 | uninstall_warp_native() { 260 | info "$(get_string "warp_native_stopping_warp")" 261 | 262 | if ip link show warp &>/dev/null; then 263 | wg-quick down warp &>/dev/null || true 264 | fi 265 | 266 | systemctl disable wg-quick@warp &>/dev/null || true 267 | 268 | rm -f /etc/wireguard/warp.conf &>/dev/null 269 | rm -rf /etc/wireguard &>/dev/null 270 | rm -f /usr/local/bin/wgcf &>/dev/null 271 | rm -f wgcf-account.toml wgcf-profile.conf &>/dev/null 272 | 273 | info "$(get_string "warp_native_removing_packages")" 274 | DEBIAN_FRONTEND=noninteractive apt remove --purge -y wireguard &>/dev/null || true 275 | DEBIAN_FRONTEND=noninteractive apt autoremove -y &>/dev/null || true 276 | 277 | success "$(get_string "warp_native_uninstall_complete")" 278 | } 279 | 280 | install_warp() { 281 | info "$(get_string "warp_native_start_install")" 282 | echo "" 283 | 284 | info "$(get_string "warp_native_install_wireguard")" 285 | apt update -qq &>/dev/null || { 286 | error "$(get_string "warp_native_update_failed")" 287 | exit 1 288 | } 289 | apt install wireguard -y &>/dev/null || { 290 | error "$(get_string "warp_native_wireguard_failed")" 291 | exit 1 292 | } 293 | success "$(get_string "warp_native_wireguard_ok")" 294 | echo "" 295 | 296 | info "$(get_string "warp_native_temp_dns")" 297 | cp /etc/resolv.conf /etc/resolv.conf.backup 298 | RESTORE_DNS_REQUIRED=true 299 | echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" > /etc/resolv.conf || { 300 | error "$(get_string "warp_native_dns_failed")" 301 | exit 1 302 | } 303 | success "$(get_string "warp_native_dns_ok")" 304 | echo "" 305 | 306 | info "$(get_string "warp_native_download_wgcf")" 307 | WGCF_RELEASE_URL="https://api.github.com/repos/ViRb3/wgcf/releases/latest" 308 | WGCF_VERSION=$(curl -s "$WGCF_RELEASE_URL" | grep tag_name | cut -d '"' -f 4) 309 | 310 | if [ -z "$WGCF_VERSION" ]; then 311 | error "$(get_string "warp_native_wgcf_version_failed")" 312 | exit 1 313 | fi 314 | 315 | ARCH=$(uname -m) 316 | case $ARCH in 317 | x86_64) WGCF_ARCH="amd64" ;; 318 | aarch64|arm64) WGCF_ARCH="arm64" ;; 319 | armv7l) WGCF_ARCH="armv7" ;; 320 | *) WGCF_ARCH="amd64" ;; 321 | esac 322 | 323 | info "$(get_string "warp_native_arch_detected") $ARCH -> $WGCF_ARCH" 324 | 325 | WGCF_DOWNLOAD_URL="https://github.com/ViRb3/wgcf/releases/download/${WGCF_VERSION}/wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}" 326 | WGCF_BINARY_NAME="wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}" 327 | 328 | wget -q "$WGCF_DOWNLOAD_URL" -O "$WGCF_BINARY_NAME" || { 329 | error "$(get_string "warp_native_wgcf_download_failed")" 330 | exit 1 331 | } 332 | 333 | chmod +x "$WGCF_BINARY_NAME" || { 334 | error "$(get_string "warp_native_wgcf_chmod_failed")" 335 | exit 1 336 | } 337 | mv "$WGCF_BINARY_NAME" /usr/local/bin/wgcf || { 338 | error "$(get_string "warp_native_wgcf_move_failed")" 339 | exit 1 340 | } 341 | success "wgcf $WGCF_VERSION $(get_string "warp_native_wgcf_installed")" 342 | echo "" 343 | 344 | info "$(get_string "warp_native_register_wgcf")" 345 | 346 | if [[ -f wgcf-account.toml ]]; then 347 | info "$(get_string "warp_native_account_exists")" 348 | else 349 | info "$(get_string "warp_native_registering")" 350 | 351 | info "$(get_string "warp_native_wgcf_binary_check")" 352 | if ! wgcf --help &>/dev/null; then 353 | warn "$(get_string "warp_native_wgcf_not_executable")" 354 | chmod +x /usr/local/bin/wgcf 355 | if ! wgcf --help &>/dev/null; then 356 | error "$(get_string "warp_native_wgcf_not_executable")" 357 | exit 1 358 | fi 359 | fi 360 | 361 | output=$(timeout 60 bash -c 'yes | wgcf register' 2>&1) 362 | ret=$? 363 | 364 | if [[ $ret -ne 0 ]]; then 365 | warn "$(get_string "warp_native_register_error") $ret." 366 | 367 | if [[ $ret -eq 126 ]]; then 368 | warn "$(get_string "warp_native_wgcf_not_executable")" 369 | elif [[ $ret -eq 124 ]]; then 370 | warn "Registration timed out after 60 seconds." 371 | elif [[ "$output" == *"500 Internal Server Error"* ]]; then 372 | warn "$(get_string "warp_native_cf_500_detected")" 373 | info "$(get_string "warp_native_known_behavior")" 374 | elif [[ "$output" == *"429"* || "$output" == *"Too Many Requests"* ]]; then 375 | warn "$(get_string "warp_native_cf_rate_limited")" 376 | elif [[ "$output" == *"403"* || "$output" == *"Forbidden"* ]]; then 377 | warn "$(get_string "warp_native_cf_forbidden")" 378 | elif [[ "$output" == *"network"* || "$output" == *"connection"* ]]; then 379 | warn "$(get_string "warp_native_network_issue")" 380 | else 381 | warn "$(get_string "warp_native_unknown_error")" 382 | echo "$output" 383 | fi 384 | 385 | info "$(get_string "warp_native_trying_alternative")" 386 | echo | wgcf register &>/dev/null || true 387 | 388 | sleep 2 389 | fi 390 | 391 | if [[ ! -f wgcf-account.toml ]]; then 392 | error "$(get_string "warp_native_registration_failed")" 393 | exit 1 394 | fi 395 | 396 | success "$(get_string "warp_native_account_created")" 397 | fi 398 | 399 | wgcf generate &>/dev/null || { 400 | error "$(get_string "warp_native_config_gen_failed")" 401 | exit 1 402 | } 403 | success "$(get_string "warp_native_config_generated")" 404 | echo "" 405 | 406 | info "$(get_string "warp_native_edit_config")" 407 | WGCF_CONF_FILE="wgcf-profile.conf" 408 | 409 | if [ ! -f "$WGCF_CONF_FILE" ]; then 410 | error "$(get_string "warp_native_config_not_found" | sed "s/не найден/Файл $WGCF_CONF_FILE не найден/" | sed "s/not found/File $WGCF_CONF_FILE not found/")" 411 | exit 1 412 | fi 413 | 414 | sed -i '/^DNS =/d' "$WGCF_CONF_FILE" || { 415 | error "$(get_string "warp_native_dns_removed")" 416 | exit 1 417 | } 418 | 419 | if ! grep -q "Table = off" "$WGCF_CONF_FILE"; then 420 | sed -i '/^MTU =/aTable = off' "$WGCF_CONF_FILE" || { 421 | error "$(get_string "warp_native_table_off_failed")" 422 | exit 1 423 | } 424 | fi 425 | 426 | if ! grep -q "PersistentKeepalive = 25" "$WGCF_CONF_FILE"; then 427 | sed -i '/^Endpoint =/aPersistentKeepalive = 25' "$WGCF_CONF_FILE" || { 428 | error "$(get_string "warp_native_keepalive_failed")" 429 | exit 1 430 | } 431 | fi 432 | 433 | mkdir -p /etc/wireguard || { 434 | error "$(get_string "warp_native_wireguard_dir_failed")" 435 | exit 1 436 | } 437 | mv "$WGCF_CONF_FILE" /etc/wireguard/warp.conf || { 438 | error "$(get_string "warp_native_config_move_failed")" 439 | exit 1 440 | } 441 | success "$(get_string "warp_native_config_saved")" 442 | echo "" 443 | 444 | info "$(get_string "warp_native_check_ipv6")" 445 | 446 | is_ipv6_enabled() { 447 | sysctl net.ipv6.conf.all.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1 448 | sysctl net.ipv6.conf.default.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1 449 | ip -6 addr show scope global | grep -qv 'inet6 .*fe80::' || return 1 450 | return 0 451 | } 452 | 453 | if is_ipv6_enabled; then 454 | success "$(get_string "warp_native_ipv6_enabled")" 455 | else 456 | warn "$(get_string "warp_native_ipv6_disabled")" 457 | sed -i 's/,\s*[0-9a-fA-F:]\+\/128//' /etc/wireguard/warp.conf 458 | sed -i '/Address = [0-9a-fA-F:]\+\/128/d' /etc/wireguard/warp.conf 459 | success "$(get_string "warp_native_ipv6_removed")" 460 | fi 461 | echo "" 462 | 463 | info "$(get_string "warp_native_connect_warp")" 464 | systemctl start wg-quick@warp &>/dev/null || { 465 | error "$(get_string "warp_native_connect_failed")" 466 | exit 1 467 | } 468 | success "$(get_string "warp_native_warp_connected")" 469 | echo "" 470 | 471 | info "$(get_string "warp_native_check_status")" 472 | 473 | if ! wg show warp &>/dev/null; then 474 | error "$(get_string "warp_native_warp_not_found")" 475 | exit 1 476 | fi 477 | 478 | for i in {1..10}; do 479 | handshake=$(wg show warp | grep "latest handshake" | awk -F': ' '{print $2}') 480 | if [[ "$handshake" == *"second"* || "$handshake" == *"minute"* ]]; then 481 | success "$(get_string "warp_native_handshake_received") $handshake" 482 | success "$(get_string "warp_native_warp_active")" 483 | break 484 | fi 485 | sleep 1 486 | done 487 | 488 | if [[ -z "$handshake" || "$handshake" == "0 seconds ago" ]]; then 489 | warn "$(get_string "warp_native_handshake_failed")" 490 | fi 491 | 492 | curl_result=$(curl -s --interface warp https://www.cloudflare.com/cdn-cgi/trace | grep "warp=" | cut -d= -f2) 493 | 494 | if [[ "$curl_result" == "on" ]]; then 495 | success "$(get_string "warp_native_cf_response")" 496 | else 497 | warn "$(get_string "warp_native_cf_not_confirmed")" 498 | fi 499 | echo "" 500 | 501 | info "$(get_string "warp_native_enable_autostart")" 502 | systemctl enable wg-quick@warp &>/dev/null || { 503 | error "$(get_string "warp_native_autostart_failed")" 504 | exit 1 505 | } 506 | success "$(get_string "warp_native_autostart_enabled")" 507 | echo "" 508 | 509 | restore_dns 510 | success "$(get_string "warp_native_installation_complete")" 511 | } 512 | 513 | install_bbr() { 514 | info "$(get_string "install_full_node_installing_bbr")" 515 | sudo sh -c 'modprobe tcp_bbr && sysctl net.ipv4.tcp_available_congestion_control && sysctl -w net.ipv4.tcp_congestion_control=bbr && echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf && sysctl -p' 516 | success "$(get_string "install_full_node_bbr_installed_success")" 517 | } 518 | 519 | setup_logs_and_logrotate() { 520 | info "$(get_string "install_full_node_setup_logs")" 521 | 522 | if [ ! -d "/var/log/remnanode" ]; then 523 | sudo mkdir -p /var/log/remnanode 524 | sudo chmod -R 777 /var/log/remnanode 525 | info "$(get_string "install_full_node_logs_dir_created")" 526 | else 527 | info "$(get_string "install_full_node_logs_dir_exists")" 528 | fi 529 | 530 | if ! command -v logrotate >/dev/null 2>&1; then 531 | sudo apt update -y && sudo apt install logrotate -y 532 | fi 533 | 534 | if [ ! -f "/etc/logrotate.d/remnanode" ] || ! grep -q "copytruncate" /etc/logrotate.d/remnanode; then 535 | sudo tee /etc/logrotate.d/remnanode > /dev/null <\n " /var/www/site/index.html 578 | sudo sed -i "s/ .env 599 | echo "$SSL_CERT_FULL" >> .env 600 | 601 | info "$(get_string "install_full_node_using_standard_compose")" 602 | cp "/opt/remnasetup/data/docker/node-compose.yml" docker-compose.yml 603 | 604 | sudo docker compose up -d || { 605 | error "$(get_string "install_full_node_remnanode_error")" 606 | exit 1 607 | } 608 | success "$(get_string "install_full_node_remnanode_installed_success")" 609 | } 610 | 611 | main() { 612 | trap restore_dns EXIT 613 | 614 | info "$(get_string "install_full_node_start")" 615 | 616 | check_components 617 | request_data 618 | 619 | info "$(get_string "install_full_node_updating_packages")" 620 | sudo apt update -y 621 | 622 | if ! check_docker; then 623 | install_docker 624 | fi 625 | 626 | if [[ "$SKIP_WARP" != "true" ]]; then 627 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then 628 | uninstall_warp_native 629 | echo "" 630 | fi 631 | install_warp 632 | fi 633 | 634 | if [[ "$SKIP_BBR" != "true" ]]; then 635 | install_bbr 636 | fi 637 | 638 | if [[ "$SKIP_CADDY" != "true" ]]; then 639 | if [[ "$UPDATE_CADDY" == "true" ]]; then 640 | sudo systemctl stop caddy 641 | sudo rm -f /etc/caddy/Caddyfile 642 | fi 643 | install_caddy 644 | fi 645 | 646 | setup_logs_and_logrotate 647 | 648 | if [[ "$SKIP_REMNANODE" != "true" ]]; then 649 | if [[ "$UPDATE_REMNANODE" == "true" ]]; then 650 | cd /opt/remnanode 651 | sudo docker compose down 652 | rm -f docker-compose.yml 653 | fi 654 | install_remnanode 655 | fi 656 | 657 | success "$(get_string "install_full_node_complete")" 658 | 659 | if [[ "$SKIP_WARP" != "true" ]]; then 660 | echo "" 661 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_check_service"):${RESET} systemctl status wg-quick@warp" 662 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_show_info"):${RESET} wg show warp" 663 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_stop_interface"):${RESET} systemctl stop wg-quick@warp" 664 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_start_interface"):${RESET} systemctl start wg-quick@warp" 665 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_restart_interface"):${RESET} systemctl restart wg-quick@warp" 666 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_disable_autostart"):${RESET} systemctl disable wg-quick@warp" 667 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_enable_autostart_cmd"):${RESET} systemctl enable wg-quick@warp" 668 | echo "" 669 | fi 670 | 671 | read -n 1 -s -r -p "$(get_string "install_full_node_press_key")" 672 | exit 0 673 | } 674 | 675 | main 676 | -------------------------------------------------------------------------------- /scripts/remnanode/install-ipv6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | check_ipv6_status() { 8 | if sysctl net.ipv6.conf.all.disable_ipv6 | grep -q "= 1"; then 9 | return 1 10 | else 11 | return 0 12 | fi 13 | } 14 | 15 | disable_ipv6() { 16 | info "$(get_string "ipv6_disabling")" 17 | 18 | sudo tee -a /etc/sysctl.conf > /dev/null << EOF 19 | 20 | # IPv6 Disable 21 | net.ipv6.conf.all.disable_ipv6 = 1 22 | net.ipv6.conf.default.disable_ipv6 = 1 23 | net.ipv6.conf.lo.disable_ipv6 = 1 24 | EOF 25 | 26 | if [ -d "/proc/sys/net/ipv6/conf/tun0" ]; then 27 | echo "net.ipv6.conf.tun0.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf 28 | fi 29 | 30 | sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 31 | sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 32 | sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 33 | 34 | if [ -d "/proc/sys/net/ipv6/conf/tun0" ]; then 35 | sudo sysctl -w net.ipv6.conf.tun0.disable_ipv6=1 36 | fi 37 | 38 | success "$(get_string "ipv6_disabled_success")" 39 | } 40 | 41 | enable_ipv6() { 42 | info "$(get_string "ipv6_enabling")" 43 | 44 | sudo sed -i '/^# IPv6 Disable$/,/^net\.ipv6\.conf\..*\.disable_ipv6 = 1$/d' /etc/sysctl.conf 45 | 46 | sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0 47 | sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0 48 | sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0 49 | 50 | if [ -d "/proc/sys/net/ipv6/conf/tun0" ]; then 51 | sudo sysctl -w net.ipv6.conf.tun0.disable_ipv6=0 52 | fi 53 | 54 | success "$(get_string "ipv6_enabled_success")" 55 | } 56 | 57 | main() { 58 | if check_ipv6_status; then 59 | echo -e "${GREEN}[INFO] $(get_string "ipv6_status_enabled")${RESET}" 60 | while true; do 61 | question "$(get_string "ipv6_disable_confirm")" 62 | if [[ "$REPLY" == "y" || "$REPLY" == "Y" ]]; then 63 | disable_ipv6 64 | break 65 | elif [[ "$REPLY" == "n" || "$REPLY" == "N" ]]; then 66 | info "$(get_string "ipv6_operation_cancelled")" 67 | break 68 | else 69 | warn "$(get_string "install_full_node_please_enter_yn")" 70 | fi 71 | done 72 | else 73 | echo -e "${RED}[INFO] $(get_string "ipv6_status_disabled")${RESET}" 74 | while true; do 75 | question "$(get_string "ipv6_enable_confirm")" 76 | if [[ "$REPLY" == "y" || "$REPLY" == "Y" ]]; then 77 | enable_ipv6 78 | break 79 | elif [[ "$REPLY" == "n" || "$REPLY" == "N" ]]; then 80 | info "$(get_string "ipv6_operation_cancelled")" 81 | break 82 | else 83 | warn "$(get_string "install_full_node_please_enter_yn")" 84 | fi 85 | done 86 | fi 87 | 88 | read -n 1 -s -r -p "$(get_string "press_any_key")" 89 | exit 0 90 | } 91 | 92 | main -------------------------------------------------------------------------------- /scripts/remnanode/install-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | check_docker() { 8 | if command -v docker >/dev/null 2>&1; then 9 | info "$(get_string "install_node_docker_installed")" 10 | return 0 11 | else 12 | return 1 13 | fi 14 | } 15 | 16 | install_docker() { 17 | info "$(get_string "install_node_installing_docker")" 18 | sudo curl -fsSL https://get.docker.com | sh || { 19 | error "$(get_string "install_node_docker_error")" 20 | exit 1 21 | } 22 | success "$(get_string "install_node_docker_success")" 23 | } 24 | 25 | setup_logs_and_logrotate() { 26 | info "$(get_string "install_node_setup_logs")" 27 | 28 | if [ ! -d "/var/log/remnanode" ]; then 29 | sudo mkdir -p /var/log/remnanode 30 | sudo chmod -R 777 /var/log/remnanode 31 | info "$(get_string "install_node_logs_dir_created")" 32 | else 33 | info "$(get_string "install_node_logs_dir_exists")" 34 | fi 35 | 36 | if ! command -v logrotate >/dev/null 2>&1; then 37 | sudo apt update -y && sudo apt install logrotate -y 38 | fi 39 | 40 | if [ ! -f "/etc/logrotate.d/remnanode" ] || ! grep -q "copytruncate" /etc/logrotate.d/remnanode; then 41 | sudo tee /etc/logrotate.d/remnanode > /dev/null < .env 86 | echo "$SSL_CERT_FULL" >> .env 87 | 88 | cp "/opt/remnasetup/data/docker/node-compose.yml" docker-compose.yml 89 | 90 | sudo docker compose up -d || { 91 | error "$(get_string "install_node_error")" 92 | exit 1 93 | } 94 | success "$(get_string "install_node_success")" 95 | } 96 | 97 | main() { 98 | if check_remnanode; then 99 | cd /opt/remnanode 100 | sudo docker compose down 101 | fi 102 | 103 | while true; do 104 | question "$(get_string "install_node_enter_app_port")" 105 | APP_PORT="$REPLY" 106 | APP_PORT=${APP_PORT:-3001} 107 | if [[ "$APP_PORT" =~ ^[0-9]+$ ]]; then 108 | break 109 | fi 110 | warn "$(get_string "install_node_port_must_be_number")" 111 | done 112 | 113 | while true; do 114 | question "$(get_string "install_node_enter_ssl_cert")" 115 | SSL_CERT_FULL="$REPLY" 116 | if [[ -n "$SSL_CERT_FULL" ]]; then 117 | break 118 | fi 119 | warn "$(get_string "install_node_ssl_cert_empty")" 120 | done 121 | 122 | if ! check_docker; then 123 | install_docker 124 | fi 125 | 126 | setup_logs_and_logrotate 127 | 128 | install_remnanode 129 | 130 | success "$(get_string "install_node_complete")" 131 | read -n 1 -s -r -p "$(get_string "install_node_press_key")" 132 | exit 0 133 | } 134 | 135 | main -------------------------------------------------------------------------------- /scripts/remnanode/install-warp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | RESTORE_DNS_REQUIRED=false 8 | 9 | restore_dns() { 10 | if [[ "$RESTORE_DNS_REQUIRED" == true && -f /etc/resolv.conf.backup ]]; then 11 | cp /etc/resolv.conf.backup /etc/resolv.conf 12 | success "$(get_string "warp_native_dns_restored")" 13 | RESTORE_DNS_REQUIRED=false 14 | fi 15 | } 16 | 17 | trap restore_dns EXIT 18 | 19 | check_warp_native() { 20 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then 21 | info "$(get_string "warp_native_already_installed")" 22 | while true; do 23 | question "$(get_string "warp_native_reconfigure")" 24 | RECONFIGURE="$REPLY" 25 | if [[ "$RECONFIGURE" == "y" || "$RECONFIGURE" == "Y" ]]; then 26 | return 0 27 | elif [[ "$RECONFIGURE" == "n" || "$RECONFIGURE" == "N" ]]; then 28 | info "$(get_string "warp_native_skip_installation")" 29 | read -n 1 -s -r -p "$(get_string "warp_native_press_key")" 30 | exit 0 31 | else 32 | warn "$(get_string "warp_native_please_enter_yn")" 33 | fi 34 | done 35 | fi 36 | return 0 37 | } 38 | 39 | uninstall_warp_native() { 40 | info "$(get_string "warp_native_stopping_warp")" 41 | 42 | if ip link show warp &>/dev/null; then 43 | wg-quick down warp &>/dev/null || true 44 | fi 45 | 46 | systemctl disable wg-quick@warp &>/dev/null || true 47 | 48 | rm -f /etc/wireguard/warp.conf &>/dev/null 49 | rm -rf /etc/wireguard &>/dev/null 50 | rm -f /usr/local/bin/wgcf &>/dev/null 51 | rm -f wgcf-account.toml wgcf-profile.conf &>/dev/null 52 | 53 | info "$(get_string "warp_native_removing_packages")" 54 | DEBIAN_FRONTEND=noninteractive apt remove --purge -y wireguard &>/dev/null || true 55 | DEBIAN_FRONTEND=noninteractive apt autoremove -y &>/dev/null || true 56 | 57 | success "$(get_string "warp_native_uninstall_complete")" 58 | } 59 | 60 | install_warp_native() { 61 | info "$(get_string "warp_native_start_install")" 62 | echo "" 63 | 64 | info "$(get_string "warp_native_install_wireguard")" 65 | apt update -qq &>/dev/null || { 66 | error "$(get_string "warp_native_update_failed")" 67 | exit 1 68 | } 69 | apt install wireguard -y &>/dev/null || { 70 | error "$(get_string "warp_native_wireguard_failed")" 71 | exit 1 72 | } 73 | success "$(get_string "warp_native_wireguard_ok")" 74 | echo "" 75 | 76 | info "$(get_string "warp_native_temp_dns")" 77 | cp /etc/resolv.conf /etc/resolv.conf.backup 78 | RESTORE_DNS_REQUIRED=true 79 | echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" > /etc/resolv.conf || { 80 | error "$(get_string "warp_native_dns_failed")" 81 | exit 1 82 | } 83 | success "$(get_string "warp_native_dns_ok")" 84 | echo "" 85 | 86 | info "$(get_string "warp_native_download_wgcf")" 87 | WGCF_RELEASE_URL="https://api.github.com/repos/ViRb3/wgcf/releases/latest" 88 | WGCF_VERSION=$(curl -s "$WGCF_RELEASE_URL" | grep tag_name | cut -d '"' -f 4) 89 | 90 | if [ -z "$WGCF_VERSION" ]; then 91 | error "$(get_string "warp_native_wgcf_version_failed")" 92 | exit 1 93 | fi 94 | 95 | ARCH=$(uname -m) 96 | case $ARCH in 97 | x86_64) WGCF_ARCH="amd64" ;; 98 | aarch64|arm64) WGCF_ARCH="arm64" ;; 99 | armv7l) WGCF_ARCH="armv7" ;; 100 | *) WGCF_ARCH="amd64" ;; 101 | esac 102 | 103 | info "$(get_string "warp_native_arch_detected") $ARCH -> $WGCF_ARCH" 104 | 105 | WGCF_DOWNLOAD_URL="https://github.com/ViRb3/wgcf/releases/download/${WGCF_VERSION}/wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}" 106 | WGCF_BINARY_NAME="wgcf_${WGCF_VERSION#v}_linux_${WGCF_ARCH}" 107 | 108 | wget -q "$WGCF_DOWNLOAD_URL" -O "$WGCF_BINARY_NAME" || { 109 | error "$(get_string "warp_native_wgcf_download_failed")" 110 | exit 1 111 | } 112 | 113 | chmod +x "$WGCF_BINARY_NAME" || { 114 | error "$(get_string "warp_native_wgcf_chmod_failed")" 115 | exit 1 116 | } 117 | mv "$WGCF_BINARY_NAME" /usr/local/bin/wgcf || { 118 | error "$(get_string "warp_native_wgcf_move_failed")" 119 | exit 1 120 | } 121 | success "wgcf $WGCF_VERSION $(get_string "warp_native_wgcf_installed")" 122 | echo "" 123 | 124 | info "$(get_string "warp_native_register_wgcf")" 125 | 126 | if [[ -f wgcf-account.toml ]]; then 127 | info "$(get_string "warp_native_account_exists")" 128 | else 129 | info "$(get_string "warp_native_registering")" 130 | 131 | info "$(get_string "warp_native_wgcf_binary_check")" 132 | if ! wgcf --help &>/dev/null; then 133 | warn "$(get_string "warp_native_wgcf_not_executable")" 134 | chmod +x /usr/local/bin/wgcf 135 | if ! wgcf --help &>/dev/null; then 136 | error "$(get_string "warp_native_wgcf_not_executable")" 137 | exit 1 138 | fi 139 | fi 140 | 141 | output=$(timeout 60 bash -c 'yes | wgcf register' 2>&1) 142 | ret=$? 143 | 144 | if [[ $ret -ne 0 ]]; then 145 | warn "$(get_string "warp_native_register_error") $ret." 146 | 147 | if [[ $ret -eq 126 ]]; then 148 | warn "$(get_string "warp_native_wgcf_not_executable")" 149 | elif [[ $ret -eq 124 ]]; then 150 | warn "Registration timed out after 60 seconds." 151 | elif [[ "$output" == *"500 Internal Server Error"* ]]; then 152 | warn "$(get_string "warp_native_cf_500_detected")" 153 | info "$(get_string "warp_native_known_behavior")" 154 | elif [[ "$output" == *"429"* || "$output" == *"Too Many Requests"* ]]; then 155 | warn "$(get_string "warp_native_cf_rate_limited")" 156 | elif [[ "$output" == *"403"* || "$output" == *"Forbidden"* ]]; then 157 | warn "$(get_string "warp_native_cf_forbidden")" 158 | elif [[ "$output" == *"network"* || "$output" == *"connection"* ]]; then 159 | warn "$(get_string "warp_native_network_issue")" 160 | else 161 | warn "$(get_string "warp_native_unknown_error")" 162 | echo "$output" 163 | fi 164 | 165 | info "$(get_string "warp_native_trying_alternative")" 166 | echo | wgcf register &>/dev/null || true 167 | 168 | sleep 2 169 | fi 170 | 171 | if [[ ! -f wgcf-account.toml ]]; then 172 | error "$(get_string "warp_native_registration_failed")" 173 | exit 1 174 | fi 175 | 176 | success "$(get_string "warp_native_account_created")" 177 | fi 178 | 179 | wgcf generate &>/dev/null || { 180 | error "$(get_string "warp_native_config_gen_failed")" 181 | exit 1 182 | } 183 | success "$(get_string "warp_native_config_generated")" 184 | echo "" 185 | 186 | info "$(get_string "warp_native_edit_config")" 187 | WGCF_CONF_FILE="wgcf-profile.conf" 188 | 189 | if [ ! -f "$WGCF_CONF_FILE" ]; then 190 | error "$(get_string "warp_native_config_not_found" | sed "s/не найден/Файл $WGCF_CONF_FILE не найден/" | sed "s/not found/File $WGCF_CONF_FILE not found/")" 191 | exit 1 192 | fi 193 | 194 | sed -i '/^DNS =/d' "$WGCF_CONF_FILE" || { 195 | error "$(get_string "warp_native_dns_removed")" 196 | exit 1 197 | } 198 | 199 | if ! grep -q "Table = off" "$WGCF_CONF_FILE"; then 200 | sed -i '/^MTU =/aTable = off' "$WGCF_CONF_FILE" || { 201 | error "$(get_string "warp_native_table_off_failed")" 202 | exit 1 203 | } 204 | fi 205 | 206 | if ! grep -q "PersistentKeepalive = 25" "$WGCF_CONF_FILE"; then 207 | sed -i '/^Endpoint =/aPersistentKeepalive = 25' "$WGCF_CONF_FILE" || { 208 | error "$(get_string "warp_native_keepalive_failed")" 209 | exit 1 210 | } 211 | fi 212 | 213 | mkdir -p /etc/wireguard || { 214 | error "$(get_string "warp_native_wireguard_dir_failed")" 215 | exit 1 216 | } 217 | mv "$WGCF_CONF_FILE" /etc/wireguard/warp.conf || { 218 | error "$(get_string "warp_native_config_move_failed")" 219 | exit 1 220 | } 221 | success "$(get_string "warp_native_config_saved")" 222 | echo "" 223 | 224 | info "$(get_string "warp_native_check_ipv6")" 225 | 226 | is_ipv6_enabled() { 227 | sysctl net.ipv6.conf.all.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1 228 | sysctl net.ipv6.conf.default.disable_ipv6 2>/dev/null | grep -q ' = 0' || return 1 229 | ip -6 addr show scope global | grep -qv 'inet6 .*fe80::' || return 1 230 | return 0 231 | } 232 | 233 | if is_ipv6_enabled; then 234 | success "$(get_string "warp_native_ipv6_enabled")" 235 | else 236 | warn "$(get_string "warp_native_ipv6_disabled")" 237 | sed -i 's/,\s*[0-9a-fA-F:]\+\/128//' /etc/wireguard/warp.conf 238 | sed -i '/Address = [0-9a-fA-F:]\+\/128/d' /etc/wireguard/warp.conf 239 | success "$(get_string "warp_native_ipv6_removed")" 240 | fi 241 | echo "" 242 | 243 | info "$(get_string "warp_native_connect_warp")" 244 | systemctl start wg-quick@warp &>/dev/null || { 245 | error "$(get_string "warp_native_connect_failed")" 246 | exit 1 247 | } 248 | success "$(get_string "warp_native_warp_connected")" 249 | echo "" 250 | 251 | info "$(get_string "warp_native_check_status")" 252 | 253 | if ! wg show warp &>/dev/null; then 254 | error "$(get_string "warp_native_warp_not_found")" 255 | exit 1 256 | fi 257 | 258 | for i in {1..10}; do 259 | handshake=$(wg show warp | grep "latest handshake" | awk -F': ' '{print $2}') 260 | if [[ "$handshake" == *"second"* || "$handshake" == *"minute"* ]]; then 261 | success "$(get_string "warp_native_handshake_received") $handshake" 262 | success "$(get_string "warp_native_warp_active")" 263 | break 264 | fi 265 | sleep 1 266 | done 267 | 268 | if [[ -z "$handshake" || "$handshake" == "0 seconds ago" ]]; then 269 | warn "$(get_string "warp_native_handshake_failed")" 270 | fi 271 | 272 | curl_result=$(curl -s --interface warp https://www.cloudflare.com/cdn-cgi/trace | grep "warp=" | cut -d= -f2) 273 | 274 | if [[ "$curl_result" == "on" ]]; then 275 | success "$(get_string "warp_native_cf_response")" 276 | else 277 | warn "$(get_string "warp_native_cf_not_confirmed")" 278 | fi 279 | echo "" 280 | 281 | info "$(get_string "warp_native_enable_autostart")" 282 | systemctl enable wg-quick@warp &>/dev/null || { 283 | error "$(get_string "warp_native_autostart_failed")" 284 | exit 1 285 | } 286 | success "$(get_string "warp_native_autostart_enabled")" 287 | echo "" 288 | 289 | restore_dns 290 | success "$(get_string "warp_native_installation_complete")" 291 | echo "" 292 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_check_service"):${RESET} systemctl status wg-quick@warp" 293 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_show_info"):${RESET} wg show warp" 294 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_stop_interface"):${RESET} systemctl stop wg-quick@warp" 295 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_start_interface"):${RESET} systemctl start wg-quick@warp" 296 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_restart_interface"):${RESET} systemctl restart wg-quick@warp" 297 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_disable_autostart"):${RESET} systemctl disable wg-quick@warp" 298 | echo -e "${BOLD_CYAN}➤ $(get_string "warp_native_enable_autostart_cmd"):${RESET} systemctl enable wg-quick@warp" 299 | echo "" 300 | } 301 | 302 | main() { 303 | if ! check_warp_native; then 304 | return 0 305 | fi 306 | 307 | if command -v wgcf >/dev/null 2>&1 && [ -f "/etc/wireguard/warp.conf" ]; then 308 | uninstall_warp_native 309 | echo "" 310 | fi 311 | 312 | install_warp_native 313 | read -n 1 -s -r -p "$(get_string "warp_native_press_key")" 314 | exit 0 315 | } 316 | 317 | main 318 | -------------------------------------------------------------------------------- /scripts/remnanode/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | update_panel() { 8 | info "$(get_string "update_node_updating")" 9 | cd /opt/remnanode 10 | docker compose pull 11 | docker compose up -d 12 | } 13 | 14 | main() { 15 | update_panel 16 | success "$(get_string "update_node_complete")" 17 | read -n 1 -s -r -p "$(get_string "update_node_press_key")" 18 | exit 0 19 | } 20 | 21 | main 22 | -------------------------------------------------------------------------------- /scripts/remnawave/install-caddy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | REINSTALL_CADDY=false 8 | 9 | check_component() { 10 | if [ -f "/opt/remnawave/caddy/docker-compose.yml" ] || [ -f "/opt/remnawave/caddy/Caddyfile" ]; then 11 | info "$(get_string "install_caddy_detected")" 12 | while true; do 13 | question "$(get_string "install_caddy_reinstall")" 14 | REINSTALL="$REPLY" 15 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 16 | warn "$(get_string "install_caddy_stopping")" 17 | if [ -f "/opt/remnawave/caddy/docker-compose.yml" ]; then 18 | cd /opt/remnawave/caddy && docker compose down 19 | fi 20 | if docker ps -a --format '{{.Names}}' | grep -q "remnawave-caddy\|caddy"; then 21 | if [ "$NEED_PROTECTION" = "y" ]; then 22 | docker rmi remnawave/caddy-with-auth:latest 2>/dev/null || true 23 | else 24 | docker rmi caddy:2.9 2>/dev/null || true 25 | fi 26 | fi 27 | rm -f /opt/remnawave/caddy/Caddyfile 28 | rm -f /opt/remnawave/caddy/docker-compose.yml 29 | REINSTALL_CADDY=true 30 | break 31 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then 32 | info "$(get_string "install_caddy_reinstall_denied")" 33 | read -n 1 -s -r -p "$(get_string "install_caddy_press_key")" 34 | exit 0 35 | else 36 | warn "$(get_string "install_caddy_please_enter_yn")" 37 | fi 38 | done 39 | else 40 | REINSTALL_CADDY=true 41 | fi 42 | } 43 | 44 | install_docker() { 45 | if ! command -v docker &> /dev/null; then 46 | info "$(get_string "install_caddy_installing")" 47 | sudo curl -fsSL https://get.docker.com | sh 48 | fi 49 | } 50 | 51 | install_without_protection() { 52 | if [ "$REINSTALL_CADDY" = true ]; then 53 | info "$(get_string "install_caddy_installing")" 54 | mkdir -p /opt/remnawave/caddy 55 | cd /opt/remnawave/caddy 56 | 57 | cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile 58 | cp "/opt/remnasetup/data/docker/caddy-compose.yml" docker-compose.yml 59 | 60 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" Caddyfile 61 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile 62 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile 63 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile 64 | 65 | cd /opt/remnawave 66 | if [ -f ".env" ]; then 67 | sed -i "s|PANEL_DOMAIN=.*|PANEL_DOMAIN=$PANEL_DOMAIN|g" .env 68 | sed -i "s|SUB_DOMAIN=.*|SUB_DOMAIN=$SUB_DOMAIN|g" .env 69 | sed -i "s|PANEL_PORT=.*|PANEL_PORT=$PANEL_PORT|g" .env 70 | fi 71 | 72 | cd /opt/remnawave/subscription 73 | if [ -f "docker-compose.yml" ]; then 74 | sed -i "s|PANEL_DOMAIN=.*|PANEL_DOMAIN=$PANEL_DOMAIN|g" docker-compose.yml 75 | sed -i "s|SUB_PORT=.*|SUB_PORT=$SUB_PORT|g" docker-compose.yml 76 | fi 77 | 78 | cd /opt/remnawave && docker compose down && docker compose up -d 79 | cd /opt/remnawave/subscription && docker compose down && docker compose up -d 80 | cd /opt/remnawave/caddy && docker compose up -d 81 | fi 82 | } 83 | 84 | install_with_protection() { 85 | if [ "$REINSTALL_CADDY" = true ]; then 86 | info "$(get_string "install_caddy_installing")" 87 | mkdir -p /opt/remnawave/caddy 88 | cd /opt/remnawave/caddy 89 | 90 | cp "/opt/remnasetup/data/caddy/caddyfile-protection" Caddyfile 91 | cp "/opt/remnasetup/data/docker/caddy-protection-compose.yml" docker-compose.yml 92 | 93 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml 94 | sed -i "s|\$CUSTOM_LOGIN_ROUTE|$CUSTOM_LOGIN_ROUTE|g" docker-compose.yml 95 | sed -i "s|\$LOGIN_USERNAME|$LOGIN_USERNAME|g" docker-compose.yml 96 | sed -i "s|\$LOGIN_EMAIL|$LOGIN_EMAIL|g" docker-compose.yml 97 | sed -i "s|\$LOGIN_PASSWORD|$LOGIN_PASSWORD|g" docker-compose.yml 98 | 99 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile 100 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile 101 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile 102 | 103 | cd /opt/remnawave 104 | if [ -f ".env" ]; then 105 | sed -i "s|PANEL_DOMAIN=.*|PANEL_DOMAIN=$PANEL_DOMAIN|g" .env 106 | sed -i "s|SUB_DOMAIN=.*|SUB_DOMAIN=$SUB_DOMAIN|g" .env 107 | sed -i "s|PANEL_PORT=.*|PANEL_PORT=$PANEL_PORT|g" .env 108 | fi 109 | 110 | cd /opt/remnawave/subscription 111 | docker compose down 112 | rm -f docker-compose.yml 113 | cp "/opt/remnasetup/data/docker/subscription-protection-compose.yml" docker-compose.yml 114 | 115 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml 116 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml 117 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml 118 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml 119 | 120 | cd /opt/remnawave && docker compose down && docker compose up -d 121 | cd /opt/remnawave/subscription && docker compose up -d 122 | cd /opt/remnawave/caddy && docker compose up -d 123 | fi 124 | } 125 | 126 | check_docker() { 127 | if command -v docker >/dev/null 2>&1; then 128 | info "$(get_string "install_caddy_detected")" 129 | return 0 130 | else 131 | return 1 132 | fi 133 | } 134 | 135 | main() { 136 | check_component 137 | 138 | while true; do 139 | question "$(get_string "install_caddy_need_protection")" 140 | NEED_PROTECTION="$REPLY" 141 | if [[ "$NEED_PROTECTION" == "y" || "$NEED_PROTECTION" == "Y" || "$NEED_PROTECTION" == "n" || "$NEED_PROTECTION" == "N" ]]; then 142 | break 143 | fi 144 | warn "$(get_string "install_caddy_please_enter_yn")" 145 | done 146 | 147 | while true; do 148 | question "$(get_string "install_caddy_enter_panel_domain")" 149 | PANEL_DOMAIN="$REPLY" 150 | if [[ -n "$PANEL_DOMAIN" ]]; then 151 | break 152 | fi 153 | warn "$(get_string "install_caddy_domain_empty")" 154 | done 155 | 156 | while true; do 157 | question "$(get_string "install_caddy_enter_sub_domain")" 158 | SUB_DOMAIN="$REPLY" 159 | if [[ -n "$SUB_DOMAIN" ]]; then 160 | break 161 | fi 162 | warn "$(get_string "install_caddy_domain_empty")" 163 | done 164 | 165 | question "$(get_string "install_caddy_enter_panel_port")" 166 | PANEL_PORT="$REPLY" 167 | PANEL_PORT=${PANEL_PORT:-3000} 168 | 169 | question "$(get_string "install_caddy_enter_sub_port")" 170 | SUB_PORT="$REPLY" 171 | SUB_PORT=${SUB_PORT:-3010} 172 | 173 | if [ "$NEED_PROTECTION" = "y" ]; then 174 | while true; do 175 | question "$(get_string "install_caddy_enter_project_name")" 176 | PROJECT_NAME="$REPLY" 177 | if [[ -n "$PROJECT_NAME" ]]; then 178 | break 179 | fi 180 | warn "$(get_string "install_caddy_project_name_empty")" 181 | done 182 | 183 | while true; do 184 | question "$(get_string "install_caddy_enter_project_description")" 185 | PROJECT_DESCRIPTION="$REPLY" 186 | if [[ -n "$PROJECT_DESCRIPTION" ]]; then 187 | break 188 | fi 189 | warn "$(get_string "install_caddy_project_description_empty")" 190 | done 191 | 192 | while true; do 193 | question "$(get_string "install_caddy_enter_login_route")" 194 | CUSTOM_LOGIN_ROUTE="$REPLY" 195 | if [[ -n "$CUSTOM_LOGIN_ROUTE" ]]; then 196 | break 197 | fi 198 | warn "$(get_string "install_caddy_login_route_empty")" 199 | done 200 | 201 | while true; do 202 | question "$(get_string "install_caddy_enter_admin_login")" 203 | LOGIN_USERNAME="$REPLY" 204 | if [[ -n "$LOGIN_USERNAME" ]]; then 205 | break 206 | fi 207 | warn "$(get_string "install_caddy_admin_login_empty")" 208 | done 209 | 210 | while true; do 211 | question "$(get_string "install_caddy_enter_admin_email")" 212 | LOGIN_EMAIL="$REPLY" 213 | if [[ -n "$LOGIN_EMAIL" ]]; then 214 | break 215 | fi 216 | warn "$(get_string "install_caddy_admin_email_empty")" 217 | done 218 | 219 | while true; do 220 | question "$(get_string "install_caddy_enter_admin_password")" 221 | LOGIN_PASSWORD="$REPLY" 222 | if [[ ${#LOGIN_PASSWORD} -lt 8 ]]; then 223 | warn "$(get_string "install_caddy_password_short")" 224 | continue 225 | fi 226 | if ! [[ "$LOGIN_PASSWORD" =~ [A-Z] ]]; then 227 | warn "$(get_string "install_caddy_password_uppercase")" 228 | continue 229 | fi 230 | if ! [[ "$LOGIN_PASSWORD" =~ [a-z] ]]; then 231 | warn "$(get_string "install_caddy_password_lowercase")" 232 | continue 233 | fi 234 | if ! [[ "$LOGIN_PASSWORD" =~ [0-9] ]]; then 235 | warn "$(get_string "install_caddy_password_number")" 236 | continue 237 | fi 238 | if ! [[ "$LOGIN_PASSWORD" =~ [^a-zA-Z0-9] ]]; then 239 | warn "$(get_string "install_caddy_password_special")" 240 | continue 241 | fi 242 | break 243 | done 244 | fi 245 | 246 | if ! check_docker; then 247 | install_docker 248 | fi 249 | if [ "$NEED_PROTECTION" = "y" ]; then 250 | install_with_protection 251 | else 252 | install_without_protection 253 | fi 254 | 255 | success "$(get_string "install_caddy_complete")" 256 | read -n 1 -s -r -p "$(get_string "install_caddy_press_key")" 257 | exit 0 258 | } 259 | 260 | main 261 | -------------------------------------------------------------------------------- /scripts/remnawave/install-full.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | REINSTALL_PANEL=false 8 | REINSTALL_SUBSCRIPTION=false 9 | REINSTALL_CADDY=false 10 | 11 | check_component() { 12 | local component=$1 13 | local path=$2 14 | local env_file=$3 15 | 16 | case $component in 17 | "panel") 18 | if [ -f "$path/docker-compose.yml" ] && (cd "$path" && docker compose ps -q | grep -q "remnawave") || [ -f "$env_file" ]; then 19 | info "$(get_string "install_full_detected")" 20 | while true; do 21 | question "$(get_string "install_full_reinstall")" 22 | REINSTALL="$REPLY" 23 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 24 | warn "$(get_string "install_full_stopping")" 25 | cd "$path" && docker compose down 26 | docker rmi remnawave/panel:latest 2>/dev/null || true 27 | docker rmi remnawave/redis:latest 2>/dev/null || true 28 | docker rmi remnawave/postgres:latest 2>/dev/null || true 29 | docker volume rm remnawave-db-data remnawave-redis-data 2>/dev/null || true 30 | rm -f "$env_file" 31 | rm -f "$path/docker-compose.yml" 32 | REINSTALL_PANEL=true 33 | break 34 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then 35 | info "$(get_string "install_full_reinstall_denied")" 36 | REINSTALL_PANEL=false 37 | break 38 | else 39 | warn "$(get_string "install_full_please_enter_yn")" 40 | fi 41 | done 42 | else 43 | REINSTALL_PANEL=true 44 | fi 45 | ;; 46 | "subscription") 47 | if [ -f "$path/docker-compose.yml" ] && (cd "$path" && docker compose ps -q | grep -q "remnawave-subscription-page") || [ -f "$path/app-config.json" ]; then 48 | info "$(get_string "install_full_subscription_detected")" 49 | while true; do 50 | question "$(get_string "install_full_subscription_reinstall")" 51 | REINSTALL="$REPLY" 52 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 53 | warn "$(get_string "install_full_stopping")" 54 | cd "$path" && docker compose down 55 | docker rmi remnawave/subscription-page:latest 2>/dev/null || true 56 | rm -f "$path/app-config.json" 57 | rm -f "$path/docker-compose.yml" 58 | REINSTALL_SUBSCRIPTION=true 59 | break 60 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then 61 | info "$(get_string "install_full_subscription_reinstall_denied")" 62 | REINSTALL_SUBSCRIPTION=false 63 | break 64 | else 65 | warn "$(get_string "install_full_please_enter_yn")" 66 | fi 67 | done 68 | else 69 | REINSTALL_SUBSCRIPTION=true 70 | fi 71 | ;; 72 | "caddy") 73 | if [ -f "$path/docker-compose.yml" ] || [ -f "$path/Caddyfile" ]; then 74 | info "$(get_string "install_full_caddy_detected")" 75 | while true; do 76 | question "$(get_string "install_full_caddy_reinstall")" 77 | REINSTALL="$REPLY" 78 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 79 | warn "$(get_string "install_full_stopping")" 80 | if [ -f "$path/docker-compose.yml" ]; then 81 | cd "$path" && docker compose down 82 | fi 83 | if docker ps -a --format '{{.Names}}' | grep -q "remnawave-caddy\|caddy"; then 84 | if [ "$NEED_PROTECTION" = "y" ]; then 85 | docker rmi remnawave/caddy-with-auth:latest 2>/dev/null || true 86 | else 87 | docker rmi caddy:2.9 2>/dev/null || true 88 | fi 89 | fi 90 | rm -f "$path/Caddyfile" 91 | rm -f "$path/docker-compose.yml" 92 | REINSTALL_CADDY=true 93 | break 94 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then 95 | info "$(get_string "install_full_caddy_reinstall_denied")" 96 | REINSTALL_CADDY=false 97 | break 98 | else 99 | warn "$(get_string "install_full_please_enter_yn")" 100 | fi 101 | done 102 | else 103 | REINSTALL_CADDY=true 104 | fi 105 | ;; 106 | esac 107 | } 108 | 109 | install_docker() { 110 | if ! command -v docker &> /dev/null; then 111 | info "$(get_string "install_full_installing_docker")" 112 | sudo curl -fsSL https://get.docker.com | sh 113 | fi 114 | } 115 | 116 | generate_64() { 117 | openssl rand -hex 64 118 | } 119 | 120 | generate_24() { 121 | openssl rand -hex 24 122 | } 123 | 124 | generate_login() { 125 | tr -dc 'a-zA-Z' < /dev/urandom | head -c 15 126 | } 127 | 128 | 129 | 130 | install_without_protection() { 131 | if [ "$REINSTALL_PANEL" = true ]; then 132 | info "$(get_string "install_full_installing")" 133 | mkdir -p /opt/remnawave 134 | cd /opt/remnawave 135 | 136 | cp "/opt/remnasetup/data/docker/panel.env" .env 137 | cp "/opt/remnasetup/data/docker/panel-compose.yml" docker-compose.yml 138 | 139 | JWT_AUTH_SECRET=$(generate_64) 140 | JWT_API_TOKENS_SECRET=$(generate_64) 141 | METRICS_USER=$(generate_login) 142 | METRICS_PASS=$(generate_64) 143 | WEBHOOK_SECRET_HEADER=$(generate_64) 144 | DB_USER=$(generate_login) 145 | DB_PASSWORD=$(generate_24) 146 | 147 | export DB_USER 148 | export DB_PASSWORD 149 | 150 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" .env 151 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" .env 152 | sed -i "s|\$METRICS_USER|$METRICS_USER|g" .env 153 | sed -i "s|\$METRICS_PASS|$METRICS_PASS|g" .env 154 | sed -i "s|\$DB_USER|$DB_USER|g" .env 155 | sed -i "s|\$DB_PASSWORD|$DB_PASSWORD|g" .env 156 | sed -i "s|\$JWT_AUTH_SECRET|$JWT_AUTH_SECRET|g" .env 157 | sed -i "s|\$JWT_API_TOKENS_SECRET|$JWT_API_TOKENS_SECRET|g" .env 158 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" .env 159 | sed -i "s|\$WEBHOOK_SECRET_HEADER|$WEBHOOK_SECRET_HEADER|g" .env 160 | 161 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml 162 | 163 | docker compose up -d 164 | fi 165 | 166 | if [ "$REINSTALL_SUBSCRIPTION" = true ]; then 167 | info "$(get_string "install_full_installing_subscription")" 168 | mkdir -p /opt/remnawave/subscription 169 | cd /opt/remnawave/subscription 170 | 171 | cp "/opt/remnasetup/data/app-config.json" app-config.json 172 | cp "/opt/remnasetup/data/docker/subscription-compose.yml" docker-compose.yml 173 | 174 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml 175 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml 176 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml 177 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml 178 | 179 | docker compose up -d 180 | fi 181 | 182 | if [ "$REINSTALL_CADDY" = true ]; then 183 | info "$(get_string "install_full_installing_caddy")" 184 | mkdir -p /opt/remnawave/caddy 185 | cd /opt/remnawave/caddy 186 | 187 | cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile 188 | cp "/opt/remnasetup/data/docker/caddy-compose.yml" docker-compose.yml 189 | 190 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" Caddyfile 191 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile 192 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile 193 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile 194 | 195 | docker compose up -d 196 | fi 197 | } 198 | 199 | install_with_protection() { 200 | if [ "$REINSTALL_PANEL" = true ]; then 201 | info "$(get_string "install_full_installing_with_protection")" 202 | mkdir -p /opt/remnawave 203 | cd /opt/remnawave 204 | 205 | cp "/opt/remnasetup/data/docker/panel.env" .env 206 | cp "/opt/remnasetup/data/docker/panel-compose.yml" docker-compose.yml 207 | 208 | JWT_AUTH_SECRET=$(generate_64) 209 | JWT_API_TOKENS_SECRET=$(generate_64) 210 | METRICS_USER=$(generate_login) 211 | METRICS_PASS=$(generate_64) 212 | WEBHOOK_SECRET_HEADER=$(generate_64) 213 | DB_USER=$(generate_login) 214 | DB_PASSWORD=$(generate_24) 215 | 216 | export DB_USER 217 | export DB_PASSWORD 218 | 219 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" .env 220 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" .env 221 | sed -i "s|\$METRICS_USER|$METRICS_USER|g" .env 222 | sed -i "s|\$METRICS_PASS|$METRICS_PASS|g" .env 223 | sed -i "s|\$DB_USER|$DB_USER|g" .env 224 | sed -i "s|\$DB_PASSWORD|$DB_PASSWORD|g" .env 225 | sed -i "s|\$JWT_AUTH_SECRET|$JWT_AUTH_SECRET|g" .env 226 | sed -i "s|\$JWT_API_TOKENS_SECRET|$JWT_API_TOKENS_SECRET|g" .env 227 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" .env 228 | sed -i "s|\$WEBHOOK_SECRET_HEADER|$WEBHOOK_SECRET_HEADER|g" .env 229 | 230 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml 231 | 232 | docker compose up -d 233 | fi 234 | 235 | if [ "$REINSTALL_SUBSCRIPTION" = true ]; then 236 | info "$(get_string "install_full_installing_subscription_with_protection")" 237 | mkdir -p /opt/remnawave/subscription 238 | cd /opt/remnawave/subscription 239 | 240 | cp "/opt/remnasetup/data/app-config.json" app-config.json 241 | cp "/opt/remnasetup/data/docker/subscription-protection-compose.yml" docker-compose.yml 242 | 243 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml 244 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml 245 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml 246 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml 247 | 248 | docker compose up -d 249 | fi 250 | 251 | if [ "$REINSTALL_CADDY" = true ]; then 252 | info "$(get_string "install_full_installing_caddy_with_protection")" 253 | mkdir -p /opt/remnawave/caddy 254 | cd /opt/remnawave/caddy 255 | 256 | cp "/opt/remnasetup/data/caddy/caddyfile-protection" Caddyfile 257 | cp "/opt/remnasetup/data/docker/caddy-protection-compose.yml" docker-compose.yml 258 | 259 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" Caddyfile 260 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" Caddyfile 261 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" Caddyfile 262 | 263 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml 264 | sed -i "s|\$CUSTOM_LOGIN_ROUTE|$CUSTOM_LOGIN_ROUTE|g" docker-compose.yml 265 | sed -i "s|\$LOGIN_USERNAME|$LOGIN_USERNAME|g" docker-compose.yml 266 | sed -i "s|\$LOGIN_EMAIL|$LOGIN_EMAIL|g" docker-compose.yml 267 | sed -i "s|\$LOGIN_PASSWORD|$LOGIN_PASSWORD|g" docker-compose.yml 268 | 269 | docker compose up -d 270 | fi 271 | } 272 | 273 | check_docker() { 274 | if command -v docker >/dev/null 2>&1; then 275 | info "$(get_string "install_full_docker_already_installed")" 276 | return 0 277 | else 278 | return 1 279 | fi 280 | } 281 | 282 | 283 | 284 | show_panel_info() { 285 | echo "" 286 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 287 | echo -e "${BOLD_CYAN}$(get_string "install_full_panel_info_header")${RESET}" 288 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 289 | 290 | if [ "$NEED_PROTECTION" = "y" ]; then 291 | echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}/${CUSTOM_LOGIN_ROUTE}${RESET}" 292 | else 293 | echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}${RESET}" 294 | fi 295 | 296 | if [ "$NEED_PROTECTION" = "y" ]; then 297 | echo -e "${BOLD_GREEN}$(get_string "install_full_email")${RESET} ${YELLOW}${LOGIN_EMAIL}${RESET}" 298 | echo -e "${BOLD_GREEN}$(get_string "install_full_username")${RESET} ${YELLOW}${LOGIN_USERNAME}${RESET}" 299 | echo -e "${BOLD_GREEN}$(get_string "install_full_password")${RESET} ${YELLOW}${LOGIN_PASSWORD}${RESET}" 300 | fi 301 | 302 | echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" 303 | echo "" 304 | } 305 | 306 | main() { 307 | check_component "panel" "/opt/remnawave" "/opt/remnawave/.env" 308 | check_component "subscription" "/opt/remnawave/subscription" "/opt/remnawave/subscription/.env" 309 | check_component "caddy" "/opt/remnawave/caddy" "/opt/remnawave/caddy/.env" 310 | 311 | if [ "$REINSTALL_PANEL" = false ] && [ "$REINSTALL_SUBSCRIPTION" = false ] && [ "$REINSTALL_CADDY" = false ]; then 312 | info "$(get_string "install_full_no_components")" 313 | read -n 1 -s -r -p "$(get_string "install_full_press_key")" 314 | exit 0 315 | fi 316 | 317 | while true; do 318 | question "$(get_string "install_full_need_protection")" 319 | NEED_PROTECTION="$REPLY" 320 | if [[ "$NEED_PROTECTION" == "y" || "$NEED_PROTECTION" == "Y" ]]; then 321 | break 322 | elif [[ "$NEED_PROTECTION" == "n" || "$NEED_PROTECTION" == "N" ]]; then 323 | break 324 | else 325 | warn "$(get_string "install_full_please_enter_yn")" 326 | fi 327 | done 328 | 329 | while true; do 330 | question "$(get_string "install_full_enter_panel_domain")" 331 | PANEL_DOMAIN="$REPLY" 332 | if [[ -n "$PANEL_DOMAIN" ]]; then 333 | break 334 | fi 335 | warn "$(get_string "install_full_domain_empty")" 336 | done 337 | 338 | while true; do 339 | question "$(get_string "install_full_enter_sub_domain")" 340 | SUB_DOMAIN="$REPLY" 341 | if [[ -n "$SUB_DOMAIN" ]]; then 342 | break 343 | fi 344 | warn "$(get_string "install_full_domain_empty")" 345 | done 346 | 347 | question "$(get_string "install_full_enter_panel_port")" 348 | PANEL_PORT="$REPLY" 349 | PANEL_PORT=${PANEL_PORT:-3000} 350 | 351 | question "$(get_string "install_full_enter_sub_port")" 352 | SUB_PORT="$REPLY" 353 | SUB_PORT=${SUB_PORT:-3010} 354 | 355 | while true; do 356 | question "$(get_string "install_full_enter_project_name")" 357 | PROJECT_NAME="$REPLY" 358 | if [[ -n "$PROJECT_NAME" ]]; then 359 | break 360 | fi 361 | warn "$(get_string "install_full_project_name_empty")" 362 | done 363 | 364 | while true; do 365 | question "$(get_string "install_full_enter_project_description")" 366 | PROJECT_DESCRIPTION="$REPLY" 367 | if [[ -n "$PROJECT_DESCRIPTION" ]]; then 368 | break 369 | fi 370 | warn "$(get_string "install_full_project_description_empty")" 371 | done 372 | 373 | if [ "$NEED_PROTECTION" = "y" ]; then 374 | while true; do 375 | question "$(get_string "install_full_enter_login_route")" 376 | CUSTOM_LOGIN_ROUTE="$REPLY" 377 | if [[ -n "$CUSTOM_LOGIN_ROUTE" ]]; then 378 | break 379 | fi 380 | warn "$(get_string "install_full_login_route_empty")" 381 | done 382 | 383 | while true; do 384 | question "$(get_string "install_full_enter_admin_login")" 385 | LOGIN_USERNAME="$REPLY" 386 | if [[ -n "$LOGIN_USERNAME" ]]; then 387 | break 388 | fi 389 | warn "$(get_string "install_full_admin_login_empty")" 390 | done 391 | 392 | while true; do 393 | question "$(get_string "install_full_enter_admin_email")" 394 | LOGIN_EMAIL="$REPLY" 395 | if [[ -n "$LOGIN_EMAIL" ]]; then 396 | break 397 | fi 398 | warn "$(get_string "install_full_admin_email_empty")" 399 | done 400 | 401 | while true; do 402 | question "$(get_string "install_full_enter_admin_password")" 403 | LOGIN_PASSWORD="$REPLY" 404 | if [[ ${#LOGIN_PASSWORD} -lt 8 ]]; then 405 | warn "$(get_string "install_full_password_short")" 406 | continue 407 | fi 408 | if ! [[ "$LOGIN_PASSWORD" =~ [A-Z] ]]; then 409 | warn "$(get_string "install_full_password_uppercase")" 410 | continue 411 | fi 412 | if ! [[ "$LOGIN_PASSWORD" =~ [a-z] ]]; then 413 | warn "$(get_string "install_full_password_lowercase")" 414 | continue 415 | fi 416 | if ! [[ "$LOGIN_PASSWORD" =~ [0-9] ]]; then 417 | warn "$(get_string "install_full_password_number")" 418 | continue 419 | fi 420 | if ! [[ "$LOGIN_PASSWORD" =~ [^a-zA-Z0-9] ]]; then 421 | warn "$(get_string "install_full_password_special")" 422 | continue 423 | fi 424 | break 425 | done 426 | fi 427 | 428 | if ! check_docker; then 429 | install_docker 430 | fi 431 | if [ "$NEED_PROTECTION" = "y" ]; then 432 | install_with_protection 433 | else 434 | install_without_protection 435 | fi 436 | 437 | success "$(get_string "install_full_complete")" 438 | 439 | show_panel_info 440 | 441 | read -n 1 -s -r -p "$(get_string "install_full_press_key")" 442 | exit 0 443 | } 444 | 445 | main 446 | -------------------------------------------------------------------------------- /scripts/remnawave/install-panel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/languages.sh" 4 | source "/opt/remnasetup/scripts/common/colors.sh" 5 | source "/opt/remnasetup/scripts/common/functions.sh" 6 | 7 | REINSTALL_PANEL=false 8 | 9 | check_component() { 10 | if [ -f "/opt/remnawave/docker-compose.yml" ] && (cd /opt/remnawave && docker compose ps -q | grep -q "remnawave\|remnawave-db\|remnawave-redis") || [ -f "/opt/remnawave/.env" ]; then 11 | info "$(get_string "install_panel_detected")" 12 | while true; do 13 | question "$(get_string "install_panel_reinstall")" 14 | REINSTALL="$REPLY" 15 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 16 | warn "$(get_string "install_panel_stopping")" 17 | cd /opt/remnawave && docker compose down 18 | docker rmi remnawave/backend:latest postgres:17 valkey/valkey:8.0.2-alpine 2>/dev/null || true 19 | docker volume rm remnawave-db-data remnawave-redis-data 2>/dev/null || true 20 | rm -f /opt/remnawave/.env 21 | rm -f /opt/remnawave/docker-compose.yml 22 | REINSTALL_PANEL=true 23 | break 24 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then 25 | info "$(get_string "install_panel_reinstall_denied")" 26 | read -n 1 -s -r -p "$(get_string "install_panel_press_key")" 27 | exit 1 28 | else 29 | warn "$(get_string "install_panel_please_enter_yn")" 30 | fi 31 | done 32 | else 33 | REINSTALL_PANEL=true 34 | fi 35 | } 36 | 37 | install_docker() { 38 | if ! command -v docker &> /dev/null; then 39 | info "$(get_string "install_panel_installing_docker")" 40 | curl -fsSL https://get.docker.com -o get-docker.sh 41 | sh get-docker.sh 42 | rm get-docker.sh 43 | fi 44 | } 45 | 46 | generate_64() { 47 | openssl rand -hex 64 48 | } 49 | 50 | generate_24() { 51 | openssl rand -hex 24 52 | } 53 | 54 | generate_login() { 55 | tr -dc 'a-zA-Z' < /dev/urandom | head -c 15 56 | } 57 | 58 | install_panel() { 59 | if [ "$REINSTALL_PANEL" = true ]; then 60 | info "$(get_string "install_panel_installing")" 61 | mkdir -p /opt/remnawave 62 | cd /opt/remnawave 63 | 64 | cp "/opt/remnasetup/data/docker/panel.env" .env 65 | cp "/opt/remnasetup/data/docker/panel-compose.yml" docker-compose.yml 66 | 67 | JWT_AUTH_SECRET=$(generate_64) 68 | JWT_API_TOKENS_SECRET=$(generate_64) 69 | METRICS_USER=$(generate_login) 70 | METRICS_PASS=$(generate_64) 71 | WEBHOOK_SECRET_HEADER=$(generate_64) 72 | DB_USER=$(generate_login) 73 | DB_PASSWORD=$(generate_24) 74 | 75 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" .env 76 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" .env 77 | sed -i "s|\$DB_USER|$DB_USER|g" .env 78 | sed -i "s|\$DB_PASSWORD|$DB_PASSWORD|g" .env 79 | sed -i "s|\$JWT_AUTH_SECRET|$JWT_AUTH_SECRET|g" .env 80 | sed -i "s|\$JWT_API_TOKENS_SECRET|$JWT_API_TOKENS_SECRET|g" .env 81 | sed -i "s|\$SUB_DOMAIN|$SUB_DOMAIN|g" .env 82 | sed -i "s|\$METRICS_USER|$METRICS_USER|g" .env 83 | sed -i "s|\$METRICS_PASS|$METRICS_PASS|g" .env 84 | sed -i "s|\$WEBHOOK_SECRET_HEADER|$WEBHOOK_SECRET_HEADER|g" .env 85 | 86 | sed -i "s|\$PANEL_PORT|$PANEL_PORT|g" docker-compose.yml 87 | 88 | docker compose up -d 89 | fi 90 | } 91 | 92 | check_docker() { 93 | if command -v docker >/dev/null 2>&1; then 94 | info "$(get_string "install_panel_docker_installed")" 95 | return 0 96 | else 97 | return 1 98 | fi 99 | } 100 | 101 | main() { 102 | check_component 103 | 104 | while true; do 105 | question "$(get_string "install_panel_enter_panel_domain")" 106 | PANEL_DOMAIN="$REPLY" 107 | if [[ -n "$PANEL_DOMAIN" ]]; then 108 | break 109 | fi 110 | warn "$(get_string "install_panel_domain_empty")" 111 | done 112 | 113 | while true; do 114 | question "$(get_string "install_panel_enter_sub_domain")" 115 | SUB_DOMAIN="$REPLY" 116 | if [[ -n "$SUB_DOMAIN" ]]; then 117 | break 118 | fi 119 | warn "$(get_string "install_panel_domain_empty")" 120 | done 121 | 122 | question "$(get_string "install_panel_enter_panel_port")" 123 | PANEL_PORT="$REPLY" 124 | PANEL_PORT=${PANEL_PORT:-3000} 125 | 126 | if ! check_docker; then 127 | install_docker 128 | fi 129 | install_panel 130 | 131 | success "$(get_string "install_panel_complete")" 132 | read -n 1 -s -r -p "$(get_string "install_panel_press_key")" 133 | exit 0 134 | } 135 | 136 | main 137 | -------------------------------------------------------------------------------- /scripts/remnawave/install-subscription.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | REINSTALL_SUBSCRIPTION=false 8 | 9 | check_component() { 10 | if [ -f "/opt/remnawave/subscription/docker-compose.yml" ] && (cd /opt/remnawave/subscription && docker compose ps -q | grep -q "remnawave-subscription-page") || [ -f "/opt/remnawave/subscription/app-config.json" ]; then 11 | info "$(get_string install_subscription_detected)" 12 | while true; do 13 | question "$(get_string install_subscription_reinstall)" 14 | REINSTALL="$REPLY" 15 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 16 | warn "$(get_string install_subscription_stopping)" 17 | cd /opt/remnawave/subscription && docker compose down 18 | docker rmi remnawave/subscription-page:latest 2>/dev/null || true 19 | rm -f /opt/remnawave/subscription/app-config.json 20 | rm -f /opt/remnawave/subscription/docker-compose.yml 21 | REINSTALL_SUBSCRIPTION=true 22 | break 23 | elif [[ "$REINSTALL" == "n" || "$REINSTALL" == "N" ]]; then 24 | info "$(get_string install_subscription_reinstall_denied)" 25 | read -n 1 -s -r -p "$(get_string install_subscription_press_key)" 26 | exit 0 27 | else 28 | warn "$(get_string install_subscription_please_enter_yn)" 29 | fi 30 | done 31 | else 32 | REINSTALL_SUBSCRIPTION=true 33 | fi 34 | } 35 | 36 | install_docker() { 37 | if ! command -v docker &> /dev/null; then 38 | info "$(get_string install_subscription_installing_docker)" 39 | sudo curl -fsSL https://get.docker.com | sh 40 | fi 41 | } 42 | 43 | install_subscription() { 44 | if [ "$REINSTALL_SUBSCRIPTION" = true ]; then 45 | info "$(get_string install_subscription_installing)" 46 | mkdir -p /opt/remnawave/subscription 47 | cd /opt/remnawave/subscription 48 | 49 | cp "/opt/remnasetup/data/app-config.json" app-config.json 50 | cp "/opt/remnasetup/data/docker/subscription-compose.yml" docker-compose.yml 51 | 52 | sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" docker-compose.yml 53 | sed -i "s|\$SUB_PORT|$SUB_PORT|g" docker-compose.yml 54 | sed -i "s|\$PROJECT_NAME|$PROJECT_NAME|g" docker-compose.yml 55 | sed -i "s|\$PROJECT_DESCRIPTION|$PROJECT_DESCRIPTION|g" docker-compose.yml 56 | 57 | cd /opt/remnawave 58 | if [ -f ".env" ]; then 59 | sed -i "s|SUB_DOMAIN=.*|SUB_DOMAIN=$SUB_DOMAIN|g" .env 60 | fi 61 | 62 | docker compose down && docker compose up -d 63 | cd /opt/remnawave/subscription 64 | docker compose down && docker compose up -d 65 | fi 66 | } 67 | 68 | check_docker() { 69 | if command -v docker >/dev/null 2>&1; then 70 | info "$(get_string install_subscription_docker_installed)" 71 | return 0 72 | else 73 | return 1 74 | fi 75 | } 76 | 77 | main() { 78 | check_component 79 | 80 | while true; do 81 | question "$(get_string install_subscription_enter_panel_domain)" 82 | PANEL_DOMAIN="$REPLY" 83 | if [[ -n "$PANEL_DOMAIN" ]]; then 84 | break 85 | fi 86 | warn "$(get_string install_subscription_domain_empty)" 87 | done 88 | 89 | while true; do 90 | question "$(get_string install_subscription_enter_sub_domain)" 91 | SUB_DOMAIN="$REPLY" 92 | if [[ -n "$SUB_DOMAIN" ]]; then 93 | break 94 | fi 95 | warn "$(get_string install_subscription_domain_empty)" 96 | done 97 | 98 | question "$(get_string install_subscription_enter_sub_port)" 99 | SUB_PORT="$REPLY" 100 | SUB_PORT=${SUB_PORT:-3010} 101 | 102 | while true; do 103 | question "$(get_string install_subscription_enter_project_name)" 104 | PROJECT_NAME="$REPLY" 105 | if [[ -n "$PROJECT_NAME" ]]; then 106 | break 107 | fi 108 | warn "$(get_string install_subscription_project_name_empty)" 109 | done 110 | 111 | while true; do 112 | question "$(get_string install_subscription_enter_project_description)" 113 | PROJECT_DESCRIPTION="$REPLY" 114 | if [[ -n "$PROJECT_DESCRIPTION" ]]; then 115 | break 116 | fi 117 | warn "$(get_string install_subscription_project_description_empty)" 118 | done 119 | 120 | if ! check_docker; then 121 | install_docker 122 | fi 123 | install_subscription 124 | 125 | success "$(get_string install_subscription_complete)" 126 | read -n 1 -s -r -p "$(get_string install_subscription_press_key)" 127 | exit 0 128 | } 129 | 130 | main 131 | -------------------------------------------------------------------------------- /scripts/remnawave/update-full.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | update_all() { 8 | info "$(get_string update_full_updating)" 9 | 10 | cd /opt/remnawave 11 | docker compose pull 12 | docker compose up -d 13 | 14 | cd /opt/remnawave/subscription 15 | docker compose pull 16 | docker compose up -d 17 | 18 | success "$(get_string update_full_complete)" 19 | } 20 | 21 | main() { 22 | update_all 23 | read -n 1 -s -r -p "$(get_string update_full_press_key)" 24 | exit 0 25 | } 26 | 27 | main 28 | -------------------------------------------------------------------------------- /scripts/remnawave/update-panel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | update_panel() { 8 | info "$(get_string update_panel_updating)" 9 | cd /opt/remnawave 10 | docker compose pull 11 | docker compose up -d 12 | success "$(get_string update_panel_complete)" 13 | } 14 | 15 | main() { 16 | update_panel 17 | read -n 1 -s -r -p "$(get_string update_panel_press_key)" 18 | exit 0 19 | } 20 | 21 | main 22 | -------------------------------------------------------------------------------- /scripts/remnawave/update-subscription.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "/opt/remnasetup/scripts/common/colors.sh" 4 | source "/opt/remnasetup/scripts/common/functions.sh" 5 | source "/opt/remnasetup/scripts/common/languages.sh" 6 | 7 | update_subscription() { 8 | info "$(get_string update_subscription_updating)" 9 | cd /opt/remnawave/subscription 10 | docker compose pull 11 | docker compose up -d 12 | success "$(get_string update_subscription_complete)" 13 | } 14 | 15 | main() { 16 | update_subscription 17 | read -n 1 -s -r -p "$(get_string update_subscription_press_key)" 18 | exit 0 19 | } 20 | 21 | main 22 | --------------------------------------------------------------------------------