Fix SSH permissions: store known_hosts in persistent /data and disable pseudo-terminal warning
This commit is contained in:
1 parent
6d03f5de43
commit
f74a3e09f5
2 files changed
+48
-68
No files matched your search
+44
-65
@@ -4,7 +4,7 @@ import shutil
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List, Optional, Tuple
|
||||
from backend.config import load_settings, DEFAULT_KEY_FILE, DEFAULT_PUB_KEY_FILE
|
||||
from backend.config import load_settings, DEFAULT_KEY_FILE, DEFAULT_PUB_KEY_FILE, DATA_DIR
|
||||
|
||||
def get_public_key() -> str:
|
||||
if DEFAULT_PUB_KEY_FILE.exists():
|
||||
@@ -40,9 +40,12 @@ def build_ssh_prefix(settings: Optional[Dict[str, Any]] = None) -> List[str]:
|
||||
raw_key_path = settings.get("vps_ssh_key_path", str(DEFAULT_KEY_FILE))
|
||||
key_path = resolve_key_path(raw_key_path)
|
||||
password = settings.get("vps_ssh_password", "")
|
||||
known_hosts = str(DATA_DIR / "known_hosts")
|
||||
|
||||
ssh_opts = [
|
||||
"-T",
|
||||
"-o", "StrictHostKeyChecking=accept-new",
|
||||
"-o", f"UserKnownHostsFile={known_hosts}",
|
||||
"-o", "ConnectTimeout=8",
|
||||
"-o", "ServerAliveInterval=15",
|
||||
"-p", port
|
||||
@@ -69,10 +72,12 @@ def build_scp_prefix(settings: Optional[Dict[str, Any]] = None) -> List[str]:
|
||||
raw_key_path = settings.get("vps_ssh_key_path", str(DEFAULT_KEY_FILE))
|
||||
key_path = resolve_key_path(raw_key_path)
|
||||
password = settings.get("vps_ssh_password", "")
|
||||
known_hosts = str(DATA_DIR / "known_hosts")
|
||||
|
||||
scp_opts = [
|
||||
"-P", port,
|
||||
"-o", "StrictHostKeyChecking=accept-new",
|
||||
"-o", f"UserKnownHostsFile={known_hosts}",
|
||||
"-o", "ConnectTimeout=10"
|
||||
]
|
||||
|
||||
@@ -99,79 +104,53 @@ async def run_remote_quick(cmd: str, settings: Optional[Dict[str, Any]] = None)
|
||||
stdout, stderr = await proc.communicate()
|
||||
return proc.returncode, stdout.decode('utf-8', errors='replace'), stderr.decode('utf-8', errors='replace')
|
||||
except Exception as e:
|
||||
return -1, "", str(e)
|
||||
return 1, "", str(e)
|
||||
|
||||
async def test_vps_connection(custom_settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
s = custom_settings or load_settings()
|
||||
host = s.get("vps_host", "").strip()
|
||||
async def test_vps_connection(settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
if not settings:
|
||||
settings = load_settings()
|
||||
|
||||
host = settings.get("vps_host", "").strip()
|
||||
if not host:
|
||||
return {
|
||||
"success": False,
|
||||
"connected": False,
|
||||
"error": "No VPS Host / IP configured. Please specify the VPS address in Settings."
|
||||
}
|
||||
return {"success": False, "error": "No VPS host configured. Please provide IP or hostname."}
|
||||
|
||||
# Run comprehensive diagnostic script on remote VPS
|
||||
diag_script = """
|
||||
echo "===PING_OK==="
|
||||
uname -n
|
||||
cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '"'
|
||||
uname -r
|
||||
uptime -p 2>/dev/null || uptime
|
||||
which docker >/dev/null 2>&1 && echo "DOCKER_FOUND" || echo "DOCKER_NOT_FOUND"
|
||||
docker ps >/dev/null 2>&1 && echo "DOCKER_RUNNING" || echo "DOCKER_STOPPED"
|
||||
"""
|
||||
ret, stdout, stderr = await run_remote_quick(diag_script, s)
|
||||
# 1. Test basic SSH connectivity & gather OS summary
|
||||
cmd_ping = 'echo "PANGOLIN_PONG" && uname -srm && cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d \'"\''
|
||||
code, stdout, stderr = await run_remote_quick(cmd_ping, settings)
|
||||
|
||||
if ret != 0 or "===PING_OK===" not in stdout:
|
||||
err = stderr.strip() or "Connection failed. Please check Host, Port, and SSH credentials."
|
||||
if "Permission denied" in err:
|
||||
err = "Permission denied (SSH authentication failed). Please ensure your SSH public key or password is correct on the VPS."
|
||||
elif "Connection timed out" in err or "No route to host" in err:
|
||||
err = f"Could not reach {host}:{s.get('vps_port', 22)}. Verify firewall, IP address, and SSH port."
|
||||
return {
|
||||
"success": False,
|
||||
"connected": False,
|
||||
"error": err
|
||||
}
|
||||
if code != 0:
|
||||
err_msg = stderr.strip() or "Connection timed out or failed."
|
||||
if "Permission denied" in err_msg:
|
||||
err_msg += " (SSH Authentication failed. Check key in ~/.ssh/authorized_keys or password)"
|
||||
return {"success": False, "error": err_msg}
|
||||
|
||||
lines = [line.strip() for line in stdout.splitlines() if line.strip()]
|
||||
hostname = lines[1] if len(lines) > 1 else host
|
||||
os_name = lines[2] if len(lines) > 2 else "Linux"
|
||||
kernel = lines[3] if len(lines) > 3 else ""
|
||||
uptime_str = lines[4] if len(lines) > 4 else ""
|
||||
docker_found = "DOCKER_FOUND" in stdout
|
||||
docker_running = "DOCKER_RUNNING" in stdout
|
||||
lines = stdout.strip().split("\n")
|
||||
os_name = lines[2] if len(lines) > 2 else (lines[1] if len(lines) > 1 else "Linux")
|
||||
kernel = lines[1] if len(lines) > 1 else ""
|
||||
|
||||
# Check Pangolin path on VPS
|
||||
p_dir = s.get("vps_pangolin_dir", "/opt/pangolin")
|
||||
p_script = f"""
|
||||
[ -d "{p_dir}" ] && echo "DIR_OK" || echo "DIR_MISSING"
|
||||
[ -f "{p_dir}/docker-compose.yml" ] || [ -f "{p_dir}/compose.yaml" ] && echo "COMPOSE_OK" || echo "COMPOSE_MISSING"
|
||||
find "{p_dir}" -name "*.db" -o -name "*.sqlite" 2>/dev/null | head -n 1
|
||||
"""
|
||||
_, p_out, _ = await run_remote_quick(p_script, s)
|
||||
p_lines = [l.strip() for l in p_out.splitlines() if l.strip()]
|
||||
# 2. Check Docker availability on VPS
|
||||
code_docker, stdout_docker, _ = await run_remote_quick('docker --version', settings)
|
||||
docker_installed = (code_docker == 0)
|
||||
docker_version = stdout_docker.strip() if docker_installed else "Not detected"
|
||||
|
||||
dir_ok = "DIR_OK" in p_lines
|
||||
compose_ok = "COMPOSE_OK" in p_lines
|
||||
db_file = None
|
||||
for l in p_lines:
|
||||
if l.endswith((".db", ".sqlite")):
|
||||
db_file = l
|
||||
break
|
||||
# 3. Check Pangolin Directory
|
||||
pangolin_dir = settings.get("vps_pangolin_dir", "/opt/pangolin")
|
||||
chk_dir_cmd = f'[ -d "{pangolin_dir}" ] && echo "DIR_EXISTS" || echo "DIR_MISSING"'
|
||||
_, stdout_dir, _ = await run_remote_quick(chk_dir_cmd, settings)
|
||||
pangolin_dir_found = "DIR_EXISTS" in stdout_dir
|
||||
|
||||
# 4. Check SQLite database status in Pangolin directory
|
||||
chk_db_cmd = f'find "{pangolin_dir}" -name "*.db" -o -name "*.sqlite" 2>/dev/null'
|
||||
_, stdout_db, _ = await run_remote_quick(chk_db_cmd, settings)
|
||||
dbs_found = [p.strip() for p in stdout_db.strip().split("\n") if p.strip()]
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"connected": True,
|
||||
"hostname": hostname,
|
||||
"os": os_name,
|
||||
"os_name": os_name,
|
||||
"kernel": kernel,
|
||||
"uptime": uptime_str,
|
||||
"docker_installed": docker_found,
|
||||
"docker_running": docker_running,
|
||||
"pangolin_dir_exists": dir_ok,
|
||||
"compose_file_exists": compose_ok,
|
||||
"database_file": db_file,
|
||||
"message": f"Successfully connected to {hostname} ({host}) via SSH!"
|
||||
"docker_installed": docker_installed,
|
||||
"docker_version": docker_version,
|
||||
"pangolin_dir_found": pangolin_dir_found,
|
||||
"sqlite_databases": dbs_found,
|
||||
"message": "SSH Connection successful! Remote host is ready for backups and updates."
|
||||
}
|
||||
+4
-3
@@ -5,7 +5,8 @@ set -e
|
||||
DATA_PATH="${DATA_DIR:-/data}"
|
||||
BACKUP_PATH="${BACKUP_DIR:-/backups}"
|
||||
|
||||
mkdir -p "$DATA_PATH" "$BACKUP_PATH"
|
||||
mkdir -p "$DATA_PATH" "$BACKUP_PATH" "$DATA_PATH/.ssh"
|
||||
export HOME="$DATA_PATH"
|
||||
|
||||
# Set secure permissions on SSH private key if it exists
|
||||
if [ -f "$DATA_PATH/id_ed25519" ]; then
|
||||
@@ -22,9 +23,9 @@ if [ "$PUID" -ne 0 ] && [ "$PGID" -ne 0 ]; then
|
||||
groupadd -g "$PGID" pangolin 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Create or update user
|
||||
# Create or update user with persistent home in DATA_PATH
|
||||
if ! getent passwd pangolin >/dev/null 2>&1; then
|
||||
useradd -u "$PUID" -g "$PGID" -d /app -s /bin/bash pangolin 2>/dev/null || true
|
||||
useradd -u "$PUID" -g "$PGID" -d "$DATA_PATH" -s /bin/bash pangolin 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Ensure ownership of persistent data and backup volumes
|
||||
|
||||
Reference in new issue
Block a user