@backstage/plugin-kubernetes-backend 0.21.7-next.0 → 0.21.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"KubernetesRouter.cjs.js","sources":["../../src/service/KubernetesRouter.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Config } from '@backstage/config';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,\n kubernetesClustersReadPermission,\n kubernetesPermissions,\n kubernetesResourcesReadPermission,\n} from '@backstage/plugin-kubernetes-common';\nimport { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\nimport express from 'express';\nimport Router from 'express-promise-router';\n\nimport { DispatchStrategy } from '../auth';\n\nimport {\n AuthService,\n BackstageCredentials,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n AuthenticationStrategy,\n AuthMetadata,\n KubernetesClustersSupplier,\n KubernetesFetcher,\n KubernetesObjectsProvider,\n KubernetesRouterFactory,\n KubernetesServiceLocator,\n} from '@backstage/plugin-kubernetes-node';\nimport { addResourceRoutesToRouter } from '../routes/resourcesRoutes';\nimport { ObjectsByEntityRequest } from '../types/types';\nimport { KubernetesProxy } from './KubernetesProxy';\nimport { requirePermission } from '../auth/requirePermission';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport interface KubernetesEnvironment {\n logger: LoggerService;\n config: Config;\n catalog: CatalogService;\n discovery: DiscoveryService;\n permissions: PermissionEvaluator;\n auth: AuthService;\n httpAuth: HttpAuthService;\n authStrategyMap: { [key: string]: AuthenticationStrategy };\n fetcher: KubernetesFetcher;\n clusterSupplier: KubernetesClustersSupplier;\n serviceLocator: KubernetesServiceLocator;\n objectsProvider: KubernetesObjectsProvider;\n customRouter?: KubernetesRouterFactory;\n}\n\nexport class KubernetesRouter {\n static create(env: KubernetesEnvironment) {\n return new KubernetesRouter(env);\n }\n\n protected readonly env: KubernetesEnvironment;\n\n constructor(env: KubernetesEnvironment) {\n this.env = env;\n }\n\n public async getRouter() {\n const {\n logger,\n config,\n permissions,\n authStrategyMap,\n clusterSupplier,\n objectsProvider,\n catalog,\n discovery,\n httpAuth,\n customRouter,\n } = this.env;\n\n logger.info('Initializing Kubernetes backend');\n\n if (!config.has('kubernetes')) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error('Kubernetes configuration is missing');\n }\n logger.warn(\n 'Failed to initialize kubernetes backend: kubernetes config is missing',\n );\n return Router();\n }\n\n const proxy = this.buildProxy(\n logger,\n clusterSupplier,\n discovery,\n httpAuth,\n authStrategyMap,\n );\n\n return (\n customRouter?.({\n getDefault: () =>\n this.buildDefaultRouter(\n objectsProvider,\n clusterSupplier,\n catalog,\n proxy,\n permissions,\n httpAuth,\n authStrategyMap,\n ),\n objectsProvider,\n clusterSupplier,\n authStrategyMap,\n }) ??\n this.buildDefaultRouter(\n objectsProvider,\n clusterSupplier,\n catalog,\n proxy,\n permissions,\n httpAuth,\n authStrategyMap,\n )\n );\n }\n\n private buildProxy(\n logger: LoggerService,\n clusterSupplier: KubernetesClustersSupplier,\n discovery: DiscoveryService,\n httpAuth: HttpAuthService,\n authStrategyMap: { [key: string]: AuthenticationStrategy },\n ): KubernetesProxy {\n const authStrategy = new DispatchStrategy({\n authStrategyMap,\n });\n return new KubernetesProxy({\n logger,\n clusterSupplier,\n authStrategy,\n discovery,\n httpAuth,\n });\n }\n\n private buildDefaultRouter(\n objectsProvider: KubernetesObjectsProvider,\n clusterSupplier: KubernetesClustersSupplier,\n catalog: CatalogService,\n proxy: KubernetesProxy,\n permissionApi: PermissionEvaluator,\n httpAuth: HttpAuthService,\n authStrategyMap: { [key: string]: AuthenticationStrategy },\n ): express.Router {\n const logger = this.env.logger;\n const router = Router();\n router.use('/proxy', proxy.createRequestHandler({ permissionApi }));\n router.use(express.json());\n router.use(\n createPermissionIntegrationRouter({\n permissions: kubernetesPermissions,\n }),\n );\n\n // @deprecated\n router.post('/services/:serviceId', async (req, res) => {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const serviceId = req.params.serviceId;\n const requestBody: ObjectsByEntityRequest = req.body;\n try {\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity: requestBody.entity,\n auth: requestBody.auth || {},\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n } catch (e) {\n logger.error(\n `action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`,\n );\n res.status(500).json({ error: e.message });\n }\n });\n\n router.get('/clusters', async (req, res) => {\n await requirePermission(\n permissionApi,\n kubernetesClustersReadPermission,\n httpAuth,\n req,\n );\n const credentials = await httpAuth.credentials(req);\n const clusterDetails = await this.fetchClusterDetails(clusterSupplier, {\n credentials,\n });\n res.json({\n items: clusterDetails.map(cd => {\n const oidcTokenProvider =\n cd.authMetadata[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];\n const authProvider =\n cd.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n const strategy = authStrategyMap[authProvider];\n let auth: AuthMetadata = {};\n if (strategy) {\n auth = strategy.presentAuthMetadata(cd.authMetadata);\n }\n\n return {\n name: cd.name,\n title: cd.title,\n dashboardUrl: cd.dashboardUrl,\n authProvider,\n ...(oidcTokenProvider && { oidcTokenProvider }),\n ...(auth && Object.keys(auth).length !== 0 && { auth }),\n };\n }),\n });\n });\n\n addResourceRoutesToRouter(\n router,\n catalog,\n objectsProvider,\n httpAuth,\n permissionApi,\n );\n\n return router;\n }\n\n private async fetchClusterDetails(\n clusterSupplier: KubernetesClustersSupplier,\n options: { credentials: BackstageCredentials },\n ) {\n const clusterDetails = await clusterSupplier.getClusters(options);\n\n this.env.logger.debug(\n `action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,\n );\n\n return clusterDetails;\n }\n}\n"],"names":["Router","DispatchStrategy","KubernetesProxy","express","createPermissionIntegrationRouter","kubernetesPermissions","requirePermission","kubernetesResourcesReadPermission","kubernetesClustersReadPermission","ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER","ANNOTATION_KUBERNETES_AUTH_PROVIDER","addResourceRoutesToRouter"],"mappings":"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"}
1
+ {"version":3,"file":"KubernetesRouter.cjs.js","sources":["../../src/service/KubernetesRouter.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Config } from '@backstage/config';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,\n kubernetesClustersReadPermission,\n kubernetesPermissions,\n kubernetesResourcesReadPermission,\n} from '@backstage/plugin-kubernetes-common';\nimport { PermissionEvaluator } from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\nimport express from 'express';\nimport Router from 'express-promise-router';\n\nimport { DispatchStrategy } from '../auth';\nimport { NotAllowedError, toError } from '@backstage/errors';\n\nimport {\n AuthService,\n AuditorService,\n BackstageCredentials,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n AuthenticationStrategy,\n AuthMetadata,\n KubernetesClustersSupplier,\n KubernetesFetcher,\n KubernetesObjectsProvider,\n KubernetesRouterFactory,\n KubernetesServiceLocator,\n} from '@backstage/plugin-kubernetes-node';\nimport { addResourceRoutesToRouter } from '../routes/resourcesRoutes';\nimport { ObjectsByEntityRequest } from '../types/types';\nimport { KubernetesProxy } from './KubernetesProxy';\nimport { requirePermission } from '../auth/requirePermission';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { resolveProxyMiddlewareCacheOptions } from './ProxyMiddlewareCache';\n\nexport interface KubernetesEnvironment {\n logger: LoggerService;\n config: Config;\n catalog: CatalogService;\n discovery: DiscoveryService;\n permissions: PermissionEvaluator;\n auth: AuthService;\n httpAuth: HttpAuthService;\n auditor: AuditorService;\n authStrategyMap: { [key: string]: AuthenticationStrategy };\n fetcher: KubernetesFetcher;\n clusterSupplier: KubernetesClustersSupplier;\n serviceLocator: KubernetesServiceLocator;\n objectsProvider: KubernetesObjectsProvider;\n customRouter?: KubernetesRouterFactory;\n}\n\nexport class KubernetesRouter {\n static create(env: KubernetesEnvironment) {\n return new KubernetesRouter(env);\n }\n\n protected readonly env: KubernetesEnvironment;\n\n constructor(env: KubernetesEnvironment) {\n this.env = env;\n }\n\n public async getRouter() {\n const {\n logger,\n config,\n permissions,\n authStrategyMap,\n clusterSupplier,\n objectsProvider,\n catalog,\n discovery,\n httpAuth,\n customRouter,\n } = this.env;\n\n logger.info('Initializing Kubernetes backend');\n\n if (!config.has('kubernetes')) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error('Kubernetes configuration is missing');\n }\n logger.warn(\n 'Failed to initialize kubernetes backend: kubernetes config is missing',\n );\n return Router();\n }\n\n await this.warnForClustersWithSkipTLSVerify(logger, clusterSupplier);\n\n const proxy = this.buildProxy(\n logger,\n clusterSupplier,\n discovery,\n httpAuth,\n authStrategyMap,\n );\n\n return (\n customRouter?.({\n getDefault: () =>\n this.buildDefaultRouter(\n objectsProvider,\n clusterSupplier,\n catalog,\n proxy,\n permissions,\n httpAuth,\n authStrategyMap,\n ),\n objectsProvider,\n clusterSupplier,\n authStrategyMap,\n }) ??\n this.buildDefaultRouter(\n objectsProvider,\n clusterSupplier,\n catalog,\n proxy,\n permissions,\n httpAuth,\n authStrategyMap,\n )\n );\n }\n\n private buildProxy(\n logger: LoggerService,\n clusterSupplier: KubernetesClustersSupplier,\n discovery: DiscoveryService,\n httpAuth: HttpAuthService,\n authStrategyMap: { [key: string]: AuthenticationStrategy },\n ): KubernetesProxy {\n const authStrategy = new DispatchStrategy({\n authStrategyMap,\n });\n const middlewareCacheConfig = this.env.config.getOptionalConfig(\n 'kubernetes.proxy.middlewareCache',\n );\n const { ttlMs, maxSize } = resolveProxyMiddlewareCacheOptions({\n ttlMs: middlewareCacheConfig?.getOptionalNumber('ttl.milliseconds'),\n maxSize: middlewareCacheConfig?.getOptionalNumber('maxSize'),\n });\n\n return new KubernetesProxy({\n logger,\n clusterSupplier,\n authStrategy,\n discovery,\n httpAuth,\n auditor: this.env.auditor,\n middlewareCache: { ttlMs, maxSize },\n });\n }\n\n private buildDefaultRouter(\n objectsProvider: KubernetesObjectsProvider,\n clusterSupplier: KubernetesClustersSupplier,\n catalog: CatalogService,\n proxy: KubernetesProxy,\n permissionApi: PermissionEvaluator,\n httpAuth: HttpAuthService,\n authStrategyMap: { [key: string]: AuthenticationStrategy },\n ): express.Router {\n const logger = this.env.logger;\n const auditor = this.env.auditor;\n const router = Router();\n router.use('/proxy', proxy.createRequestHandler({ permissionApi }));\n router.use(express.json());\n router.use(\n createPermissionIntegrationRouter({\n permissions: kubernetesPermissions,\n }),\n );\n\n // @deprecated\n router.post('/services/:serviceId', async (req, res) => {\n const serviceId = req.params.serviceId;\n const requestBody: ObjectsByEntityRequest = req.body;\n let entityRef: string | undefined;\n if (requestBody?.entity) {\n try {\n entityRef = stringifyEntityRef(requestBody.entity);\n } catch {\n entityRef = undefined;\n }\n }\n\n const auditorEvent = await auditor.createEvent({\n eventId: 'resource-fetch',\n request: req,\n meta: { queryType: 'services', entityRef, serviceId },\n });\n\n try {\n await requirePermission(\n permissionApi,\n kubernetesResourcesReadPermission,\n httpAuth,\n req,\n );\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity: requestBody?.entity,\n auth: requestBody?.auth || {},\n },\n { credentials: await httpAuth.credentials(req) },\n );\n res.json(response);\n auditorEvent\n .success()\n .catch(error =>\n logger.error(\n 'Failed to emit audit event resource-fetch (services)',\n error,\n ),\n );\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n await auditorEvent.fail({ error: err });\n if (e instanceof NotAllowedError) {\n throw e;\n }\n logger.error(\n `action=retrieveObjectsByServiceId service=${serviceId}, error: ${err}`,\n );\n if (!res.headersSent) {\n res.status(500).json({ error: err.message });\n }\n }\n });\n\n router.get('/clusters', async (req, res) => {\n const auditorEvent = await auditor.createEvent({\n eventId: 'cluster-fetch',\n request: req,\n meta: { queryType: 'list' },\n });\n\n try {\n await requirePermission(\n permissionApi,\n kubernetesClustersReadPermission,\n httpAuth,\n req,\n );\n const credentials = await httpAuth.credentials(req);\n const clusterDetails = await this.fetchClusterDetails(clusterSupplier, {\n credentials,\n });\n res.json({\n items: clusterDetails.map(cd => {\n const oidcTokenProvider =\n cd.authMetadata[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];\n const authProvider =\n cd.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n const strategy = authStrategyMap[authProvider];\n let auth: AuthMetadata = {};\n if (strategy) {\n auth = strategy.presentAuthMetadata(cd.authMetadata);\n }\n\n return {\n name: cd.name,\n title: cd.title,\n dashboardUrl: cd.dashboardUrl,\n authProvider,\n ...(oidcTokenProvider && { oidcTokenProvider }),\n ...(auth && Object.keys(auth).length !== 0 && { auth }),\n };\n }),\n });\n auditorEvent\n .success()\n .catch(error =>\n logger.error(\n 'Failed to emit audit event cluster-fetch (list)',\n error,\n ),\n );\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n await auditorEvent.fail({ error: err });\n throw e;\n }\n });\n\n addResourceRoutesToRouter(\n router,\n catalog,\n objectsProvider,\n httpAuth,\n permissionApi,\n auditor,\n logger,\n );\n\n return router;\n }\n\n private async fetchClusterDetails(\n clusterSupplier: KubernetesClustersSupplier,\n options: { credentials: BackstageCredentials },\n ) {\n const clusterDetails = await clusterSupplier.getClusters(options);\n\n this.env.logger.debug(\n `action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,\n );\n\n return clusterDetails;\n }\n\n private async warnForClustersWithSkipTLSVerify(\n logger: LoggerService,\n clusterSupplier: KubernetesClustersSupplier,\n ): Promise<void> {\n try {\n const credentials = await this.env.auth.getOwnServiceCredentials();\n const clusters = await clusterSupplier.getClusters({ credentials });\n\n for (const cluster of clusters) {\n if (cluster.skipTLSVerify) {\n logger.warn(\n `Cluster '${cluster.name}' is configured with skipTLSVerify: true; TLS certificate verification is disabled for Kubernetes API traffic to this cluster`,\n );\n }\n }\n } catch (error) {\n logger.warn(\n `Failed to log skipTLSVerify warnings at startup: ${\n toError(error).message\n }`,\n );\n }\n }\n}\n"],"names":["Router","DispatchStrategy","resolveProxyMiddlewareCacheOptions","KubernetesProxy","express","createPermissionIntegrationRouter","kubernetesPermissions","stringifyEntityRef","requirePermission","kubernetesResourcesReadPermission","NotAllowedError","kubernetesClustersReadPermission","ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER","ANNOTATION_KUBERNETES_AUTH_PROVIDER","addResourceRoutesToRouter","toError"],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ const PROXY_WEBSOCKET_AUDIT = /* @__PURE__ */ Symbol("kubernetesProxyWebSocketAudit");
4
+ class ProxyAuditSession {
5
+ constructor(event, initialClusterName, logger) {
6
+ this.event = event;
7
+ this.initialClusterName = initialClusterName;
8
+ this.logger = logger;
9
+ }
10
+ resolvedClusterName;
11
+ finalized = false;
12
+ static async start(auditor, options) {
13
+ if (!auditor) {
14
+ return new ProxyAuditSession(
15
+ void 0,
16
+ options.clusterName,
17
+ options.logger
18
+ );
19
+ }
20
+ const sanitizedUrl = (options.req.originalUrl ?? options.req.url).split(
21
+ "?"
22
+ )[0];
23
+ const sanitizedRequest = Object.create(options.req, {
24
+ originalUrl: { value: sanitizedUrl, enumerable: true },
25
+ url: { value: sanitizedUrl, enumerable: true }
26
+ });
27
+ const event = await auditor.createEvent({
28
+ eventId: "cluster-fetch",
29
+ severityLevel: "medium",
30
+ request: sanitizedRequest,
31
+ meta: {
32
+ queryType: "proxy",
33
+ clusterName: options.clusterName,
34
+ method: options.method,
35
+ path: options.path
36
+ }
37
+ });
38
+ return new ProxyAuditSession(event, options.clusterName, options.logger);
39
+ }
40
+ setResolvedClusterName(name) {
41
+ this.resolvedClusterName = name;
42
+ }
43
+ finalize(error) {
44
+ if (this.finalized) {
45
+ return;
46
+ }
47
+ this.finalized = true;
48
+ if (!this.event) {
49
+ return;
50
+ }
51
+ const terminalMeta = this.resolvedClusterName && this.resolvedClusterName !== this.initialClusterName ? { clusterName: this.resolvedClusterName } : void 0;
52
+ const audit = error ? this.event.fail({
53
+ error,
54
+ ...terminalMeta && { meta: terminalMeta }
55
+ }) : this.event.success(terminalMeta ? { meta: terminalMeta } : void 0);
56
+ audit.catch(
57
+ (err) => this.logger.error("Failed to emit proxy audit event", err)
58
+ );
59
+ }
60
+ attachToHttpResponse(res) {
61
+ const finalizeFromResponse = () => {
62
+ if (!res.writableFinished) {
63
+ this.finalize(
64
+ new Error("Client disconnected before response completed")
65
+ );
66
+ } else if (res.statusCode >= 400) {
67
+ this.finalize(
68
+ new Error(`Proxy responded with status ${res.statusCode}`)
69
+ );
70
+ } else {
71
+ this.finalize();
72
+ }
73
+ };
74
+ const finalizeOnce = () => {
75
+ res.removeListener("finish", finalizeOnce);
76
+ res.removeListener("close", finalizeOnce);
77
+ finalizeFromResponse();
78
+ };
79
+ res.once("finish", finalizeOnce);
80
+ res.once("close", finalizeOnce);
81
+ }
82
+ prepareForWebSocketUpgrade(req, onSocketClose) {
83
+ req[PROXY_WEBSOCKET_AUDIT] = { session: this, onSocketClose };
84
+ }
85
+ static handleWebSocketProxyReq(proxyReq, req, socket) {
86
+ const typedReq = req;
87
+ const context = typedReq[PROXY_WEBSOCKET_AUDIT];
88
+ if (!context) {
89
+ return;
90
+ }
91
+ delete typedReq[PROXY_WEBSOCKET_AUDIT];
92
+ const { session, onSocketClose } = context;
93
+ const cleanups = [];
94
+ const finalize = (error) => {
95
+ session.finalize(error);
96
+ cleanups.forEach((fn) => fn());
97
+ req.socket.removeListener("close", onSocketClose);
98
+ };
99
+ const onUpgrade = () => finalize();
100
+ const onResponse = (proxyRes) => {
101
+ finalize(
102
+ new Error(
103
+ `Upstream rejected WebSocket upgrade with status ${proxyRes.statusCode}`
104
+ )
105
+ );
106
+ };
107
+ const onProxyError = (err) => finalize(err);
108
+ const onSocketError = (err) => finalize(err instanceof Error ? err : new Error(String(err)));
109
+ const onSocketClosed = () => finalize(
110
+ new Error("Client closed connection during WebSocket handshake")
111
+ );
112
+ proxyReq.once("upgrade", onUpgrade);
113
+ proxyReq.once("response", onResponse);
114
+ proxyReq.once("error", onProxyError);
115
+ socket.once("error", onSocketError);
116
+ socket.once("close", onSocketClosed);
117
+ cleanups.push(
118
+ () => proxyReq.removeListener("upgrade", onUpgrade),
119
+ () => proxyReq.removeListener("response", onResponse),
120
+ () => proxyReq.removeListener("error", onProxyError),
121
+ () => socket.removeListener("error", onSocketError),
122
+ () => socket.removeListener("close", onSocketClosed)
123
+ );
124
+ }
125
+ }
126
+
127
+ exports.ProxyAuditSession = ProxyAuditSession;
128
+ //# sourceMappingURL=ProxyAuditSession.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProxyAuditSession.cjs.js","sources":["../../src/service/ProxyAuditSession.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n AuditorService,\n AuditorServiceEvent,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { Request, Response } from 'express';\n\nconst PROXY_WEBSOCKET_AUDIT = Symbol('kubernetesProxyWebSocketAudit');\n\ntype WebSocketAuditContext = {\n session: ProxyAuditSession;\n onSocketClose: () => void;\n};\n\n/**\n * Tracks a single proxy request through audit event creation and finalization.\n * When no auditor is configured every method is a no-op.\n */\nexport class ProxyAuditSession {\n private resolvedClusterName?: string;\n private finalized = false;\n\n private constructor(\n private readonly event: AuditorServiceEvent | undefined,\n private readonly initialClusterName: string,\n private readonly logger: LoggerService,\n ) {}\n\n static async start(\n auditor: AuditorService | undefined,\n options: {\n req: Request;\n clusterName: string;\n method: string | undefined;\n path: string;\n logger: LoggerService;\n },\n ): Promise<ProxyAuditSession> {\n if (!auditor) {\n return new ProxyAuditSession(\n undefined,\n options.clusterName,\n options.logger,\n );\n }\n\n const sanitizedUrl = (options.req.originalUrl ?? options.req.url).split(\n '?',\n )[0];\n const sanitizedRequest = Object.create(options.req, {\n originalUrl: { value: sanitizedUrl, enumerable: true },\n url: { value: sanitizedUrl, enumerable: true },\n });\n\n const event = await auditor.createEvent({\n eventId: 'cluster-fetch',\n severityLevel: 'medium',\n request: sanitizedRequest,\n meta: {\n queryType: 'proxy',\n clusterName: options.clusterName,\n method: options.method,\n path: options.path,\n },\n });\n\n return new ProxyAuditSession(event, options.clusterName, options.logger);\n }\n\n setResolvedClusterName(name: string): void {\n this.resolvedClusterName = name;\n }\n\n finalize(error?: Error): void {\n if (this.finalized) {\n return;\n }\n this.finalized = true;\n\n if (!this.event) {\n return;\n }\n\n const terminalMeta =\n this.resolvedClusterName &&\n this.resolvedClusterName !== this.initialClusterName\n ? { clusterName: this.resolvedClusterName }\n : undefined;\n\n const audit = error\n ? this.event.fail({\n error,\n ...(terminalMeta && { meta: terminalMeta }),\n })\n : this.event.success(terminalMeta ? { meta: terminalMeta } : undefined);\n\n audit.catch(err =>\n this.logger.error('Failed to emit proxy audit event', err),\n );\n }\n\n attachToHttpResponse(res: Response): void {\n const finalizeFromResponse = () => {\n if (!res.writableFinished) {\n this.finalize(\n new Error('Client disconnected before response completed'),\n );\n } else if (res.statusCode >= 400) {\n this.finalize(\n new Error(`Proxy responded with status ${res.statusCode}`),\n );\n } else {\n this.finalize();\n }\n };\n\n const finalizeOnce = () => {\n res.removeListener('finish', finalizeOnce);\n res.removeListener('close', finalizeOnce);\n finalizeFromResponse();\n };\n\n res.once('finish', finalizeOnce);\n res.once('close', finalizeOnce);\n }\n\n prepareForWebSocketUpgrade(req: Request, onSocketClose: () => void): void {\n (req as Request & { [PROXY_WEBSOCKET_AUDIT]?: WebSocketAuditContext })[\n PROXY_WEBSOCKET_AUDIT\n ] = { session: this, onSocketClose };\n }\n\n static handleWebSocketProxyReq(\n proxyReq: import('http').ClientRequest,\n req: import('http').IncomingMessage,\n socket: import('net').Socket,\n ): void {\n const typedReq = req as Request & {\n [PROXY_WEBSOCKET_AUDIT]?: WebSocketAuditContext;\n };\n const context = typedReq[PROXY_WEBSOCKET_AUDIT];\n if (!context) {\n return;\n }\n delete typedReq[PROXY_WEBSOCKET_AUDIT];\n\n const { session, onSocketClose } = context;\n const cleanups: (() => void)[] = [];\n const finalize = (error?: Error) => {\n session.finalize(error);\n cleanups.forEach(fn => fn());\n req.socket.removeListener('close', onSocketClose);\n };\n\n const onUpgrade = () => finalize();\n const onResponse = (proxyRes: import('http').IncomingMessage) => {\n finalize(\n new Error(\n `Upstream rejected WebSocket upgrade with status ${proxyRes.statusCode}`,\n ),\n );\n };\n const onProxyError = (err: Error) => finalize(err);\n const onSocketError = (err: Error | unknown) =>\n finalize(err instanceof Error ? err : new Error(String(err)));\n const onSocketClosed = () =>\n finalize(\n new Error('Client closed connection during WebSocket handshake'),\n );\n\n proxyReq.once('upgrade', onUpgrade);\n proxyReq.once('response', onResponse);\n proxyReq.once('error', onProxyError);\n socket.once('error', onSocketError);\n socket.once('close', onSocketClosed);\n\n cleanups.push(\n () => proxyReq.removeListener('upgrade', onUpgrade),\n () => proxyReq.removeListener('response', onResponse),\n () => proxyReq.removeListener('error', onProxyError),\n () => socket.removeListener('error', onSocketError),\n () => socket.removeListener('close', onSocketClosed),\n );\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_PROXY_MIDDLEWARE_CACHE_TTL_MS = 6e4;
4
+ const DEFAULT_PROXY_MIDDLEWARE_CACHE_MAX_SIZE = 100;
5
+ function resolveProxyMiddlewareCacheOptions(options) {
6
+ const ttlMs = options?.ttlMs !== void 0 && options.ttlMs > 0 ? options.ttlMs : DEFAULT_PROXY_MIDDLEWARE_CACHE_TTL_MS;
7
+ const maxSize = options?.maxSize !== void 0 && options.maxSize >= 1 ? options.maxSize : DEFAULT_PROXY_MIDDLEWARE_CACHE_MAX_SIZE;
8
+ return { ttlMs, maxSize };
9
+ }
10
+ function buildProxyMiddlewareFingerprint(cluster) {
11
+ return `${cluster.url}|${cluster.skipTLSVerify ?? false}|${cluster.caData ?? ""}|${cluster.caFile ?? ""}`;
12
+ }
13
+ class ProxyMiddlewareCache {
14
+ #ttlMs;
15
+ #maxSize;
16
+ #entries = /* @__PURE__ */ new Map();
17
+ #now;
18
+ constructor(options) {
19
+ const resolved = resolveProxyMiddlewareCacheOptions(options);
20
+ this.#ttlMs = resolved.ttlMs;
21
+ this.#maxSize = resolved.maxSize;
22
+ this.#now = options.now ?? (() => Date.now());
23
+ }
24
+ get(cluster) {
25
+ const entry = this.#entries.get(cluster.name);
26
+ if (!entry) {
27
+ return void 0;
28
+ }
29
+ const fingerprint = buildProxyMiddlewareFingerprint(cluster);
30
+ if (entry.fingerprint !== fingerprint || this.#now() - entry.createdAtMs > this.#ttlMs) {
31
+ this.#entries.delete(cluster.name);
32
+ return void 0;
33
+ }
34
+ return entry.middleware;
35
+ }
36
+ set(cluster, middleware) {
37
+ this.#entries.delete(cluster.name);
38
+ while (this.#entries.size >= this.#maxSize) {
39
+ const oldestKey = this.#findOldestKey();
40
+ if (!oldestKey) {
41
+ break;
42
+ }
43
+ this.#entries.delete(oldestKey);
44
+ }
45
+ this.#entries.set(cluster.name, {
46
+ middleware,
47
+ fingerprint: buildProxyMiddlewareFingerprint(cluster),
48
+ createdAtMs: this.#now()
49
+ });
50
+ }
51
+ #findOldestKey() {
52
+ let oldestKey;
53
+ let oldestTime = Number.POSITIVE_INFINITY;
54
+ for (const [key, entry] of this.#entries) {
55
+ if (entry.createdAtMs < oldestTime) {
56
+ oldestTime = entry.createdAtMs;
57
+ oldestKey = key;
58
+ }
59
+ }
60
+ return oldestKey;
61
+ }
62
+ }
63
+
64
+ exports.DEFAULT_PROXY_MIDDLEWARE_CACHE_MAX_SIZE = DEFAULT_PROXY_MIDDLEWARE_CACHE_MAX_SIZE;
65
+ exports.DEFAULT_PROXY_MIDDLEWARE_CACHE_TTL_MS = DEFAULT_PROXY_MIDDLEWARE_CACHE_TTL_MS;
66
+ exports.ProxyMiddlewareCache = ProxyMiddlewareCache;
67
+ exports.buildProxyMiddlewareFingerprint = buildProxyMiddlewareFingerprint;
68
+ exports.resolveProxyMiddlewareCacheOptions = resolveProxyMiddlewareCacheOptions;
69
+ //# sourceMappingURL=ProxyMiddlewareCache.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProxyMiddlewareCache.cjs.js","sources":["../../src/service/ProxyMiddlewareCache.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { RequestHandler } from 'http-proxy-middleware';\nimport type { ClusterDetails } from '@backstage/plugin-kubernetes-node';\n\nexport const DEFAULT_PROXY_MIDDLEWARE_CACHE_TTL_MS = 60_000;\nexport const DEFAULT_PROXY_MIDDLEWARE_CACHE_MAX_SIZE = 100;\n\n/**\n * @internal\n */\nexport function resolveProxyMiddlewareCacheOptions(options?: {\n ttlMs?: number;\n maxSize?: number;\n}): { ttlMs: number; maxSize: number } {\n const ttlMs =\n options?.ttlMs !== undefined && options.ttlMs > 0\n ? options.ttlMs\n : DEFAULT_PROXY_MIDDLEWARE_CACHE_TTL_MS;\n const maxSize =\n options?.maxSize !== undefined && options.maxSize >= 1\n ? options.maxSize\n : DEFAULT_PROXY_MIDDLEWARE_CACHE_MAX_SIZE;\n\n return { ttlMs, maxSize };\n}\n\ntype CacheEntry = {\n middleware: RequestHandler;\n fingerprint: string;\n createdAtMs: number;\n};\n\n/**\n * Builds a cache fingerprint from cluster details that affect cached proxy middleware.\n *\n * @internal\n */\nexport function buildProxyMiddlewareFingerprint(\n cluster: ClusterDetails,\n): string {\n return `${cluster.url}|${cluster.skipTLSVerify ?? false}|${\n cluster.caData ?? ''\n }|${cluster.caFile ?? ''}`;\n}\n\n/**\n * Bounded TTL cache for per-cluster http-proxy-middleware instances.\n *\n * @internal\n */\nexport class ProxyMiddlewareCache {\n readonly #ttlMs: number;\n readonly #maxSize: number;\n readonly #entries = new Map<string, CacheEntry>();\n readonly #now: () => number;\n\n constructor(options: {\n ttlMs?: number;\n maxSize?: number;\n now?: () => number;\n }) {\n const resolved = resolveProxyMiddlewareCacheOptions(options);\n this.#ttlMs = resolved.ttlMs;\n this.#maxSize = resolved.maxSize;\n this.#now = options.now ?? (() => Date.now());\n }\n\n get(cluster: ClusterDetails): RequestHandler | undefined {\n const entry = this.#entries.get(cluster.name);\n if (!entry) {\n return undefined;\n }\n\n const fingerprint = buildProxyMiddlewareFingerprint(cluster);\n if (\n entry.fingerprint !== fingerprint ||\n this.#now() - entry.createdAtMs > this.#ttlMs\n ) {\n this.#entries.delete(cluster.name);\n return undefined;\n }\n\n return entry.middleware;\n }\n\n set(cluster: ClusterDetails, middleware: RequestHandler): void {\n this.#entries.delete(cluster.name);\n\n while (this.#entries.size >= this.#maxSize) {\n const oldestKey = this.#findOldestKey();\n if (!oldestKey) {\n break;\n }\n this.#entries.delete(oldestKey);\n }\n\n this.#entries.set(cluster.name, {\n middleware,\n fingerprint: buildProxyMiddlewareFingerprint(cluster),\n createdAtMs: this.#now(),\n });\n }\n\n #findOldestKey(): string | undefined {\n let oldestKey: string | undefined;\n let oldestTime = Number.POSITIVE_INFINITY;\n\n for (const [key, entry] of this.#entries) {\n if (entry.createdAtMs < oldestTime) {\n oldestTime = entry.createdAtMs;\n oldestKey = key;\n }\n }\n\n return oldestKey;\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-kubernetes-backend",
3
- "version": "0.21.7-next.0",
3
+ "version": "0.21.7",
4
4
  "description": "A Backstage backend plugin that integrates towards Kubernetes",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",
@@ -49,18 +49,18 @@
49
49
  "@aws-crypto/sha256-js": "^5.0.0",
50
50
  "@aws-sdk/credential-providers": "^3.350.0",
51
51
  "@azure/identity": "^4.0.0",
52
- "@backstage/backend-plugin-api": "1.10.0-next.0",
53
- "@backstage/catalog-client": "1.16.1",
54
- "@backstage/catalog-model": "1.9.0",
55
- "@backstage/config": "1.3.8",
56
- "@backstage/errors": "1.3.1",
57
- "@backstage/integration-aws-node": "0.2.0",
58
- "@backstage/plugin-catalog-node": "2.2.4-next.0",
59
- "@backstage/plugin-kubernetes-common": "0.9.12",
60
- "@backstage/plugin-kubernetes-node": "0.4.7-next.0",
61
- "@backstage/plugin-permission-common": "0.9.9",
62
- "@backstage/plugin-permission-node": "0.11.3-next.0",
63
- "@backstage/types": "1.2.2",
52
+ "@backstage/backend-plugin-api": "^1.10.0",
53
+ "@backstage/catalog-client": "^1.16.1",
54
+ "@backstage/catalog-model": "^1.10.0",
55
+ "@backstage/config": "^1.3.8",
56
+ "@backstage/errors": "^1.3.1",
57
+ "@backstage/integration-aws-node": "^0.2.1",
58
+ "@backstage/plugin-catalog-node": "^2.2.4",
59
+ "@backstage/plugin-kubernetes-common": "^0.9.12",
60
+ "@backstage/plugin-kubernetes-node": "^0.4.7",
61
+ "@backstage/plugin-permission-common": "^0.9.10",
62
+ "@backstage/plugin-permission-node": "^0.11.3",
63
+ "@backstage/types": "^1.2.2",
64
64
  "@google-cloud/container": "^5.0.0",
65
65
  "@jest-mock/express": "^2.0.1",
66
66
  "@kubernetes/client-node": "1.4.0",
@@ -75,11 +75,11 @@
75
75
  "node-fetch": "^2.7.0"
76
76
  },
77
77
  "devDependencies": {
78
- "@backstage/backend-defaults": "0.17.6-next.0",
79
- "@backstage/backend-test-utils": "1.11.6-next.0",
80
- "@backstage/cli": "0.36.4",
81
- "@backstage/plugin-permission-backend": "0.7.15-next.0",
82
- "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.22-next.0",
78
+ "@backstage/backend-defaults": "^0.17.7",
79
+ "@backstage/backend-test-utils": "^1.11.6",
80
+ "@backstage/cli": "^0.36.5",
81
+ "@backstage/plugin-permission-backend": "^0.7.15",
82
+ "@backstage/plugin-permission-backend-module-allow-all-policy": "^0.2.22",
83
83
  "@types/express": "^4.17.6",
84
84
  "msw": "^1.0.0",
85
85
  "supertest": "^7.0.0",