From 18d1e735d8f50d606a385789880c9ecacd7c6f52 Mon Sep 17 00:00:00 2001 From: isaacclad Date: Fri, 26 Jun 2026 23:55:22 +0000 Subject: [PATCH] Jail the remote (SSH) terminal path too, not just local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accounts whose node has an ssh_private_key take the remote branch, which still launched an unjailed 'runuser' shell — leaving the breach open. Route it through the same jailkit launcher (no sudo; provider connects as root). Now every terminal session is chrooted to the customer's own home. Co-Authored-By: Claude Opus 4.8 --- .../RunHostingTerminalWorkerCommand.php | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/app/Console/Commands/RunHostingTerminalWorkerCommand.php b/app/Console/Commands/RunHostingTerminalWorkerCommand.php index a13cff6..27b027d 100644 --- a/app/Console/Commands/RunHostingTerminalWorkerCommand.php +++ b/app/Console/Commands/RunHostingTerminalWorkerCommand.php @@ -143,7 +143,10 @@ class RunHostingTerminalWorkerCommand extends Command throw new \RuntimeException('Unable to establish an SSH connection for the browser terminal.'); } - $remoteCommand = "runuser -u {$account->username} -- bash -lc ".escapeshellarg($launchCommand); + // SECURITY: jail the remote shell too. provider->connect() logs in as + // root, so the launcher runs without sudo and chroots the customer into + // the node's jail (only their own home visible) — never an unjailed shell. + $remoteCommand = $this->buildJailedLaunchCommand($account, $relativePath, false); return new RemoteSshShellSession($ssh, $remoteCommand, $cols, $rows); } @@ -156,17 +159,21 @@ class RunHostingTerminalWorkerCommand extends Command * /etc, secrets). Invoked via sudo -n, scoped in /etc/sudoers.d/ladill-jailsh * because the terminal worker runs as www-data. */ - private function buildJailedLaunchCommand(HostingAccount $account, string $relativePath): string + private function buildJailedLaunchCommand(HostingAccount $account, string $relativePath, bool $sudo = true): string { $launcher = (string) config('hosting.terminal_jail_launcher', '/usr/local/sbin/ladill-jailsh'); $rel = $relativePath !== '' ? $relativePath : '/public_html'; - return implode(' ', [ - 'sudo', '-n', - escapeshellarg($launcher), - escapeshellarg($account->username), - escapeshellarg($rel), - ]); + $parts = []; + if ($sudo) { + $parts[] = 'sudo'; + $parts[] = '-n'; + } + $parts[] = escapeshellarg($launcher); + $parts[] = escapeshellarg($account->username); + $parts[] = escapeshellarg($rel); + + return implode(' ', $parts); } private function buildUserShellBootstrap(HostingAccount $account, string $relativePath, int $cols, int $rows): string