Доступ по SSH
В AgentBox нет отдельного метода для SSH. Если нужен интерактивный SSH-клиент, создайте многоразовый шаблон с OpenSSH и WebSocket-мостом.
Установите websocat на свой компьютер и подготовьте пару ключей Ed25519. По умолчанию пример читает ~/.ssh/id_ed25519 и ~/.ssh/id_ed25519.pub.
import { readFile as readSshPublicKey } from "node:fs/promises";
import { homedir as homeDirectory } from "node:os";
import pathForSsh from "node:path";
import { spawnSync as runSsh } from "node:child_process";
import { Sandbox, Template, waitForPort } from "@abox-dev/sdk";
const templateName = process.env.TEMPLATE_NAME ?? "ssh-ready";
const sshTemplate = Template()
.fromTemplate("base")
.aptInstall(["openssh-server", "curl"])
.runCmd(
"case $(uname -m) in " +
"x86_64) asset=websocat.x86_64-unknown-linux-musl ;; " +
"aarch64|arm64) asset=websocat.aarch64-unknown-linux-musl ;; " +
"*) exit 1 ;; esac && " +
"curl -fsSL -o /usr/local/bin/websocat " +
'"https://github.com/vi/websocat/releases/download/v1.14.1/$asset" && ' +
"chmod 755 /usr/local/bin/websocat",
{ user: "root" },
)
.setStartCmd(
"sudo mkdir -p /run/sshd && " +
"sudo ssh-keygen -A && " +
"sudo /usr/sbin/sshd -E /tmp/sshd.log && " +
"sudo /usr/local/bin/websocat -b --exit-on-eof " +
"ws-l:0.0.0.0:8081 tcp:127.0.0.1:22",
waitForPort(8081),
);
await Template.build(sshTemplate, templateName);
const publicKeyPath =
process.env.AGENTBOX_SSH_PUBLIC_KEY ??
pathForSsh.join(homeDirectory(), ".ssh/id_ed25519.pub");
const privateKeyPath =
process.env.AGENTBOX_SSH_PRIVATE_KEY ??
pathForSsh.join(homeDirectory(), ".ssh/id_ed25519");
const publicKey = (await readSshPublicKey(publicKeyPath, "utf8")).trim();
const sandbox = await Sandbox.create(templateName, {
network: { allowPublicTraffic: false },
});
try {
await sandbox.commands.run(
"sudo install -d -m 700 -o user -g user /home/user/.ssh && " +
"printf '%s\\n' \"$SSH_PUBLIC_KEY\" | " +
"sudo tee /home/user/.ssh/authorized_keys >/dev/null && " +
"sudo chown user:user /home/user/.ssh/authorized_keys && " +
"sudo chmod 600 /home/user/.ssh/authorized_keys",
{ envs: { SSH_PUBLIC_KEY: publicKey } },
);
const proxyCommand = process.env.AGENTBOX_SSH_PROXY_COMMAND?.replaceAll(
"__SANDBOX_ID__",
sandbox.sandboxId,
).replaceAll("__TRAFFIC_TOKEN__", sandbox.trafficAccessToken ?? "");
const connection = runSsh(
"ssh",
[
"-i",
privateKeyPath,
"-o",
"IdentitiesOnly=yes",
"-o",
"StrictHostKeyChecking=accept-new",
"-o",
`ProxyCommand=${proxyCommand ?? `websocat --binary -B 65536 -H="Agentbox-Traffic-Access-Token: ${sandbox.trafficAccessToken}" wss://8081-${sandbox.sandboxId}.agentbox-runtime.ru`}`,
`user@${sandbox.sandboxId}`,
"printf 'SSH connection works'",
],
{
encoding: "utf8",
env: process.env,
},
);
if (connection.status !== 0) throw new Error(connection.stderr);
console.log(connection.stdout);
} finally {
await sandbox.kill();
}Шаблон достаточно собрать один раз, а затем использовать для новых песочниц. В каждую песочницу добавляется только открытый ключ. WebSocket-порт закрыт от публичного доступа, поэтому процесс ssh передаёт токен трафика в websocat и не выводит его в терминал. Чтобы открыть интерактивную оболочку, уберите последнюю удалённую команду и подключите ввод и вывод процесса к терминалу.
Храните закрытый ключ и токен трафика как секреты. Не добавляйте их в шаблон, репозиторий, метаданные песочницы или вывод команд.