version 1.2.1: авто-SSL при установке, кнопка перезапуска панели

- install-panel.sh: самоподписной сертификат создаётся автоматически при установке, панель сразу работает по HTTPS
- app.py: API /api/panel/restart для перезапуска панели
- index.html: кнопка «Перезапустить панель» на вкладке SSL
- mita-ctl.sh: автоопределение http/https (_panel_proto) для URL панели
- README: обновлена дорожная карта
This commit is contained in:
2026-06-26 23:44:53 +03:00
parent 1f5511a41a
commit 667a0957ac
5 changed files with 84 additions and 13 deletions
+2 -1
View File
@@ -78,7 +78,8 @@ curl -Ls https://raw.githubusercontent.com/grendervilll/mitactl/main/bootstrap.s
- [x] Генерация клиентских конфигов (mieru JSON и sing-box JSON) - [x] Генерация клиентских конфигов (mieru JSON и sing-box JSON)
- [x] Скачивание конфигов и отображение QR-кодов - [x] Скачивание конфигов и отображение QR-кодов
- [x] Управление WARP на пользователя (toggle + правила: домены, IP, geosite, geoip, URL источники) - [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] Интеграция fail2ban: rate-limiting панели + fail2ban jail (пресеты)
- [x] Управление файрволом (UFW/iptables) через CLI и панель - [x] Управление файрволом (UFW/iptables) через CLI и панель
- [x] Режим SSH-туннеля: панель только на 127.0.0.1 - [x] Режим SSH-туннеля: панель только на 127.0.0.1
+9
View File
@@ -687,6 +687,15 @@ def api_ssl_letsencrypt():
return jsonify({"ok": True, "expires": exp, return jsonify({"ok": True, "expires": exp,
"note": "Перезапустите панель: systemctl restart mita-panel"}) "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): def _update_env(key, value):
env_file = "/etc/mita/panel.env" env_file = "/etc/mita/panel.env"
lines = [] lines = []
+31 -5
View File
@@ -236,11 +236,36 @@ elif command -v iptables &>/dev/null; then
ok "iptables: порт $PANEL_PORT открыт" ok "iptables: порт $PANEL_PORT открыт"
fi 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 "Установка завершена!" section "Установка завершена!"
SERVER_IP=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') SERVER_IP=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')
PROTO="http"
echo "" echo ""
echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗" echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗"
@@ -261,9 +286,10 @@ echo ""
echo -e "${CYAN}Полная ссылка для браузера:${NC}" echo -e "${CYAN}Полная ссылка для браузера:${NC}"
echo -e " ${BOLD}${GREEN}${PROTO}://${SERVER_IP}:${PANEL_PORT}/${SECRET_PATH}/${NC}" echo -e " ${BOLD}${GREEN}${PROTO}://${SERVER_IP}:${PANEL_PORT}/${SECRET_PATH}/${NC}"
echo "" echo ""
echo -e "${CYAN}Настройка SSL (опционально):${NC}" echo -e "${CYAN}SSL:${NC}"
echo -e " Войдите в панель → раздел SSL → выберите тип сертификата" echo -e " Сертификат: самоподписной (10 лет), уже установлен"
echo -e " После установки SSL: ${BOLD}systemctl restart mita-panel${NC}" echo -e " Браузер покажет предупреждение — примите его"
echo -e " Заменить на Let's Encrypt: раздел SSL в панели или mita-ctl → пункт 3"
echo "" echo ""
echo -e "${CYAN}Полезные команды:${NC}" echo -e "${CYAN}Полезные команды:${NC}"
echo -e " ${YELLOW}systemctl status mita-panel${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 -e " ${YELLOW}ssh -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} -p ${SSH_ACCESS_PORT} root@${SERVER_IP} -N${NC}"
echo "" echo ""
echo -e " Затем откройте в браузере:" 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 ""
echo -e " Для фонового режима:" echo -e " Для фонового режима:"
echo -e " ${YELLOW}ssh -fN -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} -p ${SSH_ACCESS_PORT} root@${SERVER_IP}${NC}" echo -e " ${YELLOW}ssh -fN -L ${PANEL_PORT}:127.0.0.1:${PANEL_PORT} -p ${SSH_ACCESS_PORT} root@${SERVER_IP}${NC}"
+12 -2
View File
@@ -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 ""; } 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 ""; } 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; } gen_pass() { openssl rand -base64 48 | tr -d '/+=' | head -c 32; }
# ── SSH tunnel helper ────────────────────────────────────────────────────── # ── 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 -e " ${YELLOW}ssh -L ${port}:127.0.0.1:${port} -p ${ssh_port} root@${server_ip} -N${NC}"
echo "" echo ""
echo -e " Затем откройте в браузере:" 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 ""
echo -e " ${BLUE}Флаг -N означает что SSH не выполняет команды, только туннель.${NC}" echo -e " ${BLUE}Флаг -N означает что SSH не выполняет команды, только туннель.${NC}"
echo -e " ${BLUE}Для фонового режима добавьте -f:${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 systemctl restart mita-panel 2>/dev/null || true
ok "Панель доступна по IP" ok "Панель доступна по IP"
echo "" 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 pause
;; ;;
+30 -5
View File
@@ -179,11 +179,16 @@
</div> </div>
<div class="card"> <div class="card">
<div class="card-title">Применение сертификата</div> <div class="card-title">Перезапуск панели</div>
<div class="alert alert-warn"> <p style="color:var(--muted);font-size:.875rem;margin-bottom:14px;line-height:1.6">
После смены сертификата необходимо перезапустить панель:<br> После смены сертификата необходимо перезапустить панель, чтобы применить изменения.
<code style="font-family:var(--mono);font-size:.85rem">systemctl restart mita-panel</code> </p>
</div> <button class="btn btn-warn" onclick="restartPanel()">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
Перезапустить панель
</button>
<div id="restart-result" style="margin-top:12px"></div>
</div>
</div> </div>
</div> </div>
@@ -886,6 +891,26 @@ async function getLetsEncrypt(){
} }
} }
async function restartPanel(){
const btn=document.querySelector('#page-ssl .btn-warn');
btn.disabled=true;
btn.innerHTML='<span class="spinner"></span> Перезапуск...';
const el=document.getElementById('restart-result');
try{
const d=await api('/api/panel/restart','POST');
if(d?.ok){
el.innerHTML='<div class="alert alert-ok">✓ Панель перезапущена</div>';
showToast('Панель перезапущена');
}else{
el.innerHTML=`<div class="alert alert-err">Ошибка: ${escH(d?.error||'')}</div>`;
}
}catch(e){
el.innerHTML=`<div class="alert alert-err">Ошибка: ${escH(e.message)}</div>`;
}
btn.disabled=false;
btn.innerHTML='<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg> Перезапустить панель';
}
// ──────────────── TELEGRAM BOT ──────────────── // ──────────────── TELEGRAM BOT ────────────────
async function loadBotPage() { async function loadBotPage() {
const d = await api('/api/bot/status'); const d = await api('/api/bot/status');