Skip to content

Commit

Permalink
Inject Host header to k8s client requests (#887)
Browse files Browse the repository at this point in the history
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
  • Loading branch information
jakolehm authored Sep 16, 2020
1 parent ec4d859 commit c2afd02
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 32 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
"semver": "^7.3.2",
"serializr": "^2.0.3",
"shell-env": "^3.0.0",
"spdy": "^4.0.2",
"tar": "^6.0.2",
"tcp-port-used": "^1.0.1",
"tempy": "^0.5.0",
Expand Down Expand Up @@ -248,6 +249,7 @@
"@types/request-promise-native": "^1.0.17",
"@types/semver": "^7.2.0",
"@types/shelljs": "^0.8.8",
"@types/spdy": "^3.4.4",
"@types/tcp-port-used": "^1.0.0",
"@types/tempy": "^0.3.0",
"@types/terser-webpack-plugin": "^3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/main/cluster-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class ClusterManager {
// lens-server is connecting to 127.0.0.1:<port>/<uid>
if (req.headers.host.startsWith("127.0.0.1")) {
const clusterId = req.url.split("/")[1]
const cluster = clusterStore.getById(clusterId)
cluster = clusterStore.getById(clusterId)
if (cluster) {
// we need to swap path prefix so that request is proxied to kube api
req.url = req.url.replace(`/${clusterId}`, apiKubePrefix)
Expand Down
2 changes: 1 addition & 1 deletion src/main/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class Cluster implements ClusterModel {
async init(port: number) {
try {
this.contextHandler = new ContextHandler(this);
this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler);
this.kubeconfigManager = new KubeconfigManager(this, this.contextHandler, port);
this.kubeProxyUrl = `http://localhost:${port}${apiKubePrefix}`;
this.initialized = true;
logger.info(`[CLUSTER]: "${this.contextName}" init success`, {
Expand Down
8 changes: 6 additions & 2 deletions src/main/kubeconfig-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class KubeconfigManager {
protected configDir = app.getPath("temp")
protected tempFile: string;

constructor(protected cluster: Cluster, protected contextHandler: ContextHandler) {
constructor(protected cluster: Cluster, protected contextHandler: ContextHandler, protected port: number) {
this.init();
}

Expand All @@ -28,6 +28,10 @@ export class KubeconfigManager {
return this.tempFile;
}

protected resolveProxyUrl() {
return `http://127.0.0.1:${this.port}/${this.cluster.id}`
}

/**
* Creates new "temporary" kubeconfig that point to the kubectl-proxy.
* This way any user of the config does not need to know anything about the auth etc. details.
Expand All @@ -42,7 +46,7 @@ export class KubeconfigManager {
clusters: [
{
name: contextName,
server: await contextHandler.resolveAuthProxyUrl(),
server: this.resolveProxyUrl(),
skipTLSVerify: undefined,
}
],
Expand Down
67 changes: 40 additions & 27 deletions src/main/lens-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import net from "net";
import http from "http";
import spdy from "spdy";
import httpProxy from "http-proxy";
import url from "url";
import * as WebSocket from "ws"
import { apiPrefix, apiKubePrefix } from "../common/vars"
import { openShell } from "./node-shell-session";
import { Router } from "./router"
import { ClusterManager } from "./cluster-manager"
import { ContextHandler } from "./context-handler";
import { apiKubePrefix } from "../common/vars";
import logger from "./logger"

export class LensProxy {
Expand Down Expand Up @@ -40,37 +41,49 @@ export class LensProxy {

protected buildCustomProxy(): http.Server {
const proxy = this.createProxy();
const customProxy = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {
this.handleRequest(proxy, req, res);
});
customProxy.on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
this.handleWsUpgrade(req, socket, head)
});
customProxy.on("error", (err) => {
const spdyProxy = spdy.createServer({
spdy: {
plain: true,
connection: {
autoSpdy31: true
}
}
}, (req: http.IncomingMessage, res: http.ServerResponse) => {
this.handleRequest(proxy, req, res)
})
spdyProxy.on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
if (req.url.startsWith(`${apiPrefix}/?`)) {
this.handleWsUpgrade(req, socket, head)
} else {
if (req.headers.upgrade?.startsWith("SPDY")) {
this.handleSpdyProxy(proxy, req, socket, head)
} else {
socket.end()
}
}
})
spdyProxy.on("error", (err) => {
logger.error("proxy error", err)
});
return customProxy;
})
return spdyProxy
}

protected async handleSpdyProxy(proxy: httpProxy, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
const cluster = this.clusterManager.getClusterForRequest(req)
if (cluster) {
const proxyUrl = await cluster.contextHandler.resolveAuthProxyUrl() + req.url.replace(apiKubePrefix, "")
const apiUrl = url.parse(cluster.apiUrl)
const res = new http.ServerResponse(req)
res.assignSocket(socket)
res.setHeader("Location", proxyUrl)
res.setHeader("Host", apiUrl.hostname)
res.statusCode = 302
res.end()
}
}

protected createProxy(): httpProxy {
const proxy = httpProxy.createProxyServer();
proxy.on("proxyRes", (proxyRes, req, res) => {
if (req.method !== "GET") {
return;
}
if (proxyRes.statusCode === 502) {
const cluster = this.clusterManager.getClusterForRequest(req)
const proxyError = cluster?.contextHandler.proxyLastError;
if (proxyError) {
return res.writeHead(502).end(proxyError);
}
}
const reqId = this.getRequestId(req);
if (this.retryCounters.has(reqId)) {
logger.debug(`Resetting proxy retry cache for url: ${reqId}`);
this.retryCounters.delete(reqId)
}
})
proxy.on("error", (error, req, res, target) => {
if (this.closed) {
return;
Expand Down
69 changes: 68 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,13 @@
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==

"@types/spdy@^3.4.4":
version "3.4.4"
resolved "https://registry.yarnpkg.com/@types/spdy/-/spdy-3.4.4.tgz#3282fd4ad8c4603aa49f7017dd520a08a345b2bc"
integrity sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==
dependencies:
"@types/node" "*"

"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
Expand Down Expand Up @@ -5922,6 +5929,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=

handle-thing@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==

handlebars@^4.7.6:
version "4.7.6"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e"
Expand Down Expand Up @@ -6108,6 +6120,16 @@ hosted-git-info@^3.0.4:
dependencies:
lru-cache "^5.1.1"

hpack.js@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=
dependencies:
inherits "^2.0.1"
obuf "^1.0.0"
readable-stream "^2.0.1"
wbuf "^1.1.0"

html-encoding-sniffer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
Expand Down Expand Up @@ -6170,6 +6192,11 @@ http-cache-semantics@^4.0.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==

http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=

http-proxy@^1.18.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
Expand Down Expand Up @@ -8636,6 +8663,11 @@ object.pick@^1.3.0:
dependencies:
isobject "^3.0.1"

obuf@^1.0.0, obuf@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==

oidc-token-hash@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-3.0.2.tgz#5bd4716cc48ad433f4e4e99276811019b165697e"
Expand Down Expand Up @@ -9753,7 +9785,7 @@ read-pkg@^5.2.0:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"

readable-stream@^3.1.1, readable-stream@^3.6.0:
readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
Expand Down Expand Up @@ -10284,6 +10316,11 @@ scss-tokenizer@^0.2.3:
js-base64 "^2.1.8"
source-map "^0.4.2"

select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=

semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
Expand Down Expand Up @@ -10593,6 +10630,29 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==

spdy-transport@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
dependencies:
debug "^4.1.0"
detect-node "^2.0.4"
hpack.js "^2.1.6"
obuf "^1.1.2"
readable-stream "^3.0.6"
wbuf "^1.7.3"

spdy@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b"
integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==
dependencies:
debug "^4.1.0"
handle-thing "^2.0.0"
http-deceiver "^1.2.7"
select-hose "^2.0.0"
spdy-transport "^3.0.0"

spectron@11.0.0:
version "11.0.0"
resolved "https://registry.yarnpkg.com/spectron/-/spectron-11.0.0.tgz#79d785e6b8898638e77b5186711e3910ed4ca09b"
Expand Down Expand Up @@ -11783,6 +11843,13 @@ watchpack@^1.6.1:
chokidar "^3.4.0"
watchpack-chokidar2 "^2.0.0"

wbuf@^1.1.0, wbuf@^1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
dependencies:
minimalistic-assert "^1.0.0"

wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
Expand Down

0 comments on commit c2afd02

Please sign in to comment.