diff --git a/package.json b/package.json index ab6191117a..586b5ae55c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "postversion": "git push --set-upstream ${GIT_REMOTE:-origin} release/v$npm_package_version" }, "config": { - "k8sProxyVersion": "0.1.5", + "k8sProxyVersion": "0.2.1", "bundledKubectlVersion": "1.23.3", "bundledHelmVersion": "3.7.2", "sentryDsn": "" diff --git a/src/common/__tests__/cluster-store.test.ts b/src/common/__tests__/cluster-store.test.ts index bf074f7324..9bfc5346ed 100644 --- a/src/common/__tests__/cluster-store.test.ts +++ b/src/common/__tests__/cluster-store.test.ts @@ -24,8 +24,6 @@ import { createClusterInjectionToken } from "../cluster/create-cluster-injection import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import kubeAuthProxyCaInjectable from "../../main/kube-auth-proxy/kube-auth-proxy-ca.injectable"; -import createKubeAuthProxyCertFilesInjectable from "../../main/kube-auth-proxy/create-kube-auth-proxy-cert-files.injectable"; console = new Console(stdout, stderr); @@ -89,8 +87,6 @@ describe("cluster-store", () => { mainDi = dis.mainDi; mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); - mainDi.override(createKubeAuthProxyCertFilesInjectable, () => ({} as any)); - mainDi.override(kubeAuthProxyCaInjectable, () => ({} as any)); await dis.runSetups(); diff --git a/src/main/__test__/context-handler.test.ts b/src/main/__test__/context-handler.test.ts index 4563707d0c..9541889519 100644 --- a/src/main/__test__/context-handler.test.ts +++ b/src/main/__test__/context-handler.test.ts @@ -10,7 +10,6 @@ import mockFs from "mock-fs"; import { getDiForUnitTesting } from "../getDiForUnitTesting"; import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable"; import type { Cluster } from "../../common/cluster/cluster"; -import kubeAuthProxyCaInjectable from "../kube-auth-proxy/kube-auth-proxy-ca.injectable"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; jest.mock("electron", () => ({ @@ -83,7 +82,6 @@ describe("ContextHandler", () => { }); di.override(createKubeAuthProxyInjectable, () => ({} as any)); - di.override(kubeAuthProxyCaInjectable, () => ({} as any)); await di.runSetups(); diff --git a/src/main/__test__/kube-auth-proxy.test.ts b/src/main/__test__/kube-auth-proxy.test.ts index 497ccfaa53..348112e791 100644 --- a/src/main/__test__/kube-auth-proxy.test.ts +++ b/src/main/__test__/kube-auth-proxy.test.ts @@ -52,8 +52,6 @@ import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-p import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token"; import path from "path"; import spawnInjectable from "../child-process/spawn.injectable"; -import kubeAuthProxyCaInjectable from "../kube-auth-proxy/kube-auth-proxy-ca.injectable"; -import createKubeAuthProxyCertFilesInjectable from "../kube-auth-proxy/create-kube-auth-proxy-cert-files.injectable"; console = new Console(stdout, stderr); @@ -97,8 +95,6 @@ describe("kube auth proxy tests", () => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); di.override(spawnInjectable, () => mockSpawn); - di.override(createKubeAuthProxyCertFilesInjectable, () => ({} as any)); - di.override(kubeAuthProxyCaInjectable, () => ({} as any)); mockFs(mockMinikubeConfig); diff --git a/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts b/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts index 75c58aa036..4d3820fd37 100644 --- a/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts +++ b/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts @@ -14,11 +14,8 @@ import { ClusterManager } from "../../cluster-manager"; import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token"; -import directoryForKubeConfigsInjectable - from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; -import kubeAuthProxyCaInjectable from "../../kube-auth-proxy/kube-auth-proxy-ca.injectable"; -import createKubeAuthProxyCertFilesInjectable from "../../kube-auth-proxy/create-kube-auth-proxy-cert-files.injectable"; - +import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; +import { ClusterStore } from "../../../common/cluster-store/cluster-store"; jest.mock("electron", () => ({ app: { @@ -42,9 +39,6 @@ describe("kubeconfig-sync.source tests", () => { beforeEach(async () => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); - di.override(kubeAuthProxyCaInjectable, () => Promise.resolve(Buffer.from("ca"))); - di.override(createKubeAuthProxyCertFilesInjectable, () => ({} as any)); - mockFs(); await di.runSetups(); @@ -62,6 +56,7 @@ describe("kubeconfig-sync.source tests", () => { afterEach(() => { mockFs.restore(); ClusterManager.resetInstance(); + ClusterStore.resetInstance(); }); describe("configsToModels", () => { diff --git a/src/main/context-handler/context-handler.ts b/src/main/context-handler/context-handler.ts index d08d5ace1f..2eed4e1ad7 100644 --- a/src/main/context-handler/context-handler.ts +++ b/src/main/context-handler/context-handler.ts @@ -28,7 +28,7 @@ interface PrometheusServicePreferences { interface Dependencies { createKubeAuthProxy: CreateKubeAuthProxy; - authProxyCa: Promise; + authProxyCa: string; } export class ContextHandler { @@ -123,7 +123,7 @@ export class ContextHandler { return `https://127.0.0.1:${this.kubeAuthProxy.port}${this.kubeAuthProxy.apiPrefix}${path}`; } - async resolveAuthProxyCa() { + resolveAuthProxyCa() { return this.dependencies.authProxyCa; } @@ -140,7 +140,7 @@ export class ContextHandler { protected async newApiTarget(timeout: number): Promise { await this.ensureServer(); - const caFileContents = await this.resolveAuthProxyCa(); + const ca = this.dependencies.authProxyCa; const clusterPath = this.clusterUrl.path !== "/" ? this.clusterUrl.path : ""; const apiPrefix = `${this.kubeAuthProxy.apiPrefix}${clusterPath}`; @@ -150,7 +150,7 @@ export class ContextHandler { host: "127.0.0.1", port: this.kubeAuthProxy.port, path: apiPrefix, - ca: caFileContents.toString(), + ca, }, changeOrigin: true, timeout, diff --git a/src/main/context-handler/create-context-handler.injectable.ts b/src/main/context-handler/create-context-handler.injectable.ts index c08bd4276a..86867ccdfb 100644 --- a/src/main/context-handler/create-context-handler.injectable.ts +++ b/src/main/context-handler/create-context-handler.injectable.ts @@ -3,23 +3,27 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import selfsigned from "selfsigned"; import type { Cluster } from "../../common/cluster/cluster"; import { ContextHandler } from "./context-handler"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; -import kubeAuthProxyCaInjectable from "../kube-auth-proxy/kube-auth-proxy-ca.injectable"; +import { getKubeAuthProxyCertificate } from "../kube-auth-proxy/get-kube-auth-proxy-certificate"; +import URLParse from "url-parse"; const createContextHandlerInjectable = getInjectable({ id: "create-context-handler", instantiate: (di) => { - const authProxyCa = di.inject(kubeAuthProxyCaInjectable); + return (cluster: Cluster) => { + const clusterUrl = new URLParse(cluster.apiUrl); - const dependencies = { - createKubeAuthProxy: di.inject(createKubeAuthProxyInjectable), - authProxyCa, + const dependencies = { + createKubeAuthProxy: di.inject(createKubeAuthProxyInjectable), + authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert, + }; + + return new ContextHandler(dependencies, cluster); }; - - return (cluster: Cluster) => new ContextHandler(dependencies, cluster); }, }); diff --git a/src/main/kube-auth-proxy/create-kube-auth-proxy-cert-files.injectable.ts b/src/main/kube-auth-proxy/create-kube-auth-proxy-cert-files.injectable.ts deleted file mode 100644 index 297a43daa4..0000000000 --- a/src/main/kube-auth-proxy/create-kube-auth-proxy-cert-files.injectable.ts +++ /dev/null @@ -1,26 +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 * as selfsigned from "selfsigned"; -import { createKubeAuthProxyCertFiles } from "./create-kube-auth-proxy-cert-files"; -import writeFileInjectable from "../../common/fs/write-file.injectable"; -import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import path from "path"; - -const createKubeAuthProxyCertFilesInjectable = getInjectable({ - id: "create-kube-auth-proxy-cert-files", - - instantiate: async (di) => { - const userData = di.inject(directoryForUserDataInjectable); - const certPath = path.join(userData, "kube-auth-proxy"); - - return createKubeAuthProxyCertFiles(certPath, { - generate: selfsigned.generate, - writeFile: di.inject(writeFileInjectable), - }); - }, -}); - -export default createKubeAuthProxyCertFilesInjectable; diff --git a/src/main/kube-auth-proxy/create-kube-auth-proxy-cert-files.ts b/src/main/kube-auth-proxy/create-kube-auth-proxy-cert-files.ts deleted file mode 100644 index 325495dc7a..0000000000 --- a/src/main/kube-auth-proxy/create-kube-auth-proxy-cert-files.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import path from "path"; -import type * as selfsigned from "selfsigned"; - -type SelfSignedGenerate = typeof selfsigned.generate; - -interface CreateKubeAuthProxyCertificateFilesDependencies { - generate: SelfSignedGenerate; - writeFile: (path: string, content: string | Buffer) => Promise; -} - -function getKubeAuthProxyCertificate(generate: SelfSignedGenerate): selfsigned.SelfSignedCert { - const opts = [ - { name: "commonName", value: "Lens Certificate Authority" }, - { name: "organizationName", value: "Lens" }, - ]; - - return generate(opts, { - keySize: 2048, - algorithm: "sha256", - days: 365, - extensions: [ - { name: "basicConstraints", cA: true }, - { name: "subjectAltName", altNames: [ - { type: 2, value: "localhost" }, - { type: 7, ip: "127.0.0.1" }, - ] }, - ], - }); -} - -export async function createKubeAuthProxyCertFiles(dir: string, dependencies: CreateKubeAuthProxyCertificateFilesDependencies): Promise { - const cert = getKubeAuthProxyCertificate(dependencies.generate); - - await dependencies.writeFile(path.join(dir, "proxy.key"), cert.private); - await dependencies.writeFile(path.join(dir, "proxy.crt"), cert.cert); - - return dir; -} diff --git a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts index b566782197..9a8504c675 100644 --- a/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts +++ b/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts @@ -6,10 +6,11 @@ import { getInjectable } from "@ogre-tools/injectable"; import { KubeAuthProxy, KubeAuthProxyDependencies } from "./kube-auth-proxy"; import type { Cluster } from "../../common/cluster/cluster"; import path from "path"; +import selfsigned from "selfsigned"; import { getBinaryName } from "../../common/vars"; import directoryForBundledBinariesInjectable from "../../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable"; import spawnInjectable from "../child-process/spawn.injectable"; -import createKubeAuthProxyCertFilesInjectable from "./create-kube-auth-proxy-cert-files.injectable"; +import { getKubeAuthProxyCertificate } from "./get-kube-auth-proxy-certificate"; export type CreateKubeAuthProxy = (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => KubeAuthProxy; @@ -18,15 +19,17 @@ const createKubeAuthProxyInjectable = getInjectable({ instantiate: (di): CreateKubeAuthProxy => { const binaryName = getBinaryName("lens-k8s-proxy"); - const dependencies: KubeAuthProxyDependencies = { - proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName), - proxyCertPath: di.inject(createKubeAuthProxyCertFilesInjectable), - spawn: di.inject(spawnInjectable), - }; - return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => ( - new KubeAuthProxy(dependencies, cluster, environmentVariables) - ); + return (cluster: Cluster, environmentVariables: NodeJS.ProcessEnv) => { + const clusterUrl = new URL(cluster.apiUrl); + const dependencies: KubeAuthProxyDependencies = { + proxyBinPath: path.join(di.inject(directoryForBundledBinariesInjectable), binaryName), + proxyCert: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate), + spawn: di.inject(spawnInjectable), + }; + + return new KubeAuthProxy(dependencies, cluster, environmentVariables); + }; }, }); diff --git a/src/main/kube-auth-proxy/get-kube-auth-proxy-certificate.ts b/src/main/kube-auth-proxy/get-kube-auth-proxy-certificate.ts new file mode 100644 index 0000000000..fb4c13da60 --- /dev/null +++ b/src/main/kube-auth-proxy/get-kube-auth-proxy-certificate.ts @@ -0,0 +1,39 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type * as selfsigned from "selfsigned"; + +type SelfSignedGenerate = typeof selfsigned.generate; + +const certCache: Map = new Map(); + +export function getKubeAuthProxyCertificate(hostname: string, generate: SelfSignedGenerate, useCache = true): selfsigned.SelfSignedCert { + if (useCache && certCache.has(hostname)) { + return certCache.get(hostname); + } + + const opts = [ + { name: "commonName", value: "Lens Certificate Authority" }, + { name: "organizationName", value: "Lens" }, + ]; + + const cert = generate(opts, { + keySize: 2048, + algorithm: "sha256", + days: 365, + extensions: [ + { name: "basicConstraints", cA: true }, + { name: "subjectAltName", altNames: [ + { type: 2, value: hostname }, + { type: 2, value: "localhost" }, + { type: 7, ip: "127.0.0.1" }, + ] }, + ], + }); + + certCache.set(hostname, cert); + + return cert; +} diff --git a/src/main/kube-auth-proxy/kube-auth-proxy-ca.injectable.ts b/src/main/kube-auth-proxy/kube-auth-proxy-ca.injectable.ts deleted file mode 100644 index 37aaafeeee..0000000000 --- a/src/main/kube-auth-proxy/kube-auth-proxy-ca.injectable.ts +++ /dev/null @@ -1,22 +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 path from "path"; -import readFileInjectable from "../../common/fs/read-file.injectable"; -import createKubeAuthProxyCertFilesInjectable from "./create-kube-auth-proxy-cert-files.injectable"; - -const kubeAuthProxyCaInjectable = getInjectable({ - id: "kube-auth-proxy-ca", - - instantiate: async (di) => { - const certPath = await di.inject(createKubeAuthProxyCertFilesInjectable); - - const readFile = di.inject(readFileInjectable); - - return readFile(path.join(certPath, "proxy.crt")); - }, -}); - -export default kubeAuthProxyCaInjectable; diff --git a/src/main/kube-auth-proxy/kube-auth-proxy.ts b/src/main/kube-auth-proxy/kube-auth-proxy.ts index f25ea0f97b..dfeff29c98 100644 --- a/src/main/kube-auth-proxy/kube-auth-proxy.ts +++ b/src/main/kube-auth-proxy/kube-auth-proxy.ts @@ -10,12 +10,13 @@ import type { Cluster } from "../../common/cluster/cluster"; import logger from "../logger"; import { getPortFrom } from "../utils/get-port"; import { makeObservable, observable, when } from "mobx"; +import type { SelfSignedCert } from "selfsigned"; const startingServeRegex = /starting to serve on (?
.+)/i; export interface KubeAuthProxyDependencies { proxyBinPath: string; - proxyCertPath: Promise; + proxyCert: SelfSignedCert; spawn: typeof spawn; } @@ -44,7 +45,7 @@ export class KubeAuthProxy { } const proxyBin = this.dependencies.proxyBinPath; - const certPath = await this.dependencies.proxyCertPath; + const cert = this.dependencies.proxyCert; this.proxyProcess = this.dependencies.spawn(proxyBin, [], { env: { @@ -52,7 +53,8 @@ export class KubeAuthProxy { KUBECONFIG: this.cluster.kubeConfigPath, KUBECONFIG_CONTEXT: this.cluster.contextName, API_PREFIX: this.apiPrefix, - CERT_PATH: certPath, + PROXY_KEY: cert.private, + PROXY_CERT: cert.cert, }, }); this.proxyProcess.on("error", (error) => { diff --git a/src/renderer/components/dock/logs/view.tsx b/src/renderer/components/dock/logs/view.tsx index 0f9069dafa..6385b1ca9c 100644 --- a/src/renderer/components/dock/logs/view.tsx +++ b/src/renderer/components/dock/logs/view.tsx @@ -57,8 +57,9 @@ const NonInjectedLogsDockTab = observer(({ className, tab, model, subscribeStore setTimeout(() => { const overlay = document.querySelector(".PodLogs .list span.active"); - if (!overlay) return; - overlay.scrollIntoViewIfNeeded(); + if (typeof overlay?.scrollIntoViewIfNeeded === "function") { + overlay.scrollIntoViewIfNeeded(); + } }, 100); }; diff --git a/src/renderer/components/tabs/tabs.tsx b/src/renderer/components/tabs/tabs.tsx index 3913886365..58e94f33dc 100644 --- a/src/renderer/components/tabs/tabs.tsx +++ b/src/renderer/components/tabs/tabs.tsx @@ -81,7 +81,9 @@ export class Tab extends React.PureComponent { } scrollIntoView() { - this.ref.current?.scrollIntoViewIfNeeded(); + if (typeof this.ref.current?.scrollIntoViewIfNeeded === "function") { + this.ref.current.scrollIntoViewIfNeeded(); + } } @boundMethod diff --git a/types/dom.d.ts b/types/dom.d.ts index eb6bc83dbe..0e3e1bcb43 100644 --- a/types/dom.d.ts +++ b/types/dom.d.ts @@ -6,7 +6,7 @@ export {}; declare global { interface Element { - scrollIntoViewIfNeeded(opt_center?: boolean): void; + scrollIntoViewIfNeeded?(opt_center?: boolean): void; } interface Window {