feat: Simplify AutoStart and fix critical security vulnerability

Major architectural improvements:
- Remove complex plaintext cache system, use direct database fields
- Replace IP-based authentication with secure token-based auth
- Integrate INTERNAL_AUTH_TOKEN with unified auto-generation system

Security fixes:
- Fix Docker nginx proxy authentication bypass vulnerability in /ssh/db/host/internal
- Replace req.ip detection with X-Internal-Auth-Token header validation
- Add production environment security checks for internal auth token

AutoStart simplification:
- Add autostart_{password,key,key_password} columns directly to ssh_data table
- Remove redundant autostartPlaintextCache table and AutoStartPlaintextManager
- Implement enable/disable/status endpoints for autostart management
- Update frontend to handle autostart cache lifecycle automatically

Environment variable improvements:
- Integrate INTERNAL_AUTH_TOKEN into SystemCrypto auto-generation
- Unified .env file management for all security keys (JWT, Database, Internal Auth)
- Auto-generate secure tokens with proper entropy (256-bit)

API improvements:
- Make /users/oidc-config and /users/registration-allowed public for login page
- Add /users/setup-required endpoint replacing problematic getUserCount usage
- Restrict /users/count to admin-only access for security

Database schema:
- Add autostart plaintext columns to ssh_data table with proper migrations
- Remove complex cache table structure for simplified data model
This commit is contained in:
ZacharyZcR
2025-09-24 01:57:17 +08:00
parent cf6fed8d77
commit 8c004dfcfe
11 changed files with 479 additions and 49 deletions

View File

@@ -30,6 +30,8 @@ import {
getCredentials,
getSSHHosts,
updateSSHHost,
enableAutoStart,
disableAutoStart,
} from "@/ui/main-axios.ts";
import { useTranslation } from "react-i18next";
import { CredentialSelector } from "@/ui/Desktop/Apps/Credentials/CredentialSelector.tsx";
@@ -436,22 +438,47 @@ export function HostManagerEditor({
submitData.keyType = data.keyType;
}
let savedHost;
if (editingHost && editingHost.id) {
const updatedHost = await updateSSHHost(editingHost.id, submitData);
savedHost = await updateSSHHost(editingHost.id, submitData);
toast.success(t("hosts.hostUpdatedSuccessfully", { name: data.name }));
if (onFormSubmit) {
onFormSubmit(updatedHost);
}
} else {
const newHost = await createSSHHost(submitData);
savedHost = await createSSHHost(submitData);
toast.success(t("hosts.hostAddedSuccessfully", { name: data.name }));
}
if (onFormSubmit) {
onFormSubmit(newHost);
// Handle AutoStart plaintext cache management
if (savedHost && savedHost.id && data.tunnelConnections) {
const hasAutoStartTunnels = data.tunnelConnections.some(tunnel => tunnel.autoStart);
if (hasAutoStartTunnels) {
// User has enabled autoStart on some tunnels
// Need to ensure plaintext cache exists for this host
try {
await enableAutoStart(savedHost.id);
console.log(`AutoStart plaintext cache enabled for SSH host ${savedHost.id}`);
} catch (error) {
console.warn(`Failed to enable AutoStart plaintext cache for SSH host ${savedHost.id}:`, error);
// Don't fail the whole operation if cache setup fails
toast.warning(t("hosts.autoStartEnableFailed", { name: data.name }));
}
} else {
// User has disabled autoStart on all tunnels
// Clean up plaintext cache for this host
try {
await disableAutoStart(savedHost.id);
console.log(`AutoStart plaintext cache disabled for SSH host ${savedHost.id}`);
} catch (error) {
console.warn(`Failed to disable AutoStart plaintext cache for SSH host ${savedHost.id}:`, error);
// Don't fail the whole operation
}
}
}
if (onFormSubmit) {
onFormSubmit(savedHost);
}
window.dispatchEvent(new CustomEvent("ssh-hosts:changed"));
form.reset();