Skip to content

Commit

Permalink
Merge pull request #203 from prona-p4-learning-platform/add-jumphost-…
Browse files Browse the repository at this point in the history
…support-for-lsp-connection

added support for lsp connection over SSH jumphost
  • Loading branch information
srieger1 authored Jul 17, 2024
2 parents 36d544e + afcb818 commit 011c536
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
7 changes: 6 additions & 1 deletion backend/src/Environment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SSHConsole, { Console } from "./consoles/SSHConsole";
import SSHConsole, { Console, JumpHost } from "./consoles/SSHConsole";
import FileHandler from "./filehandler/SSHFileHandler";
import {
InstanceProvider,
Expand Down Expand Up @@ -461,6 +461,11 @@ export default class Environment {
return endpoint.IPAddress;
}

async getJumphost(): Promise<JumpHost | undefined> {
const endpoint = await this.makeSureInstanceExists();
return endpoint.SSHJumpHost;
}

async makeSureInstanceExists(createIfMissing?: boolean): Promise<VMEndpoint> {
return new Promise<VMEndpoint>((resolve, reject) => {
this.persister
Expand Down
79 changes: 75 additions & 4 deletions backend/src/websocket/LanguageServerHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import WebSocket from "ws";
import Environment from "../Environment";
import { Client } from "ssh2";
import { AddressInfo, createServer, Server } from "net";
import fs from "fs";

export default function (
wsFromBrowser: WebSocket,
Expand All @@ -12,9 +15,78 @@ export default function (
Promise.all([
envInstance.getLanguageServerPort(),
envInstance.getIPAddress(),
envInstance.getJumphost(),
])
.then((result) => {
const [port, ipAddress] = result;
let [port, ipAddress] = result;
const jumpHost = result[2];
if (jumpHost !== undefined) {
console.log(
"Establishing SSH lsp connection " +
ipAddress +
":" +
port +
" via jump host " +
jumpHost.ipaddress +
":" +
jumpHost.port,
);
const sshJumpHostConnection = new Client();
let srv: Server;
sshJumpHostConnection
.on("ready", () => {
srv = createServer((socket) => {
sshJumpHostConnection.forwardOut(
"127.0.0.1",
0,
ipAddress,
port,
(err, stream) => {
if (err) {
console.log(
"Unable to forward lsp connection on jump host: " + err.message,
);
sshJumpHostConnection.end();
socket.end();
srv.close();
} else {
socket.pipe(stream);
}
},
);
});
srv.listen(0, () => {
const srvIpAddress = (srv.address() as AddressInfo).address;
const srvPort = (srv.address() as AddressInfo).port;
ipAddress = srvIpAddress;
port = srvPort;
console.log("Forwarding lsp connection from " + srvIpAddress + ":" + srvPort + " over " + jumpHost.ipaddress + ":" + jumpHost.port + " to " + ipAddress + ":" + port);
});
})
.on("close", () => {
console.log("SSH jumphost lsp connection close");
sshJumpHostConnection.end();
srv && srv.close();
})
.on("error", (err) => {
console.log("SSH jumphost lsp connection error: " + err.message);
sshJumpHostConnection.end();
srv && srv.close();
})
.connect({
host: jumpHost.ipaddress,
port: jumpHost.port,
username: jumpHost.username,
password: jumpHost.password,
privateKey: jumpHost.privateKey
? fs.readFileSync(jumpHost.privateKey)
: undefined,
//debug: (debug) => {
// console.log(debug)
//},
readyTimeout: 1000,
});
}
const wsToLanguageServer = new WebSocket(
"ws://" + ipAddress + ":" + port + "/" + language,
);
Expand Down Expand Up @@ -49,9 +121,8 @@ export default function (
wsFromBrowser.on("close", () => {
console.log("LanguageServer WebSocket closed...");
wsToLanguageServer.close();
});
})
.catch((err) => {
});
}).catch((err) => {
console.log(err);
wsFromBrowser.send(
"Could not connect to environment language server, closing connection.",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/FileEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,8 @@ export default class FileEditor extends Component<FileEditorProps> {

Y.applyUpdate(doc, toUint8Array(data.content));

// Check for double document, if environment was not undeployed

// Websocket provider
this.collaborationProvider = new WebsocketProvider(
`${window?.location?.protocol === "http:" || undefined ? "ws:" : "wss:"}//` +
Expand Down

0 comments on commit 011c536

Please sign in to comment.