diff --git a/README.md b/README.md index ef85848..8656e68 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ curl -Ls https://raw.githubusercontent.com/grendervilll/mitactl/main/bootstrap.s - [x] Генерация клиентских конфигов (mieru JSON и sing-box JSON) - [x] Скачивание конфигов и отображение QR-кодов - [x] Управление WARP на пользователя (toggle + правила: домены, IP, geosite, geoip, URL источники) -- [x] SSL-сертификаты: self-signed (10 лет) и Let's Encrypt через веб-интерфейс +- [x] SSL-сертификаты: самоподписной (10 лет) создаётся автоматически при установке, Let's Encrypt через UI +- [x] Кнопка перезапуска панели на вкладке SSL - [x] Интеграция fail2ban: rate-limiting панели + fail2ban jail (пресеты) - [x] Управление файрволом (UFW/iptables) через CLI и панель - [x] Режим SSH-туннеля: панель только на 127.0.0.1 diff --git a/app.py b/app.py index 13a5ff8..7f13a8a 100644 --- a/app.py +++ b/app.py @@ -687,6 +687,15 @@ def api_ssl_letsencrypt(): return jsonify({"ok": True, "expires": exp, "note": "Перезапустите панель: systemctl restart mita-panel"}) +@app.route(f"{BASE}/api/panel/restart", methods=["POST"]) +@login_required +def api_panel_restart(): + r = subprocess.run(["systemctl","restart","mita-panel"], + capture_output=True, text=True, timeout=10) + if r.returncode != 0: + return jsonify({"ok": False, "error": r.stderr.strip()}), 500 + return jsonify({"ok": True}) + def _update_env(key, value): env_file = "/etc/mita/panel.env" lines = [] diff --git a/install-panel.sh b/install-panel.sh index dd7926a..7fd1f3e 100755 --- a/install-panel.sh +++ b/install-panel.sh @@ -236,11 +236,36 @@ elif command -v iptables &>/dev/null; then ok "iptables: порт $PANEL_PORT открыт" fi +# ── авто-генерация самоподписного сертификата ───────────────────── +section "SSL-сертификат" +SSL_DIR="/etc/mita/ssl" +mkdir -p "$SSL_DIR" +SSL_CERT="$SSL_DIR/selfsigned.crt" +SSL_KEY="$SSL_DIR/selfsigned.key" + +info "Создание самоподписного сертификата (10 лет)..." +openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \ + -keyout "$SSL_KEY" -out "$SSL_CERT" \ + -subj "/CN=mita-panel/O=mita/C=XX" 2>/dev/null + +echo "SSL_CERT=$SSL_CERT" >> /etc/mita/panel.env +echo "SSL_KEY=$SSL_KEY" >> /etc/mita/panel.env + +python3 -c " +import json +with open('$PANEL_CONFIG') as f: d=json.load(f) +d.update({'ssl_type':'selfsigned','ssl_cert':'$SSL_CERT','ssl_key':'$SSL_KEY'}) +with open('$PANEL_CONFIG','w') as f: json.dump(d,f,indent=2) +" +systemctl restart mita-panel 2>/dev/null || true +ok "Самоподписной сертификат установлен, панель перезапущена с HTTPS" + +PROTO="https" + # ── итог ────────────────────────────────────────────────────────── section "Установка завершена!" SERVER_IP=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') -PROTO="http" echo "" echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗" @@ -261,9 +286,10 @@ echo "" echo -e "${CYAN}Полная ссылка для браузера:${NC}" echo -e " ${BOLD}${GREEN}${PROTO}://${SERVER_IP}:${PANEL_PORT}/${SECRET_PATH}/${NC}" echo "" -echo -e "${CYAN}Настройка SSL (опционально):${NC}" -echo -e " Войдите в панель → раздел SSL → выберите тип сертификата" -echo -e " После установки SSL: ${BOLD}systemctl restart mita-panel${NC}" +echo -e "${CYAN}SSL:${NC}" +echo -e " Сертификат: самоподписной (10 лет), уже установлен" +echo -e " Браузер покажет предупреждение — примите его" +echo -e " Заменить на Let's Encrypt: раздел SSL в панели или mita-ctl → пункт 3" echo "" echo -e "${CYAN}Полезные команды:${NC}" echo -e " ${YELLOW}systemctl status mita-panel${NC} — статус" @@ -285,7 +311,7 @@ if [[ "$ACCESS_MODE" == "ssh" ]]; then echo -e " ${YELLOW}ssh -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} -p ${SSH_ACCESS_PORT} root@${SERVER_IP} -N${NC}" echo "" echo -e " Затем откройте в браузере:" - echo -e " ${BOLD}${GREEN}http://127.0.0.1:${PANEL_PORT}/${SECRET_PATH}/${NC}" + echo -e " ${BOLD}${GREEN}${PROTO}://127.0.0.1:${PANEL_PORT}/${SECRET_PATH}/${NC}" echo "" echo -e " Для фонового режима:" echo -e " ${YELLOW}ssh -fN -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} -p ${SSH_ACCESS_PORT} root@${SERVER_IP}${NC}" diff --git a/mita-ctl.sh b/mita-ctl.sh index d5fe7a8..f367c59 100755 --- a/mita-ctl.sh +++ b/mita-ctl.sh @@ -28,6 +28,16 @@ load_panel() { panel_value() { load_panel | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('$1',''))" 2>/dev/null || echo ""; } env_value() { [[ -f "$PANEL_ENV" ]] && grep "^$1=" "$PANEL_ENV" | cut -d= -f2- || echo ""; } +_panel_proto() { + local ssl_cert + ssl_cert=$(env_value "SSL_CERT") + if [[ -n "$ssl_cert" && -f "$ssl_cert" ]]; then + echo "https" + else + echo "http" + fi +} + gen_pass() { openssl rand -base64 48 | tr -d '/+=' | head -c 32; } # ── SSH tunnel helper ────────────────────────────────────────────────────── @@ -47,7 +57,7 @@ print_ssh_instruction() { echo -e " ${YELLOW}ssh -L ${port}:127.0.0.1:${port} -p ${ssh_port} root@${server_ip} -N${NC}" echo "" echo -e " Затем откройте в браузере:" - echo -e " ${BOLD}${GREEN}http://127.0.0.1:${port}/${secret}/${NC}" + echo -e " ${BOLD}${GREEN}$(_panel_proto)://127.0.0.1:${port}/${secret}/${NC}" echo "" echo -e " ${BLUE}Флаг -N означает что SSH не выполняет команды, только туннель.${NC}" echo -e " ${BLUE}Для фонового режима добавьте -f:${NC}" @@ -162,7 +172,7 @@ with open('$PANEL_CONFIG','w') as f: json.dump(d,f,indent=2) systemctl restart mita-panel 2>/dev/null || true ok "Панель доступна по IP" echo "" - echo -e " Адрес: ${BOLD}${GREEN}http://${server_ip}:${panel_port}/${secret}/${NC}" + echo -e " Адрес: ${BOLD}${GREEN}$(_panel_proto)://${server_ip}:${panel_port}/${secret}/${NC}" pause ;; diff --git a/templates/index.html b/templates/index.html index 9a3a1b8..d98de6c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -179,11 +179,16 @@
-
Применение сертификата
-
- После смены сертификата необходимо перезапустить панель:
- systemctl restart mita-panel -
+
Перезапуск панели
+

+ После смены сертификата необходимо перезапустить панель, чтобы применить изменения. +

+ +
+
@@ -886,6 +891,26 @@ async function getLetsEncrypt(){ } } +async function restartPanel(){ + const btn=document.querySelector('#page-ssl .btn-warn'); + btn.disabled=true; + btn.innerHTML=' Перезапуск...'; + const el=document.getElementById('restart-result'); + try{ + const d=await api('/api/panel/restart','POST'); + if(d?.ok){ + el.innerHTML='
✓ Панель перезапущена
'; + showToast('Панель перезапущена'); + }else{ + el.innerHTML=`
Ошибка: ${escH(d?.error||'')}
`; + } + }catch(e){ + el.innerHTML=`
Ошибка: ${escH(e.message)}
`; + } + btn.disabled=false; + btn.innerHTML=' Перезапустить панель'; +} + // ──────────────── TELEGRAM BOT ──────────────── async function loadBotPage() { const d = await api('/api/bot/status');