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:
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
+31
-5
@@ -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}"
|
||||
|
||||
+12
-2
@@ -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
|
||||
;;
|
||||
|
||||
|
||||
+30
-5
@@ -179,11 +179,16 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">Применение сертификата</div>
|
||||
<div class="alert alert-warn">
|
||||
После смены сертификата необходимо перезапустить панель:<br>
|
||||
<code style="font-family:var(--mono);font-size:.85rem">systemctl restart mita-panel</code>
|
||||
</div>
|
||||
<div class="card-title">Перезапуск панели</div>
|
||||
<p style="color:var(--muted);font-size:.875rem;margin-bottom:14px;line-height:1.6">
|
||||
После смены сертификата необходимо перезапустить панель, чтобы применить изменения.
|
||||
</p>
|
||||
<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>
|
||||
|
||||
@@ -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 ────────────────
|
||||
async function loadBotPage() {
|
||||
const d = await api('/api/bot/status');
|
||||
|
||||
Reference in New Issue
Block a user