Initial commit — SETEC LABS Manager (Setec_CDM)

Flask-based VPS management panel with SSH remote command execution.
Includes E2E encrypted SSH tunnel (AES-256-GCM + Go agent), setup wizard,
security hardening tools, DNS management, firewall configs, monitoring,
backup, and .sec patch update system.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DigiJ
2026-03-13 12:39:02 -07:00
commit 9e839ee826
62 changed files with 14605 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
{% extends "base.html" %}
{% block title %}Fail2Ban{% endblock %}
{% block content %}
<h1>[!] Fail2Ban</h1>
<div class="toolbar">
<button class="btn" onclick="loadStatus()">Refresh</button>
<button class="btn" onclick="loadAllJails()">All Jails Detail</button>
<button class="btn" onclick="loadLog()">View Log</button>
<button class="btn" onclick="loadConfig()">Edit Config</button>
<button class="btn btn-warn" onclick="reloadF2B()">Reload</button>
<button class="btn btn-danger" onclick="unbanAll()">Unban All</button>
</div>
<div class="grid grid-2">
<div class="card">
<div class="card-title">Status</div>
<div class="output" id="status-output"><span class="info">Loading...</span></div>
</div>
<div class="card">
<div class="card-title">Jail Details</div>
<div class="toolbar">
<select id="jail-select" onchange="loadJail()">
<option value="">-- select jail --</option>
<option value="sshd">sshd</option>
<option value="nginx-http-auth">nginx-http-auth</option>
<option value="nginx-botsearch">nginx-botsearch</option>
<option value="nginx-badbots">nginx-badbots</option>
<option value="postfix">postfix</option>
</select>
</div>
<div class="output" id="jail-output"><span class="info">Select a jail</span></div>
</div>
</div>
<div class="grid grid-2">
<div class="card">
<div class="card-title">Ban IP</div>
<label>Jail</label>
<select id="ban-jail">
<option value="sshd">sshd</option>
<option value="nginx-http-auth">nginx-http-auth</option>
<option value="nginx-botsearch">nginx-botsearch</option>
<option value="nginx-badbots">nginx-badbots</option>
<option value="postfix">postfix</option>
</select>
<label>IP Address</label>
<input type="text" id="ban-ip" placeholder="1.2.3.4" style="width:200px">
<br><br>
<button class="btn btn-danger" onclick="banIP()">Ban</button>
</div>
<div class="card">
<div class="card-title">Unban IP</div>
<label>Jail</label>
<select id="unban-jail">
<option value="sshd">sshd</option>
<option value="nginx-http-auth">nginx-http-auth</option>
<option value="nginx-botsearch">nginx-botsearch</option>
<option value="nginx-badbots">nginx-badbots</option>
<option value="postfix">postfix</option>
</select>
<label>IP Address</label>
<input type="text" id="unban-ip" placeholder="1.2.3.4" style="width:200px">
<br><br>
<button class="btn" onclick="unbanIP()">Unban</button>
</div>
</div>
<div class="card" id="config-card" style="display:none">
<div class="card-title">jail.local</div>
<textarea id="config-editor" rows="20" style="width:100%;tab-size:4" spellcheck="false"></textarea>
<br>
<button class="btn" onclick="saveConfig()">Save & Reload</button>
</div>
<div class="card">
<div class="card-title">Output / Log</div>
<div class="output" id="output" style="max-height:500px"><span class="info">Ready.</span></div>
</div>
{% endblock %}
{% block scripts %}
<script>
async function loadStatus() {
const res = await apiGet('/api/fail2ban/status');
showResult(res, 'status-output');
}
async function loadJail() {
const name = document.getElementById('jail-select').value;
if (!name) return;
const res = await apiGet('/api/fail2ban/jail/' + name);
showResult(res, 'jail-output');
}
async function loadAllJails() {
const res = await apiGet('/api/fail2ban/jails');
showResult(res);
}
async function banIP() {
const jail = document.getElementById('ban-jail').value;
const ip = document.getElementById('ban-ip').value.trim();
if (!ip) { alert('Enter an IP'); return; }
const res = await apiPost('/api/fail2ban/ban', {jail, ip});
showResult(res);
loadJail();
}
async function unbanIP() {
const jail = document.getElementById('unban-jail').value;
const ip = document.getElementById('unban-ip').value.trim();
if (!ip) { alert('Enter an IP'); return; }
const res = await apiPost('/api/fail2ban/unban', {jail, ip});
showResult(res);
loadJail();
}
async function unbanAll() {
if (!confirm('Unban ALL IPs from ALL jails?')) return;
const res = await apiPost('/api/fail2ban/unban-all');
showResult(res);
loadStatus();
}
async function reloadF2B() {
const res = await apiPost('/api/fail2ban/reload');
showResult(res);
loadStatus();
}
async function loadLog() {
const res = await apiGet('/api/fail2ban/log?lines=50');
showResult(res);
}
async function loadConfig() {
document.getElementById('config-card').style.display = '';
const res = await apiGet('/api/fail2ban/config');
if (res.ok) {
document.getElementById('config-editor').value = res.data.stdout || '';
}
}
async function saveConfig() {
const content = document.getElementById('config-editor').value;
if (!confirm('Save config and reload fail2ban?')) return;
const res = await apiPost('/api/fail2ban/config/save', {content});
showResult(res);
loadStatus();
}
document.getElementById('config-editor').addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault();
const s = this.selectionStart;
this.value = this.value.substring(0, s) + ' ' + this.value.substring(this.selectionEnd);
this.selectionStart = this.selectionEnd = s + 4;
}
if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
saveConfig();
}
});
loadStatus();
</script>
{% endblock %}