// SSH over an open WebRTC datachannel (a tag bridged to the guest's sshd on 22), with // @microsoft/dev-tunnels-ssh (pure TS, WebCrypto) injected as `ssh` ({...namespace, Buffer}). // The library speaks ecdsa/rsa host keys, not ed25519, so the host key is reported, not // pinned: the guest is authenticated by the DTLS fingerprint the bidder signed (vm.onNetwork). // ponytail: one exec channel per command (no pty); add a shell channel + xterm if a real terminal is wanted. // received: what the channel delivered before ssh took it over (sshd speaks first: its banner). export async function connectSsh(ssh, dc, keyPair, user, received = []) { class DataChannelStream extends ssh.BaseStream { constructor() { super(); for (const data of received) this.onData(ssh.Buffer.from(data)); dc.onmessage = ({ data }) => this.onData(ssh.Buffer.from(data)); dc.onclose = () => this.onEnd(); } async write(data) { dc.send(data); } async close() { dc.close(); } } const session = new ssh.SshClientSession(new ssh.SshSessionConfiguration()); let hostKey; session.onAuthenticating((e) => { hostKey = e.publicKey; e.authenticationPromise = Promise.resolve({}); }); await session.connect(new DataChannelStream()); if (!(await session.authenticateServer())) throw new Error("ssh: server authentication failed"); const digest = await crypto.subtle.digest("SHA-256", await hostKey.getPublicKeyBytes()); const fingerprint = `SHA256:${new Uint8Array(digest).toBase64({ omitPadding: true })}`; if (!(await session.authenticateClient({ username: user, publicKeys: [keyPair] }))) throw new Error(`ssh: ${user} rejected the key`); return { hostKey: { type: hostKey.keyAlgorithmName, fingerprint }, // stdin (optional): written to the command, then EOF; secrets go here, never in command // (the guest's shell runs the command as an argument, so ps shows it). async exec(command, stdin) { const channel = await session.openChannel(); let output = ""; const decoder = new TextDecoder(); channel.onDataReceived((data) => { output += decoder.decode(data, { stream: true }); channel.adjustWindow(data.length); }); const closed = new Promise((resolve) => channel.onClosed(resolve)); const request = new ssh.CommandRequestMessage(); request.requestType = "exec"; request.command = command; request.wantReply = true; if (!(await channel.request(request))) throw new Error("ssh: exec refused"); if (stdin !== undefined) { if (stdin) await channel.send(ssh.Buffer.from(stdin)); await channel.send(ssh.Buffer.alloc(0)); // an empty send is EOF } await closed; return output; }, close: () => session.close(), }; } // An ecdsa-sha2-nistp256 key pair and its authorized_keys line. export async function sshKeyPair(ssh) { const keyPair = await ssh.SshAlgorithms.publicKey.ecdsaSha2Nistp256.generateKeyPair(); return { keyPair, authorizedKey: `ecdsa-sha2-nistp256 ${(await keyPair.getPublicKeyBytes()).toString("base64")} webrtc-page` }; }