mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
1 parent
ed6f5226bd
commit
87dc14cbfb
9 files changed
+143
-98
No files matched your search
@@ -3,38 +3,44 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable";
|
||||
import openShellSessionInjectable from "../../shell-session/create-shell-session.injectable";
|
||||
import type { LensProxyApiRequest } from "../lens-proxy";
|
||||
import URLParse from "url-parse";
|
||||
import { Server as WebSocketServer } from "ws";
|
||||
import { URL } from "url";
|
||||
import { WebSocketServer } from "ws";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import shellApiAuthenticatorInjectable from "../../../features/terminal/main/shell-api-authenticator.injectable";
|
||||
import clusterManagerInjectable from "../../cluster/manager.injectable";
|
||||
import openShellSessionInjectable from "../../shell-session/create-shell-session.injectable";
|
||||
import getClusterForRequestInjectable from "../get-cluster-for-request.injectable";
|
||||
import type { ProxyApiRequestArgs } from "./types";
|
||||
|
||||
const shellApiRequestInjectable = getInjectable({
|
||||
id: "shell-api-request",
|
||||
|
||||
instantiate: (di): LensProxyApiRequest => {
|
||||
instantiate: (di) => {
|
||||
const openShellSession = di.inject(openShellSessionInjectable);
|
||||
const authenticateRequest = di.inject(shellRequestAuthenticatorInjectable).authenticate;
|
||||
const getClusterForRequest = di.inject(getClusterForRequestInjectable);
|
||||
const clusterManager = di.inject(clusterManagerInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const shellApiAuthenticator = di.inject(shellApiAuthenticatorInjectable);
|
||||
const getClusterForRequest = di.inject(getClusterForRequestInjectable);
|
||||
|
||||
return ({ req, socket, head }) => {
|
||||
return ({ req, socket, head }: ProxyApiRequestArgs): void => {
|
||||
const cluster = getClusterForRequest(req);
|
||||
const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true);
|
||||
const { searchParams } = new URL(req.url);
|
||||
const tabId = searchParams.get("id");
|
||||
const nodeName = searchParams.get("node");
|
||||
const shellToken = searchParams.get("shellToken");
|
||||
|
||||
if (!tabId || !cluster || !authenticateRequest(cluster.id, tabId, shellToken)) {
|
||||
if (!tabId || !cluster || !shellApiAuthenticator.authenticate(cluster.id, tabId, shellToken)) {
|
||||
socket.write("Invalid shell request");
|
||||
socket.end();
|
||||
} else {
|
||||
const ws = new WebSocketServer({ noServer: true });
|
||||
|
||||
ws.handleUpgrade(req, socket, head, (websocket) => {
|
||||
openShellSession({ websocket, cluster, tabId, nodeName })
|
||||
.catch(error => logger.error(`[SHELL-SESSION]: failed to open a ${nodeName ? "node" : "local"} shell`, error));
|
||||
});
|
||||
return void socket.end();
|
||||
}
|
||||
|
||||
const ws = new WebSocketServer({ noServer: true });
|
||||
|
||||
ws.handleUpgrade(req, socket, head, (websocket) => {
|
||||
openShellSession({ websocket, cluster, tabId, nodeName })
|
||||
.catch(error => logger.error(`[SHELL-SESSION]: failed to open a ${nodeName ? "node" : "local"} shell`, error));
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { ShellRequestAuthenticator } from "./shell-request-authenticator";
|
||||
|
||||
const shellRequestAuthenticatorInjectable = getInjectable({
|
||||
id: "shell-request-authenticator",
|
||||
|
||||
instantiate: () => {
|
||||
const authenticator = new ShellRequestAuthenticator();
|
||||
|
||||
authenticator.init();
|
||||
|
||||
return authenticator;
|
||||
},
|
||||
});
|
||||
|
||||
export default shellRequestAuthenticatorInjectable;
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getOrInsertMap } from "../../../../common/utils";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
import { ipcMainHandle } from "../../../../common/ipc";
|
||||
import crypto from "crypto";
|
||||
import { promisify } from "util";
|
||||
|
||||
const randomBytes = promisify(crypto.randomBytes);
|
||||
|
||||
export class ShellRequestAuthenticator {
|
||||
private tokens = new Map<ClusterId, Map<string, Uint8Array>>();
|
||||
|
||||
init() {
|
||||
ipcMainHandle("cluster:shell-api", async (event, clusterId, tabId) => {
|
||||
const authToken = Uint8Array.from(await randomBytes(128));
|
||||
const forCluster = getOrInsertMap(this.tokens, clusterId);
|
||||
|
||||
forCluster.set(tabId, authToken);
|
||||
|
||||
return authToken;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates a single use token for creating a new shell
|
||||
* @param clusterId The `ClusterId` for the shell
|
||||
* @param tabId The ID for the shell
|
||||
* @param token The value that is being presented as a one time authentication token
|
||||
* @returns `true` if `token` was valid, false otherwise
|
||||
*/
|
||||
authenticate = (clusterId: ClusterId, tabId: string, token: string | undefined): boolean => {
|
||||
const clusterTokens = this.tokens.get(clusterId);
|
||||
|
||||
if (!clusterTokens || !tabId || !token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const authToken = clusterTokens.get(tabId);
|
||||
const buf = Uint8Array.from(Buffer.from(token, "base64"));
|
||||
|
||||
if (authToken instanceof Uint8Array && authToken.length === buf.length && crypto.timingSafeEqual(authToken, buf)) {
|
||||
// remove the token because it is a single use token
|
||||
clusterTokens.delete(tabId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export interface OpenShellSessionArgs {
|
||||
websocket: WebSocket;
|
||||
cluster: Cluster;
|
||||
tabId: string;
|
||||
nodeName?: string;
|
||||
nodeName: string | null;
|
||||
}
|
||||
|
||||
export type OpenShellSession = (args: OpenShellSessionArgs) => Promise<void>;
|
||||
|
||||
Reference in new issue
Block a user