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

126
setec-web/ssh_client.py Normal file
View File

@@ -0,0 +1,126 @@
"""SSH client with optional E2E encryption.
When E2E is enabled, commands are encrypted with the tunnel key before
being sent over SSH, and responses are decrypted on return. The Go agent
on the VPS handles decryption/execution/re-encryption.
When E2E is disabled, commands run as plain text over SSH (still encrypted
by SSH transport, just no additional application-layer encryption).
"""
import paramiko
import config
import json
_client = None
def get_client():
global _client
if _client and _client.get_transport() and _client.get_transport().is_active():
return _client
cfg = config.load()
_client = paramiko.SSHClient()
_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
key_path = cfg.get("ssh_key_path", "")
if not key_path:
raise RuntimeError("SSH key path not configured")
# Try Ed25519 first, fall back to RSA
try:
key = paramiko.Ed25519Key.from_private_key_file(key_path)
except Exception:
try:
key = paramiko.RSAKey.from_private_key_file(key_path)
except Exception:
key = paramiko.ECDSAKey.from_private_key_file(key_path)
_client.connect(
hostname=cfg["vps_host"],
port=cfg["vps_port"],
username=cfg["vps_user"],
pkey=key,
timeout=15,
)
return _client
def run(cmd, timeout=30):
"""Execute a command on the VPS. Uses E2E encryption when enabled."""
import e2e
if e2e.is_e2e_enabled():
return _run_e2e(cmd, timeout)
else:
return _run_plain(cmd, timeout)
def _run_plain(cmd, timeout=30):
"""Execute command via plain SSH (no E2E)."""
client = get_client()
_, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode("utf-8", errors="replace")
err = stderr.read().decode("utf-8", errors="replace")
code = stdout.channel.recv_exit_status()
return {"stdout": out, "stderr": err, "exit_code": code}
def _run_e2e(cmd, timeout=30):
"""Execute command via E2E encrypted tunnel.
1. Encrypt command with tunnel key
2. SSH: echo <encrypted> | setec-agent
3. Agent decrypts, executes, encrypts response
4. Decrypt response locally
"""
import e2e
tunnel_key = e2e.get_tunnel_key()
# Build the wrapped command
agent_cmd = e2e.wrap_command_for_agent(tunnel_key, cmd)
# Execute via SSH
client = get_client()
_, stdout, stderr = client.exec_command(agent_cmd, timeout=timeout)
out = stdout.read().decode("utf-8", errors="replace").strip()
err = stderr.read().decode("utf-8", errors="replace").strip()
code = stdout.channel.recv_exit_status()
# If agent returned an error on stderr, it's an agent-level failure
if code != 0 and not out:
return {
"stdout": "",
"stderr": f"E2E agent error: {err}" if err else "E2E agent returned non-zero with no output",
"exit_code": code,
}
# Decrypt the response
try:
response = e2e.decrypt_response(tunnel_key, out)
return {
"stdout": response.get("stdout", ""),
"stderr": response.get("stderr", ""),
"exit_code": response.get("exit_code", 0),
}
except Exception as ex:
# If decryption fails, the output might be from a non-E2E command
# or the agent isn't installed. Return raw output with warning.
return {
"stdout": "",
"stderr": f"E2E decryption failed: {str(ex)}\nRaw output: {out[:200]}",
"exit_code": -1,
}
def run_plain_always(cmd, timeout=30):
"""Always run without E2E — used for agent deployment and setup commands."""
return _run_plain(cmd, timeout)
def close():
global _client
if _client:
_client.close()
_client = None