From 72390ab2670d8fb5aa020aab3549c9f7fe562eb3 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Thu, 14 Sep 2023 11:29:57 -0500 Subject: [PATCH 1/5] fix: Handle Proxy WS upgrade manually for WS handshakes Signed-off-by: Carlos Esteban Lopez --- .changeset/clean-icons-wonder.md | 5 ++++ plugins/kubernetes-backend/package.json | 1 + .../src/service/KubernetesProxy.test.ts | 4 --- .../src/service/KubernetesProxy.ts | 28 ++++++++++++++++--- yarn.lock | 1 + 5 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 .changeset/clean-icons-wonder.md diff --git a/.changeset/clean-icons-wonder.md b/.changeset/clean-icons-wonder.md new file mode 100644 index 0000000000..b6beb58969 --- /dev/null +++ b/.changeset/clean-icons-wonder.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Handle Proxy WS upgrade manually for WS handshakes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index f755dc2c6b..19ded3ba59 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -75,6 +75,7 @@ "express-promise-router": "^4.1.0", "fs-extra": "10.1.0", "helmet": "^6.0.0", + "http-proxy": "^1.18.1", "http-proxy-middleware": "^2.0.6", "lodash": "^4.17.21", "luxon": "^3.0.0", diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts index a84482082f..75ae6bcf27 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts @@ -769,10 +769,6 @@ describe('KubernetesProxy', () => { ), ); - // Prepopulate the proxy so the WebSocket upgrade can happen, result doesn't actually matter - const result = await fetch(wsProxyAddress.replace('ws', 'http')); - expect(result.ok).toBeFalsy(); - const webSocket = new WebSocket(wsProxyAddress); const connectMessagePromise = eventPromiseFactory(webSocket, 'message'); diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.ts index 6e699d1220..c9a88854b2 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { ErrorResponseBody, ForwardedError, @@ -24,16 +23,19 @@ import { import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; import { kubernetesProxyPermission } from '@backstage/plugin-kubernetes-common'; import { - PermissionEvaluator, AuthorizeResult, + PermissionEvaluator, } from '@backstage/plugin-permission-common'; import { bufferFromFileOrString } from '@kubernetes/client-node'; -import type { Request, RequestHandler } from 'express'; +import { IncomingMessage, ServerResponse } from 'http'; +import Server from 'http-proxy'; import { createProxyMiddleware } from 'http-proxy-middleware'; import { Logger } from 'winston'; import { AuthenticationStrategy } from '../auth'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; +import type { Request, RequestHandler } from 'express'; + export const APPLICATION_JSON: string = 'application/json'; /** @@ -121,7 +123,25 @@ export class KubernetesProxy { } const middleware = await this.getMiddleware(req); - middleware(req, res, next); + if ( + req.header('connection')?.toLowerCase() === 'upgrade' && + req.header('upgrade')?.toLowerCase() === 'websocket' + ) { + type IPartialUpgradeHandler = ( + ...args: Partial< + Parameters< + Server>['ws'] + > + > + ) => void; + ( + middleware as unknown as { + upgrade: IPartialUpgradeHandler; + } + ).upgrade(req, req.socket); + } else { + middleware(req, res, next); + } }; } diff --git a/yarn.lock b/yarn.lock index 0176682861..762737c213 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7651,6 +7651,7 @@ __metadata: express-promise-router: ^4.1.0 fs-extra: 10.1.0 helmet: ^6.0.0 + http-proxy: ^1.18.1 http-proxy-middleware: ^2.0.6 lodash: ^4.17.21 luxon: ^3.0.0 From 69dee739be02907241f81a3ff76d00679b78b9e9 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Thu, 14 Sep 2023 13:04:53 -0500 Subject: [PATCH 2/5] fix: Remove unused dependency Signed-off-by: Carlos Esteban Lopez --- plugins/kubernetes-backend/package.json | 1 - plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts | 1 - yarn.lock | 1 - 3 files changed, 3 deletions(-) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 19ded3ba59..d2b01c333b 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -90,7 +90,6 @@ "@backstage/cli": "workspace:^", "@types/aws4": "^1.5.1", "@types/http-proxy-middleware": "^0.19.3", - "cross-fetch": "^3.1.5", "mock-fs": "^5.2.0", "msw": "^1.0.0", "supertest": "^6.1.3", diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts index 75ae6bcf27..6b5430c7d1 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts @@ -46,7 +46,6 @@ import { HEADER_KUBERNETES_CLUSTER, KubernetesProxy, } from './KubernetesProxy'; -import fetch from 'cross-fetch'; import type { Request } from 'express'; diff --git a/yarn.lock b/yarn.lock index 762737c213..177173d0ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7646,7 +7646,6 @@ __metadata: "@types/luxon": ^3.0.0 compression: ^1.7.4 cors: ^2.8.5 - cross-fetch: ^3.1.5 express: ^4.17.1 express-promise-router: ^4.1.0 fs-extra: 10.1.0 From bbb750442accb8a6ce095af7d36ce55e9c2be248 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Fri, 15 Sep 2023 09:09:55 -0500 Subject: [PATCH 3/5] refactor: Refactor simplify types for proxy upgrade call Signed-off-by: Carlos Esteban Lopez --- plugins/kubernetes-backend/package.json | 1 - .../src/service/KubernetesProxy.ts | 22 +++++-------------- yarn.lock | 1 - 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index d2b01c333b..8cfe7a31b9 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -75,7 +75,6 @@ "express-promise-router": "^4.1.0", "fs-extra": "10.1.0", "helmet": "^6.0.0", - "http-proxy": "^1.18.1", "http-proxy-middleware": "^2.0.6", "lodash": "^4.17.21", "luxon": "^3.0.0", diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.ts index c9a88854b2..cf06be6bba 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.ts @@ -27,14 +27,12 @@ import { PermissionEvaluator, } from '@backstage/plugin-permission-common'; import { bufferFromFileOrString } from '@kubernetes/client-node'; -import { IncomingMessage, ServerResponse } from 'http'; -import Server from 'http-proxy'; -import { createProxyMiddleware } from 'http-proxy-middleware'; +import { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware'; import { Logger } from 'winston'; import { AuthenticationStrategy } from '../auth'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; -import type { Request, RequestHandler } from 'express'; +import type { Request } from 'express'; export const APPLICATION_JSON: string = 'application/json'; @@ -123,22 +121,14 @@ export class KubernetesProxy { } const middleware = await this.getMiddleware(req); + + // If req is an upgrade handshake, use middleware upgrade instead of http request handler https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade if ( req.header('connection')?.toLowerCase() === 'upgrade' && req.header('upgrade')?.toLowerCase() === 'websocket' ) { - type IPartialUpgradeHandler = ( - ...args: Partial< - Parameters< - Server>['ws'] - > - > - ) => void; - ( - middleware as unknown as { - upgrade: IPartialUpgradeHandler; - } - ).upgrade(req, req.socket); + // Missing the `head`, since it's optional we pass undefined to avoid type issues + middleware.upgrade!(req, req.socket, undefined); } else { middleware(req, res, next); } diff --git a/yarn.lock b/yarn.lock index 177173d0ac..8258933b0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7650,7 +7650,6 @@ __metadata: express-promise-router: ^4.1.0 fs-extra: 10.1.0 helmet: ^6.0.0 - http-proxy: ^1.18.1 http-proxy-middleware: ^2.0.6 lodash: ^4.17.21 luxon: ^3.0.0 From 50f8786089dead8933c71705af195f0b4ce21227 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Sun, 17 Sep 2023 19:24:06 -0500 Subject: [PATCH 4/5] chore: Update API Reports Signed-off-by: Carlos Esteban Lopez --- plugins/kubernetes-backend/api-report.md | 2 +- plugins/kubernetes-backend/src/service/KubernetesProxy.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 051706bbd5..158b917a48 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -18,7 +18,7 @@ import { Logger } from 'winston'; import type { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; -import type { RequestHandler } from 'express'; +import { RequestHandler } from 'http-proxy-middleware'; import { TokenCredential } from '@azure/identity'; // @public (undocumented) diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.ts index cf06be6bba..d2a3649c27 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.ts @@ -29,6 +29,7 @@ import { import { bufferFromFileOrString } from '@kubernetes/client-node'; import { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware'; import { Logger } from 'winston'; + import { AuthenticationStrategy } from '../auth'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; From 1199394270bf8dd86c05f49834fd7c1ec3d0d861 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Mon, 18 Sep 2023 12:45:59 -0500 Subject: [PATCH 5/5] chore: Remove logs & fix type dep issue Signed-off-by: Carlos Esteban Lopez --- plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 8cfe7a31b9..17da706d66 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -68,6 +68,7 @@ "@jest-mock/express": "^2.0.1", "@kubernetes/client-node": "0.18.1", "@types/express": "^4.17.6", + "@types/http-proxy-middleware": "^0.19.3", "@types/luxon": "^3.0.0", "compression": "^1.7.4", "cors": "^2.8.5", @@ -88,7 +89,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/aws4": "^1.5.1", - "@types/http-proxy-middleware": "^0.19.3", "mock-fs": "^5.2.0", "msw": "^1.0.0", "supertest": "^6.1.3", diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts index 6b5430c7d1..327d580d10 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts @@ -756,7 +756,6 @@ describe('KubernetesProxy', () => { const wsProxyAddress = `ws://127.0.0.1:${proxyPort}${proxyPath}${wsPath}`; const wsAddress = `ws://localhost:${wsPort}${wsPath}`; - console.log('Ports: ', wsProxyAddress, wsAddress); // Let this request through so it reaches the express router above worker.use(