Fix SSH auth: respect ~/.ssh/config instead of defaulting to root

CoulombCore (92.205.130.254) authenticates as tegwick via id_ops in
ssh config, not root. Omit SANDBOXER_SSH_USER to let OpenSSH apply config;
set SANDBOXER_SSH_USER only to override.
This commit is contained in:
tegwick 2026-06-23 14:16:16 +02:00
parent 8a39eaba34
commit 939c4e1aff
5 changed files with 36 additions and 12 deletions

View file

@ -18,7 +18,7 @@ class ComposeSSHExtension:
def __init__(self, config: dict[str, Any] | None = None) -> None:
cfg = config or {}
self.base_dir: str = cfg.get("base_dir", "/tmp/sandboxer")
self.ssh_user: str = cfg.get("ssh_user", "root")
self.ssh_user: str | None = cfg.get("ssh_user")
self.compose_timeout_s: int = int(cfg.get("compose_timeout_s", 180))
def provision(
@ -33,7 +33,7 @@ class ComposeSSHExtension:
sandbox_id = inputs.get("sandbox_id") or str(uuid.uuid4())[:8]
remote_dir = f"{self.base_dir}/{sandbox_id}"
ssh = SSHConfig.from_env(host, user=self.ssh_user)
ssh = SSHConfig.from_env(host, user=self.ssh_user or None)
rc, out = ssh.run(f"mkdir -p {remote_dir}")
if rc != 0:
@ -64,7 +64,8 @@ class ComposeSSHExtension:
def wait_ready(self, handle: dict[str, str]) -> dict[str, str]:
"""Confirm compose services are running (no HTTP health polling)."""
ssh = SSHConfig.from_env(handle["host"], user=handle.get("ssh_user", self.ssh_user))
ssh_user = handle.get("ssh_user") or self.ssh_user or None
ssh = SSHConfig.from_env(handle["host"], user=ssh_user)
project = handle["compose_project"]
remote_dir = handle["remote_dir"]
compose_file = handle["compose_file"]
@ -83,7 +84,8 @@ class ComposeSSHExtension:
}
def teardown(self, handle: dict[str, str]) -> dict[str, str]:
ssh = SSHConfig.from_env(handle["host"], user=handle.get("ssh_user", self.ssh_user))
ssh_user = handle.get("ssh_user") or self.ssh_user or None
ssh = SSHConfig.from_env(handle["host"], user=ssh_user)
project = handle.get("compose_project")
remote_dir = handle.get("remote_dir")
compose_file = handle.get("compose_file")

View file

@ -11,19 +11,27 @@ from pathlib import Path
@dataclass
class SSHConfig:
host: str
user: str = "root"
user: str | None = None
key: str | None = None
connect_timeout: int = 15
@property
def destination(self) -> str:
"""SSH destination; omit user when unset so ~/.ssh/config applies."""
if self.user:
return f"{self.user}@{self.host}"
return self.host
@property
def target(self) -> str:
return f"{self.user}@{self.host}"
return self.destination
@classmethod
def from_env(cls, host: str, *, user: str | None = None, key: str | None = None) -> SSHConfig:
env_user = os.environ.get("SANDBOXER_SSH_USER")
return cls(
host=host,
user=user or os.environ.get("SANDBOXER_SSH_USER", "root"),
user=user if user is not None else (env_user or None),
key=key or os.environ.get("SANDBOXER_SSH_KEY"),
)
@ -39,7 +47,7 @@ class SSHConfig:
]
if self.key:
args += ["-i", self.key]
args.append(self.target)
args.append(self.destination)
return args
def run(self, cmd: str, *, timeout: int = 60) -> tuple[int, str]: