1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

kubectl proxy: switch to unix sockets

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2020-05-11 15:48:53 +03:00
parent e21e0b577b
commit 2baa85b2c3
4 changed files with 32 additions and 29 deletions

View File

@ -91,6 +91,10 @@ export class Cluster implements ClusterInfo {
return this.kubeconfigManager.getPath() return this.kubeconfigManager.getPath()
} }
public proxySocketPath() {
return this.kubeconfigManager.getProxySocketPath()
}
public async init(kc: KubeConfig) { public async init(kc: KubeConfig) {
this.contextHandler = new ContextHandler(kc, this) this.contextHandler = new ContextHandler(kc, this)
this.contextName = kc.currentContext this.contextName = kc.currentContext

View File

@ -1,15 +1,12 @@
import { KubeConfig, CoreV1Api } from "@kubernetes/client-node" import { KubeConfig, CoreV1Api } from "@kubernetes/client-node"
import { readFileSync } from "fs"
import * as http from "http" import * as http from "http"
import { ServerOptions } from "http-proxy" import { ServerOptions } from "http-proxy"
import * as url from "url" import * as url from "url"
import logger from "./logger" import logger from "./logger"
import { getFreePort } from "./port"
import { KubeAuthProxy } from "./kube-auth-proxy" import { KubeAuthProxy } from "./kube-auth-proxy"
import { Cluster, ClusterPreferences } from "./cluster" import { Cluster, ClusterPreferences } from "./cluster"
import { prometheusProviders } from "../common/prometheus-providers" import { prometheusProviders } from "../common/prometheus-providers"
import { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry" import { PrometheusService, PrometheusProvider } from "./prometheus/provider-registry"
import { PrometheusLens } from "./prometheus/lens"
export class ContextHandler { export class ContextHandler {
public contextName: string public contextName: string
@ -148,7 +145,7 @@ export class ContextHandler {
"Host": this.clusterUrl.hostname "Host": this.clusterUrl.hostname
}, },
target: { target: {
port: await this.resolveProxyPort(), socketPath: this.cluster.proxySocketPath(),
protocol: "http://", protocol: "http://",
host: "localhost", host: "localhost",
path: this.clusterUrl.path path: this.clusterUrl.path
@ -156,21 +153,6 @@ export class ContextHandler {
} }
} }
protected async resolveProxyPort(): Promise<number> {
if (this.proxyPort) return this.proxyPort
let serverPort: number = null
try {
serverPort = await getFreePort()
} catch(error) {
logger.error(error)
throw(error)
}
this.proxyPort = serverPort
return serverPort
}
public applyHeaders(req: http.IncomingMessage) { public applyHeaders(req: http.IncomingMessage) {
req.headers["authorization"] = `Bearer ${this.id}` req.headers["authorization"] = `Bearer ${this.id}`
} }
@ -185,12 +167,11 @@ export class ContextHandler {
public async ensureServer() { public async ensureServer() {
if (!this.proxyServer) { if (!this.proxyServer) {
const proxyPort = await this.resolveProxyPort()
const proxyEnv = Object.assign({}, process.env) const proxyEnv = Object.assign({}, process.env)
if (this.cluster.preferences && this.cluster.preferences.httpsProxy) { if (this.cluster.preferences && this.cluster.preferences.httpsProxy) {
proxyEnv.HTTPS_PROXY = this.cluster.preferences.httpsProxy proxyEnv.HTTPS_PROXY = this.cluster.preferences.httpsProxy
} }
this.proxyServer = new KubeAuthProxy(this.cluster, proxyPort, proxyEnv) this.proxyServer = new KubeAuthProxy(this.cluster, proxyEnv)
await this.proxyServer.run() await this.proxyServer.run()
} }
} }

View File

@ -1,26 +1,24 @@
import { spawn, ChildProcess } from "child_process" import { spawn, ChildProcess } from "child_process"
import logger from "./logger" import logger from "./logger"
import * as tcpPortUsed from "tcp-port-used"
import { Kubectl, bundledKubectl } from "./kubectl" import { Kubectl, bundledKubectl } from "./kubectl"
import { Cluster } from "./cluster" import { Cluster } from "./cluster"
import { readFileSync, watch } from "fs" import { readFileSync, watch, existsSync } from "fs"
import { PromiseIpc } from "electron-promise-ipc" import { PromiseIpc } from "electron-promise-ipc"
import { findMainWebContents } from "./webcontents" import { findMainWebContents } from "./webcontents"
import * as url from "url" import * as url from "url"
export class KubeAuthProxy { export class KubeAuthProxy {
public lastError: string public lastError: string
protected cluster: Cluster protected cluster: Cluster
protected env: NodeJS.ProcessEnv = null protected env: NodeJS.ProcessEnv = null
protected proxyProcess: ChildProcess protected proxyProcess: ChildProcess
protected port: number
protected kubectl: Kubectl protected kubectl: Kubectl
protected promiseIpc: any protected promiseIpc: any
constructor(cluster: Cluster, port: number, env: NodeJS.ProcessEnv) { constructor(cluster: Cluster, env: NodeJS.ProcessEnv) {
this.env = env this.env = env
this.port = port
this.cluster = cluster this.cluster = cluster
this.kubectl = bundledKubectl this.kubectl = bundledKubectl
this.promiseIpc = new PromiseIpc({ timeout: 2000 }) this.promiseIpc = new PromiseIpc({ timeout: 2000 })
@ -47,9 +45,9 @@ export class KubeAuthProxy {
const clusterUrl = url.parse(this.cluster.apiUrl) const clusterUrl = url.parse(this.cluster.apiUrl)
let args = [ let args = [
"proxy", "proxy",
"-p", this.port.toString(),
"--kubeconfig", this.cluster.kubeconfigPath(), "--kubeconfig", this.cluster.kubeconfigPath(),
"--accept-hosts", clusterUrl.hostname, "--accept-hosts", clusterUrl.hostname,
"-u", this.cluster.proxySocketPath(),
] ]
if (process.env.DEBUG_PROXY === "true") { if (process.env.DEBUG_PROXY === "true") {
args = args.concat(["-v", "9"]) args = args.concat(["-v", "9"])
@ -77,7 +75,19 @@ export class KubeAuthProxy {
this.sendIpcLogMessage(data.toString(), "stderr") this.sendIpcLogMessage(data.toString(), "stderr")
}) })
return tcpPortUsed.waitUntilUsed(this.port, 500, 10000) return await this.waitUnixSocket(this.cluster.proxySocketPath())
}
protected waitUnixSocket(socket: string): Promise<void> {
return new Promise((resolve, reject) => {
let done = false
while(!done) { // TODO: fix busy loop
if (existsSync(socket)) {
done = true
resolve()
}
}
})
} }
protected parseError(data: string) { protected parseError(data: string) {

View File

@ -7,19 +7,25 @@ export class KubeconfigManager {
protected configDir = app.getPath("temp") protected configDir = app.getPath("temp")
protected kubeconfig: string protected kubeconfig: string
protected tempFile: string protected tempFile: string
protected tempProxySocket: string
constructor(kubeconfig: string) { constructor(kubeconfig: string) {
this.kubeconfig = kubeconfig this.kubeconfig = kubeconfig
this.tempFile = this.createTemporaryKubeconfig() this.tempFile = this.createTemporaryKubeconfig()
this.tempProxySocket = `${this.configDir}${randomFileName("proxy")}.sock`
} }
public getPath() { public getPath() {
return this.tempFile return this.tempFile
} }
public getProxySocketPath() {
return this.tempProxySocket
}
protected createTemporaryKubeconfig(): string { protected createTemporaryKubeconfig(): string {
ensureDir(this.configDir) ensureDir(this.configDir)
const path = `${this.configDir}/${randomFileName("kubeconfig")}` const path = `${this.configDir}${randomFileName("kubeconfig")}`
logger.debug('Creating temporary kubeconfig: ' + path) logger.debug('Creating temporary kubeconfig: ' + path)
fs.writeFileSync(path, this.kubeconfig) fs.writeFileSync(path, this.kubeconfig)
return path return path
@ -28,5 +34,7 @@ export class KubeconfigManager {
public unlink() { public unlink() {
logger.debug('Deleting temporary kubeconfig: ' + this.tempFile) logger.debug('Deleting temporary kubeconfig: ' + this.tempFile)
fs.unlinkSync(this.tempFile) fs.unlinkSync(this.tempFile)
logger.debug('Deleting temporary proxy socket: ' + this.tempFile)
fs.unlinkSync(this.tempProxySocket)
} }
} }