@backstage/plugin-kubernetes-backend 0.21.11-next.1 → 0.21.11
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.
- package/CHANGELOG.md +41 -0
- package/config.schema.json +13 -0
- package/dist/actions/createGetKubernetesClustersAction.cjs.js +88 -0
- package/dist/actions/createGetKubernetesClustersAction.cjs.js.map +1 -0
- package/dist/actions/createGetKubernetesResourcesForEntityAction.cjs.js +137 -0
- package/dist/actions/createGetKubernetesResourcesForEntityAction.cjs.js.map +1 -0
- package/dist/actions/index.cjs.js +12 -0
- package/dist/actions/index.cjs.js.map +1 -0
- package/dist/cluster-locator/CatalogClusterLocator.cjs.js +63 -24
- package/dist/cluster-locator/CatalogClusterLocator.cjs.js.map +1 -1
- package/dist/cluster-locator/CatalogClusterLocatorOptions.cjs.js +15 -0
- package/dist/cluster-locator/CatalogClusterLocatorOptions.cjs.js.map +1 -0
- package/dist/cluster-locator/catalogClusterAuthMetadata.cjs.js +19 -0
- package/dist/cluster-locator/catalogClusterAuthMetadata.cjs.js.map +1 -0
- package/dist/cluster-locator/index.cjs.js +6 -1
- package/dist/cluster-locator/index.cjs.js.map +1 -1
- package/dist/cluster-locator/validateClusterApiServerUrl.cjs.js +89 -0
- package/dist/cluster-locator/validateClusterApiServerUrl.cjs.js.map +1 -0
- package/dist/package.json.cjs.js +1 -1
- package/dist/plugin.cjs.js +16 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/KubernetesConnection.cjs.js +8 -3
- package/dist/service/KubernetesConnection.cjs.js.map +1 -1
- package/dist/service/KubernetesProxy.cjs.js +1 -1
- package/dist/service/KubernetesProxy.cjs.js.map +1 -1
- package/dist/service/KubernetesRouter.cjs.js +0 -6
- package/dist/service/KubernetesRouter.cjs.js.map +1 -1
- package/package.json +21 -20
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var promises = require('node:dns/promises');
|
|
4
|
+
var ipaddr = require('ipaddr.js');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var ipaddr__default = /*#__PURE__*/_interopDefaultCompat(ipaddr);
|
|
9
|
+
|
|
10
|
+
function isNonPublicIp(ip) {
|
|
11
|
+
try {
|
|
12
|
+
const addr = ipaddr__default.default.parse(ip);
|
|
13
|
+
return addr.range() !== "unicast";
|
|
14
|
+
} catch {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function hostnameMatchesPattern(hostname, pattern) {
|
|
19
|
+
const normalizedHost = hostname.toLowerCase();
|
|
20
|
+
const normalizedPattern = pattern.toLowerCase();
|
|
21
|
+
if (normalizedPattern.startsWith("*.")) {
|
|
22
|
+
const suffix = normalizedPattern.slice(1);
|
|
23
|
+
const bare = normalizedPattern.slice(2);
|
|
24
|
+
return normalizedHost.endsWith(suffix) || normalizedHost === bare;
|
|
25
|
+
}
|
|
26
|
+
return normalizedHost === normalizedPattern;
|
|
27
|
+
}
|
|
28
|
+
function isHostnameDangerouslyAllowed(hostname, patterns) {
|
|
29
|
+
if (!patterns?.length) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return patterns.some((pattern) => hostnameMatchesPattern(hostname, pattern));
|
|
33
|
+
}
|
|
34
|
+
async function validateHostNotPrivate(hostname) {
|
|
35
|
+
const addresses = await promises.lookup(hostname, { all: true });
|
|
36
|
+
const nonPublic = addresses.find((addr) => isNonPublicIp(addr.address));
|
|
37
|
+
if (nonPublic) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Kubernetes cluster API server URL hostname "${hostname}" resolves to a non-public address`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function validateLiteralHost(hostname) {
|
|
44
|
+
if (ipaddr__default.default.isValid(hostname) || ipaddr__default.default.IPv6.isValid(hostname)) {
|
|
45
|
+
if (isNonPublicIp(hostname)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Kubernetes cluster API server URL must not use a non-public IP address"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function validateClusterApiServerUrl(apiServerUrl, options = {}) {
|
|
53
|
+
let url;
|
|
54
|
+
try {
|
|
55
|
+
url = new URL(apiServerUrl);
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error("Kubernetes cluster API server URL is not a valid URL");
|
|
58
|
+
}
|
|
59
|
+
if (url.username || url.password) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Kubernetes cluster API server URL must not contain credentials"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const hostname = url.hostname.toLowerCase();
|
|
65
|
+
const ssrfExempt = isHostnameDangerouslyAllowed(
|
|
66
|
+
hostname,
|
|
67
|
+
options.dangerouslyAllowClusterUrls
|
|
68
|
+
);
|
|
69
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Kubernetes cluster API server URL must use the HTTP or HTTPS scheme"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (!ssrfExempt) {
|
|
75
|
+
if (url.protocol !== "https:") {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"Kubernetes cluster API server URL must use the HTTPS scheme"
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
validateLiteralHost(hostname);
|
|
81
|
+
if (!ipaddr__default.default.isValid(hostname) && !ipaddr__default.default.IPv6.isValid(hostname)) {
|
|
82
|
+
await validateHostNotPrivate(hostname);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return url;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
exports.validateClusterApiServerUrl = validateClusterApiServerUrl;
|
|
89
|
+
//# sourceMappingURL=validateClusterApiServerUrl.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateClusterApiServerUrl.cjs.js","sources":["../../src/cluster-locator/validateClusterApiServerUrl.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 { lookup } from 'node:dns/promises';\nimport ipaddr from 'ipaddr.js';\n\nexport type ValidateClusterApiServerUrlOptions = {\n /**\n * Hostname patterns (for example `127.0.0.1` or `*.example.com`) for which\n * catalog cluster API server URLs may use HTTP or non-public addresses.\n * Configured in app-config only; not controllable from catalog entities.\n */\n dangerouslyAllowClusterUrls?: string[];\n};\n\nfunction isNonPublicIp(ip: string): boolean {\n try {\n const addr = ipaddr.parse(ip);\n return addr.range() !== 'unicast';\n } catch {\n return true;\n }\n}\n\nfunction hostnameMatchesPattern(hostname: string, pattern: string): boolean {\n const normalizedHost = hostname.toLowerCase();\n const normalizedPattern = pattern.toLowerCase();\n\n if (normalizedPattern.startsWith('*.')) {\n const suffix = normalizedPattern.slice(1);\n const bare = normalizedPattern.slice(2);\n return normalizedHost.endsWith(suffix) || normalizedHost === bare;\n }\n\n return normalizedHost === normalizedPattern;\n}\n\nfunction isHostnameDangerouslyAllowed(\n hostname: string,\n patterns: string[] | undefined,\n): boolean {\n if (!patterns?.length) {\n return false;\n }\n return patterns.some(pattern => hostnameMatchesPattern(hostname, pattern));\n}\n\nasync function validateHostNotPrivate(hostname: string): Promise<void> {\n const addresses = await lookup(hostname, { all: true });\n const nonPublic = addresses.find(addr => isNonPublicIp(addr.address));\n if (nonPublic) {\n throw new Error(\n `Kubernetes cluster API server URL hostname \"${hostname}\" resolves to a non-public address`,\n );\n }\n}\n\nfunction validateLiteralHost(hostname: string): void {\n if (ipaddr.isValid(hostname) || ipaddr.IPv6.isValid(hostname)) {\n if (isNonPublicIp(hostname)) {\n throw new Error(\n 'Kubernetes cluster API server URL must not use a non-public IP address',\n );\n }\n }\n}\n\n/**\n * Validates a catalog-provided Kubernetes API server URL against SSRF protections.\n *\n * @throws when the URL is not permitted\n */\nexport async function validateClusterApiServerUrl(\n apiServerUrl: string,\n options: ValidateClusterApiServerUrlOptions = {},\n): Promise<URL> {\n let url: URL;\n try {\n url = new URL(apiServerUrl);\n } catch {\n throw new Error('Kubernetes cluster API server URL is not a valid URL');\n }\n\n if (url.username || url.password) {\n throw new Error(\n 'Kubernetes cluster API server URL must not contain credentials',\n );\n }\n\n const hostname = url.hostname.toLowerCase();\n const ssrfExempt = isHostnameDangerouslyAllowed(\n hostname,\n options.dangerouslyAllowClusterUrls,\n );\n\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new Error(\n 'Kubernetes cluster API server URL must use the HTTP or HTTPS scheme',\n );\n }\n\n if (!ssrfExempt) {\n if (url.protocol !== 'https:') {\n throw new Error(\n 'Kubernetes cluster API server URL must use the HTTPS scheme',\n );\n }\n\n validateLiteralHost(hostname);\n\n if (!ipaddr.isValid(hostname) && !ipaddr.IPv6.isValid(hostname)) {\n await validateHostNotPrivate(hostname);\n }\n }\n\n return url;\n}\n"],"names":["ipaddr","lookup"],"mappings":"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"}
|
package/dist/package.json.cjs.js
CHANGED
package/dist/plugin.cjs.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var alpha = require('@backstage/backend-plugin-api/alpha');
|
|
5
|
+
var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
|
|
4
6
|
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
5
7
|
var pluginKubernetesNode = require('@backstage/plugin-kubernetes-node');
|
|
6
8
|
var KubernetesRouter = require('./service/KubernetesRouter.cjs.js');
|
|
7
9
|
var KubernetesInitializer = require('./service/KubernetesInitializer.cjs.js');
|
|
10
|
+
var index = require('./actions/index.cjs.js');
|
|
8
11
|
|
|
9
12
|
class ObjectsProvider {
|
|
10
13
|
objectsProvider;
|
|
@@ -145,9 +148,11 @@ const kubernetesPlugin = backendPluginApi.createBackendPlugin({
|
|
|
145
148
|
discovery: backendPluginApi.coreServices.discovery,
|
|
146
149
|
catalog: pluginCatalogNode.catalogServiceRef,
|
|
147
150
|
permissions: backendPluginApi.coreServices.permissions,
|
|
151
|
+
permissionsRegistry: backendPluginApi.coreServices.permissionsRegistry,
|
|
148
152
|
auth: backendPluginApi.coreServices.auth,
|
|
149
153
|
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
150
|
-
auditor: backendPluginApi.coreServices.auditor
|
|
154
|
+
auditor: backendPluginApi.coreServices.auditor,
|
|
155
|
+
actionsRegistry: alpha.actionsRegistryServiceRef
|
|
151
156
|
},
|
|
152
157
|
async init({
|
|
153
158
|
http,
|
|
@@ -156,9 +161,11 @@ const kubernetesPlugin = backendPluginApi.createBackendPlugin({
|
|
|
156
161
|
discovery,
|
|
157
162
|
catalog,
|
|
158
163
|
permissions,
|
|
164
|
+
permissionsRegistry,
|
|
159
165
|
auth,
|
|
160
166
|
httpAuth,
|
|
161
|
-
auditor
|
|
167
|
+
auditor,
|
|
168
|
+
actionsRegistry
|
|
162
169
|
}) {
|
|
163
170
|
if (config.has("kubernetes")) {
|
|
164
171
|
const initializer = KubernetesInitializer.KubernetesInitializer.create({
|
|
@@ -196,6 +203,13 @@ const kubernetesPlugin = backendPluginApi.createBackendPlugin({
|
|
|
196
203
|
customRouter: extPointRouter.getRouter()
|
|
197
204
|
});
|
|
198
205
|
http.use(await router.getRouter());
|
|
206
|
+
permissionsRegistry.addPermissions(pluginKubernetesCommon.kubernetesPermissions);
|
|
207
|
+
index.createKubernetesActions({
|
|
208
|
+
actionsRegistry,
|
|
209
|
+
clusterSupplier,
|
|
210
|
+
objectsProvider,
|
|
211
|
+
catalog
|
|
212
|
+
});
|
|
199
213
|
} else {
|
|
200
214
|
logger.warn(
|
|
201
215
|
"Failed to initialize kubernetes backend: valid kubernetes config is missing"
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\n\nimport {\n type AuthenticationStrategy,\n kubernetesAuthStrategyExtensionPoint,\n type KubernetesAuthStrategyExtensionPoint,\n type KubernetesClustersSupplier,\n kubernetesClusterSupplierExtensionPoint,\n type KubernetesClusterSupplierExtensionPoint,\n KubernetesClusterSupplierFactory,\n type KubernetesFetcher,\n kubernetesFetcherExtensionPoint,\n type KubernetesFetcherExtensionPoint,\n KubernetesFetcherFactory,\n type KubernetesObjectsProvider,\n kubernetesObjectsProviderExtensionPoint,\n type KubernetesObjectsProviderExtensionPoint,\n KubernetesObjectsProviderFactory,\n KubernetesRouterExtensionPoint,\n kubernetesRouterExtensionPoint,\n KubernetesRouterFactory,\n type KubernetesServiceLocator,\n kubernetesServiceLocatorExtensionPoint,\n type KubernetesServiceLocatorExtensionPoint,\n KubernetesServiceLocatorFactory,\n} from '@backstage/plugin-kubernetes-node';\nimport { KubernetesRouter } from './service/KubernetesRouter';\nimport { KubernetesInitializer } from './service/KubernetesInitializer';\n\nclass ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {\n private objectsProvider: KubernetesObjectsProviderFactory | undefined;\n\n getObjectsProvider() {\n return this.objectsProvider;\n }\n\n addObjectsProvider(\n provider: KubernetesObjectsProvider | KubernetesObjectsProviderFactory,\n ) {\n if (this.objectsProvider) {\n throw new Error(\n 'Multiple Kubernetes objects provider is not supported at this time',\n );\n }\n if (typeof provider !== 'function') {\n this.objectsProvider = async () => provider;\n } else {\n this.objectsProvider = provider;\n }\n }\n}\n\nclass ClusterSuplier implements KubernetesClusterSupplierExtensionPoint {\n private clusterSupplier: KubernetesClusterSupplierFactory | undefined;\n\n getClusterSupplier() {\n return this.clusterSupplier;\n }\n\n addClusterSupplier(\n clusterSupplier:\n | KubernetesClustersSupplier\n | KubernetesClusterSupplierFactory,\n ) {\n if (this.clusterSupplier) {\n throw new Error(\n 'Multiple Kubernetes Cluster Suppliers is not supported at this time',\n );\n }\n if (typeof clusterSupplier !== 'function') {\n this.clusterSupplier = async () => clusterSupplier;\n } else {\n this.clusterSupplier = clusterSupplier;\n }\n }\n}\n\nclass Fetcher implements KubernetesFetcherExtensionPoint {\n private fetcher: KubernetesFetcherFactory | undefined;\n\n getFetcher() {\n return this.fetcher;\n }\n\n addFetcher(fetcher: KubernetesFetcher | KubernetesFetcherFactory) {\n if (this.fetcher) {\n throw new Error(\n 'Multiple Kubernetes Fetchers is not supported at this time',\n );\n }\n if (typeof fetcher !== 'function') {\n this.fetcher = async () => fetcher;\n } else {\n this.fetcher = fetcher;\n }\n }\n}\n\nclass ServiceLocator implements KubernetesServiceLocatorExtensionPoint {\n private serviceLocator: KubernetesServiceLocatorFactory | undefined;\n\n getServiceLocator() {\n return this.serviceLocator;\n }\n\n addServiceLocator(\n serviceLocator: KubernetesServiceLocator | KubernetesServiceLocatorFactory,\n ) {\n if (this.serviceLocator) {\n throw new Error(\n 'Multiple Kubernetes Service Locators is not supported at this time',\n );\n }\n\n if (typeof serviceLocator !== 'function') {\n this.serviceLocator = async () => serviceLocator;\n } else {\n this.serviceLocator = serviceLocator;\n }\n }\n}\n\nclass AuthStrategy implements KubernetesAuthStrategyExtensionPoint {\n private authStrategies: Map<string, AuthenticationStrategy> | undefined;\n\n getAuthenticationStrategies() {\n return this.authStrategies;\n }\n\n addAuthStrategy(key: string, authStrategy: AuthenticationStrategy) {\n if (!this.authStrategies) {\n this.authStrategies = new Map<string, AuthenticationStrategy>();\n }\n\n if (key.includes('-')) {\n throw new Error('Strategy name can not include dashes');\n }\n\n this.authStrategies.set(key, authStrategy);\n }\n}\n\nclass CustomRouter implements KubernetesRouterExtensionPoint {\n private router: KubernetesRouterFactory | undefined;\n\n getRouter() {\n return this.router;\n }\n\n addRouter(router: KubernetesRouterFactory) {\n if (this.router) {\n throw new Error(\n 'Multiple Kubernetes routers is not supported at this time',\n );\n }\n\n this.router = router;\n }\n}\n\n/**\n * This is the backend plugin that provides the Kubernetes integration.\n * @public\n */\nexport const kubernetesPlugin = createBackendPlugin({\n pluginId: 'kubernetes',\n register(env) {\n const extPointObjectsProvider = new ObjectsProvider();\n const extPointClusterSuplier = new ClusterSuplier();\n const extPointAuthStrategy = new AuthStrategy();\n const extPointFetcher = new Fetcher();\n const extPointServiceLocator = new ServiceLocator();\n const extPointRouter = new CustomRouter();\n\n env.registerExtensionPoint(\n kubernetesObjectsProviderExtensionPoint,\n extPointObjectsProvider,\n );\n env.registerExtensionPoint(\n kubernetesClusterSupplierExtensionPoint,\n extPointClusterSuplier,\n );\n env.registerExtensionPoint(\n kubernetesAuthStrategyExtensionPoint,\n extPointAuthStrategy,\n );\n env.registerExtensionPoint(\n kubernetesFetcherExtensionPoint,\n extPointFetcher,\n );\n env.registerExtensionPoint(\n kubernetesServiceLocatorExtensionPoint,\n extPointServiceLocator,\n );\n env.registerExtensionPoint(kubernetesRouterExtensionPoint, extPointRouter);\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n discovery: coreServices.discovery,\n catalog: catalogServiceRef,\n permissions: coreServices.permissions,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n auditor: coreServices.auditor,\n },\n async init({\n http,\n logger,\n config,\n discovery,\n catalog,\n permissions,\n auth,\n httpAuth,\n auditor,\n }) {\n // TODO: this could do with a cleanup and push some of this initialization somewhere else\n if (config.has('kubernetes')) {\n const initializer = KubernetesInitializer.create({\n logger,\n config,\n catalog,\n auth,\n fetcher: extPointFetcher.getFetcher(),\n clusterSupplier: extPointClusterSuplier.getClusterSupplier(),\n serviceLocator: extPointServiceLocator.getServiceLocator(),\n objectsProvider: extPointObjectsProvider.getObjectsProvider(),\n authStrategyMap: extPointAuthStrategy.getAuthenticationStrategies(),\n });\n\n const {\n fetcher,\n authStrategyMap,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n } = await initializer.init();\n\n const router = KubernetesRouter.create({\n logger,\n config,\n catalog,\n permissions,\n discovery,\n auth,\n httpAuth,\n auditor,\n authStrategyMap: Object.fromEntries(authStrategyMap.entries()),\n fetcher,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n customRouter: extPointRouter.getRouter(),\n });\n\n http.use(await router.getRouter());\n } else {\n logger.warn(\n 'Failed to initialize kubernetes backend: valid kubernetes config is missing',\n );\n }\n },\n });\n },\n});\n"],"names":["createBackendPlugin","kubernetesObjectsProviderExtensionPoint","kubernetesClusterSupplierExtensionPoint","kubernetesAuthStrategyExtensionPoint","kubernetesFetcherExtensionPoint","kubernetesServiceLocatorExtensionPoint","kubernetesRouterExtensionPoint","coreServices","catalogServiceRef","KubernetesInitializer","KubernetesRouter"],"mappings":";;;;;;;;AAiDA,MAAM,eAAA,CAAmE;AAAA,EAC/D,eAAA;AAAA,EAER,kBAAA,GAAqB;AACnB,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA,EAEA,mBACE,QAAA,EACA;AACA,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,MAAA,IAAA,CAAK,kBAAkB,YAAY,QAAA;AAAA,IACrC,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,eAAA,GAAkB,QAAA;AAAA,IACzB;AAAA,EACF;AACF;AAEA,MAAM,cAAA,CAAkE;AAAA,EAC9D,eAAA;AAAA,EAER,kBAAA,GAAqB;AACnB,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA,EAEA,mBACE,eAAA,EAGA;AACA,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,OAAO,oBAAoB,UAAA,EAAY;AACzC,MAAA,IAAA,CAAK,kBAAkB,YAAY,eAAA;AAAA,IACrC,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AAAA,IACzB;AAAA,EACF;AACF;AAEA,MAAM,OAAA,CAAmD;AAAA,EAC/C,OAAA;AAAA,EAER,UAAA,GAAa;AACX,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA,EAEA,WAAW,OAAA,EAAuD;AAChE,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AACjC,MAAA,IAAA,CAAK,UAAU,YAAY,OAAA;AAAA,IAC7B,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,IACjB;AAAA,EACF;AACF;AAEA,MAAM,cAAA,CAAiE;AAAA,EAC7D,cAAA;AAAA,EAER,iBAAA,GAAoB;AAClB,IAAA,OAAO,IAAA,CAAK,cAAA;AAAA,EACd;AAAA,EAEA,kBACE,cAAA,EACA;AACA,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,OAAO,mBAAmB,UAAA,EAAY;AACxC,MAAA,IAAA,CAAK,iBAAiB,YAAY,cAAA;AAAA,IACpC,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,cAAA,GAAiB,cAAA;AAAA,IACxB;AAAA,EACF;AACF;AAEA,MAAM,YAAA,CAA6D;AAAA,EACzD,cAAA;AAAA,EAER,2BAAA,GAA8B;AAC5B,IAAA,OAAO,IAAA,CAAK,cAAA;AAAA,EACd;AAAA,EAEA,eAAA,CAAgB,KAAa,YAAA,EAAsC;AACjE,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AACxB,MAAA,IAAA,CAAK,cAAA,uBAAqB,GAAA,EAAoC;AAAA,IAChE;AAEA,IAAA,IAAI,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,EAAG;AACrB,MAAA,MAAM,IAAI,MAAM,sCAAsC,CAAA;AAAA,IACxD;AAEA,IAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,GAAA,EAAK,YAAY,CAAA;AAAA,EAC3C;AACF;AAEA,MAAM,YAAA,CAAuD;AAAA,EACnD,MAAA;AAAA,EAER,SAAA,GAAY;AACV,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,UAAU,MAAA,EAAiC;AACzC,IAAA,IAAI,KAAK,MAAA,EAAQ;AACf,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AACF;AAMO,MAAM,mBAAmBA,oCAAA,CAAoB;AAAA,EAClD,QAAA,EAAU,YAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,uBAAA,GAA0B,IAAI,eAAA,EAAgB;AACpD,IAAA,MAAM,sBAAA,GAAyB,IAAI,cAAA,EAAe;AAClD,IAAA,MAAM,oBAAA,GAAuB,IAAI,YAAA,EAAa;AAC9C,IAAA,MAAM,eAAA,GAAkB,IAAI,OAAA,EAAQ;AACpC,IAAA,MAAM,sBAAA,GAAyB,IAAI,cAAA,EAAe;AAClD,IAAA,MAAM,cAAA,GAAiB,IAAI,YAAA,EAAa;AAExC,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,4DAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,4DAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,yDAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,oDAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,2DAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,GAAA,CAAI,sBAAA,CAAuBC,qDAAgC,cAAc,CAAA;AAEzE,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,MAAMC,6BAAA,CAAa,UAAA;AAAA,QACnB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,OAAA,EAASC,mCAAA;AAAA,QACT,aAAaD,6BAAA,CAAa,WAAA;AAAA,QAC1B,MAAMA,6BAAA,CAAa,IAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,SAASA,6BAAA,CAAa;AAAA,OACxB;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,IAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,OAAA;AAAA,QACA,WAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF,EAAG;AAED,QAAA,IAAI,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,EAAG;AAC5B,UAAA,MAAM,WAAA,GAAcE,4CAAsB,MAAA,CAAO;AAAA,YAC/C,MAAA;AAAA,YACA,MAAA;AAAA,YACA,OAAA;AAAA,YACA,IAAA;AAAA,YACA,OAAA,EAAS,gBAAgB,UAAA,EAAW;AAAA,YACpC,eAAA,EAAiB,uBAAuB,kBAAA,EAAmB;AAAA,YAC3D,cAAA,EAAgB,uBAAuB,iBAAA,EAAkB;AAAA,YACzD,eAAA,EAAiB,wBAAwB,kBAAA,EAAmB;AAAA,YAC5D,eAAA,EAAiB,qBAAqB,2BAAA;AAA4B,WACnE,CAAA;AAED,UAAA,MAAM;AAAA,YACJ,OAAA;AAAA,YACA,eAAA;AAAA,YACA,eAAA;AAAA,YACA,cAAA;AAAA,YACA;AAAA,WACF,GAAI,MAAM,WAAA,CAAY,IAAA,EAAK;AAE3B,UAAA,MAAM,MAAA,GAASC,kCAAiB,MAAA,CAAO;AAAA,YACrC,MAAA;AAAA,YACA,MAAA;AAAA,YACA,OAAA;AAAA,YACA,WAAA;AAAA,YACA,SAAA;AAAA,YACA,IAAA;AAAA,YACA,QAAA;AAAA,YACA,OAAA;AAAA,YACA,eAAA,EAAiB,MAAA,CAAO,WAAA,CAAY,eAAA,CAAgB,SAAS,CAAA;AAAA,YAC7D,OAAA;AAAA,YACA,eAAA;AAAA,YACA,cAAA;AAAA,YACA,eAAA;AAAA,YACA,YAAA,EAAc,eAAe,SAAA;AAAU,WACxC,CAAA;AAED,UAAA,IAAA,CAAK,GAAA,CAAI,MAAM,MAAA,CAAO,SAAA,EAAW,CAAA;AAAA,QACnC,CAAA,MAAO;AACL,UAAA,MAAA,CAAO,IAAA;AAAA,YACL;AAAA,WACF;AAAA,QACF;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha';\nimport { kubernetesPermissions } from '@backstage/plugin-kubernetes-common';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\n\nimport {\n type AuthenticationStrategy,\n kubernetesAuthStrategyExtensionPoint,\n type KubernetesAuthStrategyExtensionPoint,\n type KubernetesClustersSupplier,\n kubernetesClusterSupplierExtensionPoint,\n type KubernetesClusterSupplierExtensionPoint,\n KubernetesClusterSupplierFactory,\n type KubernetesFetcher,\n kubernetesFetcherExtensionPoint,\n type KubernetesFetcherExtensionPoint,\n KubernetesFetcherFactory,\n type KubernetesObjectsProvider,\n kubernetesObjectsProviderExtensionPoint,\n type KubernetesObjectsProviderExtensionPoint,\n KubernetesObjectsProviderFactory,\n KubernetesRouterExtensionPoint,\n kubernetesRouterExtensionPoint,\n KubernetesRouterFactory,\n type KubernetesServiceLocator,\n kubernetesServiceLocatorExtensionPoint,\n type KubernetesServiceLocatorExtensionPoint,\n KubernetesServiceLocatorFactory,\n} from '@backstage/plugin-kubernetes-node';\nimport { KubernetesRouter } from './service/KubernetesRouter';\nimport { KubernetesInitializer } from './service/KubernetesInitializer';\nimport { createKubernetesActions } from './actions';\n\nclass ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {\n private objectsProvider: KubernetesObjectsProviderFactory | undefined;\n\n getObjectsProvider() {\n return this.objectsProvider;\n }\n\n addObjectsProvider(\n provider: KubernetesObjectsProvider | KubernetesObjectsProviderFactory,\n ) {\n if (this.objectsProvider) {\n throw new Error(\n 'Multiple Kubernetes objects provider is not supported at this time',\n );\n }\n if (typeof provider !== 'function') {\n this.objectsProvider = async () => provider;\n } else {\n this.objectsProvider = provider;\n }\n }\n}\n\nclass ClusterSuplier implements KubernetesClusterSupplierExtensionPoint {\n private clusterSupplier: KubernetesClusterSupplierFactory | undefined;\n\n getClusterSupplier() {\n return this.clusterSupplier;\n }\n\n addClusterSupplier(\n clusterSupplier:\n | KubernetesClustersSupplier\n | KubernetesClusterSupplierFactory,\n ) {\n if (this.clusterSupplier) {\n throw new Error(\n 'Multiple Kubernetes Cluster Suppliers is not supported at this time',\n );\n }\n if (typeof clusterSupplier !== 'function') {\n this.clusterSupplier = async () => clusterSupplier;\n } else {\n this.clusterSupplier = clusterSupplier;\n }\n }\n}\n\nclass Fetcher implements KubernetesFetcherExtensionPoint {\n private fetcher: KubernetesFetcherFactory | undefined;\n\n getFetcher() {\n return this.fetcher;\n }\n\n addFetcher(fetcher: KubernetesFetcher | KubernetesFetcherFactory) {\n if (this.fetcher) {\n throw new Error(\n 'Multiple Kubernetes Fetchers is not supported at this time',\n );\n }\n if (typeof fetcher !== 'function') {\n this.fetcher = async () => fetcher;\n } else {\n this.fetcher = fetcher;\n }\n }\n}\n\nclass ServiceLocator implements KubernetesServiceLocatorExtensionPoint {\n private serviceLocator: KubernetesServiceLocatorFactory | undefined;\n\n getServiceLocator() {\n return this.serviceLocator;\n }\n\n addServiceLocator(\n serviceLocator: KubernetesServiceLocator | KubernetesServiceLocatorFactory,\n ) {\n if (this.serviceLocator) {\n throw new Error(\n 'Multiple Kubernetes Service Locators is not supported at this time',\n );\n }\n\n if (typeof serviceLocator !== 'function') {\n this.serviceLocator = async () => serviceLocator;\n } else {\n this.serviceLocator = serviceLocator;\n }\n }\n}\n\nclass AuthStrategy implements KubernetesAuthStrategyExtensionPoint {\n private authStrategies: Map<string, AuthenticationStrategy> | undefined;\n\n getAuthenticationStrategies() {\n return this.authStrategies;\n }\n\n addAuthStrategy(key: string, authStrategy: AuthenticationStrategy) {\n if (!this.authStrategies) {\n this.authStrategies = new Map<string, AuthenticationStrategy>();\n }\n\n if (key.includes('-')) {\n throw new Error('Strategy name can not include dashes');\n }\n\n this.authStrategies.set(key, authStrategy);\n }\n}\n\nclass CustomRouter implements KubernetesRouterExtensionPoint {\n private router: KubernetesRouterFactory | undefined;\n\n getRouter() {\n return this.router;\n }\n\n addRouter(router: KubernetesRouterFactory) {\n if (this.router) {\n throw new Error(\n 'Multiple Kubernetes routers is not supported at this time',\n );\n }\n\n this.router = router;\n }\n}\n\n/**\n * This is the backend plugin that provides the Kubernetes integration.\n * @public\n */\nexport const kubernetesPlugin = createBackendPlugin({\n pluginId: 'kubernetes',\n register(env) {\n const extPointObjectsProvider = new ObjectsProvider();\n const extPointClusterSuplier = new ClusterSuplier();\n const extPointAuthStrategy = new AuthStrategy();\n const extPointFetcher = new Fetcher();\n const extPointServiceLocator = new ServiceLocator();\n const extPointRouter = new CustomRouter();\n\n env.registerExtensionPoint(\n kubernetesObjectsProviderExtensionPoint,\n extPointObjectsProvider,\n );\n env.registerExtensionPoint(\n kubernetesClusterSupplierExtensionPoint,\n extPointClusterSuplier,\n );\n env.registerExtensionPoint(\n kubernetesAuthStrategyExtensionPoint,\n extPointAuthStrategy,\n );\n env.registerExtensionPoint(\n kubernetesFetcherExtensionPoint,\n extPointFetcher,\n );\n env.registerExtensionPoint(\n kubernetesServiceLocatorExtensionPoint,\n extPointServiceLocator,\n );\n env.registerExtensionPoint(kubernetesRouterExtensionPoint, extPointRouter);\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n discovery: coreServices.discovery,\n catalog: catalogServiceRef,\n permissions: coreServices.permissions,\n permissionsRegistry: coreServices.permissionsRegistry,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n auditor: coreServices.auditor,\n actionsRegistry: actionsRegistryServiceRef,\n },\n async init({\n http,\n logger,\n config,\n discovery,\n catalog,\n permissions,\n permissionsRegistry,\n auth,\n httpAuth,\n auditor,\n actionsRegistry,\n }) {\n // TODO: this could do with a cleanup and push some of this initialization somewhere else\n if (config.has('kubernetes')) {\n const initializer = KubernetesInitializer.create({\n logger,\n config,\n catalog,\n auth,\n fetcher: extPointFetcher.getFetcher(),\n clusterSupplier: extPointClusterSuplier.getClusterSupplier(),\n serviceLocator: extPointServiceLocator.getServiceLocator(),\n objectsProvider: extPointObjectsProvider.getObjectsProvider(),\n authStrategyMap: extPointAuthStrategy.getAuthenticationStrategies(),\n });\n\n const {\n fetcher,\n authStrategyMap,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n } = await initializer.init();\n\n const router = KubernetesRouter.create({\n logger,\n config,\n catalog,\n permissions,\n discovery,\n auth,\n httpAuth,\n auditor,\n authStrategyMap: Object.fromEntries(authStrategyMap.entries()),\n fetcher,\n clusterSupplier,\n serviceLocator,\n objectsProvider,\n customRouter: extPointRouter.getRouter(),\n });\n\n http.use(await router.getRouter());\n\n permissionsRegistry.addPermissions(kubernetesPermissions);\n\n createKubernetesActions({\n actionsRegistry,\n clusterSupplier,\n objectsProvider,\n catalog,\n });\n } else {\n logger.warn(\n 'Failed to initialize kubernetes backend: valid kubernetes config is missing',\n );\n }\n },\n });\n },\n});\n"],"names":["createBackendPlugin","kubernetesObjectsProviderExtensionPoint","kubernetesClusterSupplierExtensionPoint","kubernetesAuthStrategyExtensionPoint","kubernetesFetcherExtensionPoint","kubernetesServiceLocatorExtensionPoint","kubernetesRouterExtensionPoint","coreServices","catalogServiceRef","actionsRegistryServiceRef","KubernetesInitializer","KubernetesRouter","kubernetesPermissions","createKubernetesActions"],"mappings":"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"}
|
|
@@ -45,10 +45,15 @@ const statusCodeToErrorType = (statusCode) => {
|
|
|
45
45
|
};
|
|
46
46
|
class KubernetesConnection {
|
|
47
47
|
logger;
|
|
48
|
+
agentFactory;
|
|
48
49
|
agentCache = /* @__PURE__ */ new Map();
|
|
49
50
|
inClusterCache;
|
|
50
|
-
constructor({
|
|
51
|
+
constructor({
|
|
52
|
+
logger,
|
|
53
|
+
agentFactory
|
|
54
|
+
}) {
|
|
51
55
|
this.logger = logger;
|
|
56
|
+
this.agentFactory = agentFactory ?? ((options) => new https__namespace.Agent(options));
|
|
52
57
|
}
|
|
53
58
|
buildResourcePath(group, apiVersion, plural, namespace) {
|
|
54
59
|
const encode = (s) => encodeURIComponent(s);
|
|
@@ -140,7 +145,7 @@ class KubernetesConnection {
|
|
|
140
145
|
kc.loadFromCluster();
|
|
141
146
|
const cluster = kc.getCurrentCluster();
|
|
142
147
|
const url2 = new URL(cluster.server);
|
|
143
|
-
const agent2 = url2.protocol === "https:" ?
|
|
148
|
+
const agent2 = url2.protocol === "https:" ? this.agentFactory({
|
|
144
149
|
ca: fs__default.default.readFileSync(cluster.caFile),
|
|
145
150
|
keepAlive: true
|
|
146
151
|
}) : void 0;
|
|
@@ -163,7 +168,7 @@ class KubernetesConnection {
|
|
|
163
168
|
const key = this.buildAgentCacheKey(clusterDetails, credential);
|
|
164
169
|
let agent = this.agentCache.get(key);
|
|
165
170
|
if (!agent) {
|
|
166
|
-
agent =
|
|
171
|
+
agent = this.agentFactory({
|
|
167
172
|
ca,
|
|
168
173
|
rejectUnauthorized: !clusterDetails.skipTLSVerify,
|
|
169
174
|
keepAlive: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KubernetesConnection.cjs.js","sources":["../../src/service/KubernetesConnection.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 { Cluster } from '@kubernetes/client-node';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n KubernetesErrorTypes,\n KubernetesFetchError,\n} from '@backstage/plugin-kubernetes-common';\nimport fetch, { RequestInit, Response } from 'node-fetch';\nimport * as https from 'node:https';\nimport fs from 'fs-extra';\nimport {\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport const statusCodeToErrorType = (\n statusCode: number,\n): KubernetesErrorTypes => {\n switch (statusCode) {\n case 400:\n return 'BAD_REQUEST';\n case 401:\n return 'UNAUTHORIZED_ERROR';\n case 404:\n return 'NOT_FOUND';\n case 500:\n return 'SYSTEM_ERROR';\n default:\n return 'UNKNOWN_ERROR';\n }\n};\n\nexport type ConnectionResult =\n | { ok: true; url: URL; requestInit: RequestInit }\n | { ok: false; reason: 'missing_credentials' };\n\nexport class KubernetesConnection {\n private readonly logger: LoggerService;\n private readonly agentCache = new Map<string, https.Agent>();\n private inClusterCache:\n | { url: URL; agent: https.Agent | undefined }\n | undefined;\n\n constructor({ logger }: { logger: LoggerService }) {\n this.logger = logger;\n }\n\n buildResourcePath(\n group: string,\n apiVersion: string,\n plural: string,\n namespace?: string,\n ): string {\n const encode = (s: string) => encodeURIComponent(s);\n let path = group\n ? `/apis/${encode(group)}/${encode(apiVersion)}`\n : `/api/${encode(apiVersion)}`;\n if (namespace) {\n path += `/namespaces/${encode(namespace)}`;\n }\n path += `/${encode(plural)}`;\n return path;\n }\n\n async resolveConnection(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<ConnectionResult> {\n const authProvider =\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (this.isServiceAccountAuthentication(authProvider, clusterDetails)) {\n const [url, requestInit] = await this.fetchArgsInCluster(credential);\n return { ok: true, url, requestInit };\n } else if (!this.isCredentialMissing(authProvider, credential)) {\n const [url, requestInit] = await this.fetchArgs(\n clusterDetails,\n credential,\n );\n return { ok: true, url, requestInit };\n }\n return { ok: false, reason: 'missing_credentials' };\n }\n\n async fetchWithConnection(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n resourcePath: string,\n labelSelector?: string,\n ): Promise<Response> {\n const result = await this.resolveConnection(clusterDetails, credential);\n if (!result.ok) {\n return Promise.reject(\n new Error(\n `no bearer token or client cert for cluster '${clusterDetails.name}' and not running in Kubernetes`,\n ),\n );\n }\n\n const { url, requestInit } = result;\n if (url.pathname === '/') {\n url.pathname = resourcePath;\n } else {\n url.pathname += resourcePath;\n }\n\n if (labelSelector) {\n url.search = `labelSelector=${encodeURIComponent(labelSelector)}`;\n }\n\n return fetch(url, requestInit);\n }\n\n async handleUnsuccessfulResponse(\n clusterName: string,\n res: Response,\n ): Promise<KubernetesFetchError> {\n const resourcePath = new URL(res.url).pathname;\n this.logger.warn(\n `Received ${\n res.status\n } status when fetching \"${resourcePath}\" from cluster \"${clusterName}\"; body=[${await res.text()}]`,\n );\n return {\n errorType: statusCodeToErrorType(res.status),\n statusCode: res.status,\n resourcePath,\n };\n }\n\n private isServiceAccountAuthentication(\n authProvider: string,\n clusterDetails: ClusterDetails,\n ) {\n return (\n authProvider === 'serviceAccount' &&\n !clusterDetails.authMetadata.serviceAccountToken &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH)\n );\n }\n\n private isCredentialMissing(\n authProvider: string,\n credential: KubernetesCredential,\n ) {\n return (\n authProvider !== 'localKubectlProxy' && credential.type === 'anonymous'\n );\n }\n\n private buildRequestHeaders(\n credential: KubernetesCredential,\n ): Record<string, string> {\n return {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n };\n }\n\n private async fetchArgs(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n const { bufferFromFileOrString } = await import('@kubernetes/client-node');\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n };\n\n const url: URL = new URL(clusterDetails.url);\n if (url.protocol === 'https:') {\n const ca =\n bufferFromFileOrString(clusterDetails.caFile, clusterDetails.caData) ??\n undefined;\n requestInit.agent = this.getOrCreateAgent(clusterDetails, credential, ca);\n }\n return [url, requestInit];\n }\n\n private async fetchArgsInCluster(\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n if (!this.inClusterCache) {\n const { KubeConfig } = await import('@kubernetes/client-node');\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const cluster = kc.getCurrentCluster() as Cluster;\n const url = new URL(cluster.server);\n const agent =\n url.protocol === 'https:'\n ? new https.Agent({\n ca: fs.readFileSync(cluster.caFile as string),\n keepAlive: true,\n })\n : undefined;\n this.inClusterCache = { url, agent };\n }\n\n const { url, agent } = this.inClusterCache;\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n ...(agent && { agent }),\n };\n return [new URL(url.toString()), requestInit];\n }\n\n private buildAgentCacheKey(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): string {\n const certPart =\n credential.type === 'x509 client certificate'\n ? `${credential.cert}|${credential.key}`\n : '';\n return `${clusterDetails.url}|${clusterDetails.skipTLSVerify ?? false}|${\n clusterDetails.caData ?? ''\n }|${clusterDetails.caFile ?? ''}|${certPart}`;\n }\n\n private getOrCreateAgent(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ca: Buffer | string | undefined,\n ): https.Agent {\n const key = this.buildAgentCacheKey(clusterDetails, credential);\n\n let agent = this.agentCache.get(key);\n if (!agent) {\n agent = new https.Agent({\n ca,\n rejectUnauthorized: !clusterDetails.skipTLSVerify,\n keepAlive: true,\n ...(credential.type === 'x509 client certificate' && {\n cert: credential.cert,\n key: credential.key,\n }),\n });\n this.agentCache.set(key, agent);\n }\n return agent;\n }\n}\n"],"names":["ANNOTATION_KUBERNETES_AUTH_PROVIDER","fetch","fs","SERVICEACCOUNT_CA_PATH","url","agent","https"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KubernetesConnection.cjs.js","sources":["../../src/service/KubernetesConnection.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 { Cluster } from '@kubernetes/client-node';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n KubernetesErrorTypes,\n KubernetesFetchError,\n} from '@backstage/plugin-kubernetes-common';\nimport fetch, { RequestInit, Response } from 'node-fetch';\nimport * as https from 'node:https';\nimport fs from 'fs-extra';\nimport {\n ClusterDetails,\n KubernetesCredential,\n} from '@backstage/plugin-kubernetes-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport const statusCodeToErrorType = (\n statusCode: number,\n): KubernetesErrorTypes => {\n switch (statusCode) {\n case 400:\n return 'BAD_REQUEST';\n case 401:\n return 'UNAUTHORIZED_ERROR';\n case 404:\n return 'NOT_FOUND';\n case 500:\n return 'SYSTEM_ERROR';\n default:\n return 'UNKNOWN_ERROR';\n }\n};\n\nexport type ConnectionResult =\n | { ok: true; url: URL; requestInit: RequestInit }\n | { ok: false; reason: 'missing_credentials' };\n\nexport class KubernetesConnection {\n private readonly logger: LoggerService;\n private readonly agentFactory: (options: https.AgentOptions) => https.Agent;\n private readonly agentCache = new Map<string, https.Agent>();\n private inClusterCache:\n | { url: URL; agent: https.Agent | undefined }\n | undefined;\n\n constructor({\n logger,\n agentFactory,\n }: {\n logger: LoggerService;\n agentFactory?: (options: https.AgentOptions) => https.Agent;\n }) {\n this.logger = logger;\n this.agentFactory = agentFactory ?? (options => new https.Agent(options));\n }\n\n buildResourcePath(\n group: string,\n apiVersion: string,\n plural: string,\n namespace?: string,\n ): string {\n const encode = (s: string) => encodeURIComponent(s);\n let path = group\n ? `/apis/${encode(group)}/${encode(apiVersion)}`\n : `/api/${encode(apiVersion)}`;\n if (namespace) {\n path += `/namespaces/${encode(namespace)}`;\n }\n path += `/${encode(plural)}`;\n return path;\n }\n\n async resolveConnection(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<ConnectionResult> {\n const authProvider =\n clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (this.isServiceAccountAuthentication(authProvider, clusterDetails)) {\n const [url, requestInit] = await this.fetchArgsInCluster(credential);\n return { ok: true, url, requestInit };\n } else if (!this.isCredentialMissing(authProvider, credential)) {\n const [url, requestInit] = await this.fetchArgs(\n clusterDetails,\n credential,\n );\n return { ok: true, url, requestInit };\n }\n return { ok: false, reason: 'missing_credentials' };\n }\n\n async fetchWithConnection(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n resourcePath: string,\n labelSelector?: string,\n ): Promise<Response> {\n const result = await this.resolveConnection(clusterDetails, credential);\n if (!result.ok) {\n return Promise.reject(\n new Error(\n `no bearer token or client cert for cluster '${clusterDetails.name}' and not running in Kubernetes`,\n ),\n );\n }\n\n const { url, requestInit } = result;\n if (url.pathname === '/') {\n url.pathname = resourcePath;\n } else {\n url.pathname += resourcePath;\n }\n\n if (labelSelector) {\n url.search = `labelSelector=${encodeURIComponent(labelSelector)}`;\n }\n\n return fetch(url, requestInit);\n }\n\n async handleUnsuccessfulResponse(\n clusterName: string,\n res: Response,\n ): Promise<KubernetesFetchError> {\n const resourcePath = new URL(res.url).pathname;\n this.logger.warn(\n `Received ${\n res.status\n } status when fetching \"${resourcePath}\" from cluster \"${clusterName}\"; body=[${await res.text()}]`,\n );\n return {\n errorType: statusCodeToErrorType(res.status),\n statusCode: res.status,\n resourcePath,\n };\n }\n\n private isServiceAccountAuthentication(\n authProvider: string,\n clusterDetails: ClusterDetails,\n ) {\n return (\n authProvider === 'serviceAccount' &&\n !clusterDetails.authMetadata.serviceAccountToken &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH)\n );\n }\n\n private isCredentialMissing(\n authProvider: string,\n credential: KubernetesCredential,\n ) {\n return (\n authProvider !== 'localKubectlProxy' && credential.type === 'anonymous'\n );\n }\n\n private buildRequestHeaders(\n credential: KubernetesCredential,\n ): Record<string, string> {\n return {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n ...(credential.type === 'bearer token' && {\n Authorization: `Bearer ${credential.token}`,\n }),\n };\n }\n\n private async fetchArgs(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n const { bufferFromFileOrString } = await import('@kubernetes/client-node');\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n };\n\n const url: URL = new URL(clusterDetails.url);\n if (url.protocol === 'https:') {\n const ca =\n bufferFromFileOrString(clusterDetails.caFile, clusterDetails.caData) ??\n undefined;\n requestInit.agent = this.getOrCreateAgent(clusterDetails, credential, ca);\n }\n return [url, requestInit];\n }\n\n private async fetchArgsInCluster(\n credential: KubernetesCredential,\n ): Promise<[URL, fetch.RequestInit]> {\n if (!this.inClusterCache) {\n const { KubeConfig } = await import('@kubernetes/client-node');\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const cluster = kc.getCurrentCluster() as Cluster;\n const url = new URL(cluster.server);\n const agent =\n url.protocol === 'https:'\n ? this.agentFactory({\n ca: fs.readFileSync(cluster.caFile as string),\n keepAlive: true,\n })\n : undefined;\n this.inClusterCache = { url, agent };\n }\n\n const { url, agent } = this.inClusterCache;\n const requestInit: RequestInit = {\n method: 'GET',\n headers: this.buildRequestHeaders(credential),\n redirect: 'manual',\n ...(agent && { agent }),\n };\n return [new URL(url.toString()), requestInit];\n }\n\n private buildAgentCacheKey(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ): string {\n const certPart =\n credential.type === 'x509 client certificate'\n ? `${credential.cert}|${credential.key}`\n : '';\n return `${clusterDetails.url}|${clusterDetails.skipTLSVerify ?? false}|${\n clusterDetails.caData ?? ''\n }|${clusterDetails.caFile ?? ''}|${certPart}`;\n }\n\n private getOrCreateAgent(\n clusterDetails: ClusterDetails,\n credential: KubernetesCredential,\n ca: Buffer | string | undefined,\n ): https.Agent {\n const key = this.buildAgentCacheKey(clusterDetails, credential);\n\n let agent = this.agentCache.get(key);\n if (!agent) {\n agent = this.agentFactory({\n ca,\n rejectUnauthorized: !clusterDetails.skipTLSVerify,\n keepAlive: true,\n ...(credential.type === 'x509 client certificate' && {\n cert: credential.cert,\n key: credential.key,\n }),\n });\n this.agentCache.set(key, agent);\n }\n return agent;\n }\n}\n"],"names":["https","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fetch","fs","SERVICEACCOUNT_CA_PATH","url","agent"],"mappings":"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"}
|
|
@@ -131,7 +131,7 @@ class KubernetesProxy {
|
|
|
131
131
|
port: url.port,
|
|
132
132
|
ca: bufferFromFileOrString(cluster.caFile, cluster.caData)?.toString()
|
|
133
133
|
};
|
|
134
|
-
const authHeader = req.headers[HEADER_KUBERNETES_AUTH.
|
|
134
|
+
const authHeader = req.headers[HEADER_KUBERNETES_AUTH.toLowerCase()];
|
|
135
135
|
if (typeof authHeader === "string") {
|
|
136
136
|
req.headers.authorization = authHeader;
|
|
137
137
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KubernetesProxy.cjs.js","sources":["../../src/service/KubernetesProxy.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {\n ErrorResponseBody,\n ForwardedError,\n NotAllowedError,\n NotFoundError,\n serializeError,\n} from '@backstage/errors';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n kubernetesProxyPermission,\n KubernetesRequestAuth,\n} from '@backstage/plugin-kubernetes-common';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport type { Cluster } from '@kubernetes/client-node';\nimport { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';\nimport fs from 'fs-extra';\n\nimport {\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\n\nimport {\n ProxyMiddlewareCache,\n resolveProxyMiddlewareCacheOptions,\n} from './ProxyMiddlewareCache';\n\nimport type { NextFunction, Request, Response } from 'express';\nimport { IncomingHttpHeaders } from 'node:http';\nimport {\n AuditorService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { ProxyAuditSession } from './ProxyAuditSession';\n\nconst PROXY_PREPARED_TARGET = Symbol('kubernetesProxyPreparedTarget');\n\ntype PreparedProxyTarget = {\n proxyTarget: any;\n rewrittenPath: string;\n cluster: ClusterDetails;\n};\n\n/**\n * The header that is used to specify the cluster name.\n *\n * @public\n */\nexport const HEADER_KUBERNETES_CLUSTER: string = 'Backstage-Kubernetes-Cluster';\n\n/**\n * The header that is used to specify the Authentication Authorities token.\n * e.x if using the google auth provider as your authentication authority then this field would be the google provided bearer token.\n * @public\n */\nexport const HEADER_KUBERNETES_AUTH: string =\n 'Backstage-Kubernetes-Authorization';\n\n/**\n * The options object expected to be passed as a parameter to KubernetesProxy.createRequestHandler().\n *\n * @public\n */\nexport type KubernetesProxyCreateRequestHandlerOptions = {\n permissionApi: PermissionsService;\n};\n\n/**\n * Options accepted as a parameter by the KubernetesProxy\n *\n * @public\n */\nexport type KubernetesProxyOptions = {\n logger: LoggerService;\n clusterSupplier: KubernetesClustersSupplier;\n authStrategy: AuthenticationStrategy;\n discovery: DiscoveryService;\n httpAuth: HttpAuthService;\n auditor?: AuditorService;\n middlewareCache?: {\n ttlMs?: number;\n maxSize?: number;\n };\n};\n\n/**\n * A proxy that routes requests to the Kubernetes API.\n *\n * @public\n */\nexport class KubernetesProxy {\n private readonly middlewareCache: ProxyMiddlewareCache;\n private readonly logger: LoggerService;\n private readonly clusterSupplier: KubernetesClustersSupplier;\n private readonly authStrategy: AuthenticationStrategy;\n private readonly httpAuth: HttpAuthService;\n private readonly auditor?: AuditorService;\n\n constructor(options: KubernetesProxyOptions) {\n this.logger = options.logger;\n this.clusterSupplier = options.clusterSupplier;\n this.authStrategy = options.authStrategy;\n this.httpAuth = options.httpAuth;\n this.auditor = options.auditor;\n this.middlewareCache = new ProxyMiddlewareCache(\n resolveProxyMiddlewareCacheOptions(options.middlewareCache),\n );\n }\n\n public createRequestHandler(\n options: KubernetesProxyCreateRequestHandlerOptions,\n ): RequestHandler {\n const { permissionApi } = options;\n return async (req, res, next) => {\n const clusterNameHeader =\n req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()];\n const clusterName =\n typeof clusterNameHeader === 'string' && clusterNameHeader.length > 0\n ? clusterNameHeader\n : 'unknown';\n const path = req.path || req.url.split('?')[0] || '';\n\n let clientDisconnected = false;\n const onSocketClose = () => {\n clientDisconnected = true;\n };\n req.socket.once('close', onSocketClose);\n\n let auditSession: ProxyAuditSession | undefined;\n let finishListenerAttached = false;\n try {\n auditSession = await ProxyAuditSession.start(this.auditor, {\n req,\n clusterName,\n method: req.method,\n path,\n logger: this.logger,\n });\n\n finishListenerAttached = await this.authorizeAndDispatch(\n req,\n res,\n next,\n permissionApi,\n auditSession,\n onSocketClose,\n () => clientDisconnected,\n );\n } catch (error) {\n req.socket.removeListener('close', onSocketClose);\n const err = error instanceof Error ? error : new Error(String(error));\n if (auditSession && !finishListenerAttached) {\n auditSession.finalize(err);\n }\n throw error;\n }\n };\n }\n\n /**\n * Authorizes the request, resolves the target cluster, and dispatches\n * to either the HTTP or WebSocket proxy path. Returns `true` when\n * HTTP finish listeners have been attached.\n */\n private async authorizeAndDispatch(\n req: Request,\n res: Response,\n next: NextFunction,\n permissionApi: PermissionsService,\n auditSession: ProxyAuditSession,\n onSocketClose: () => void,\n isClientDisconnected: () => boolean,\n ): Promise<boolean> {\n const authorizeResponse = await permissionApi.authorize(\n [{ permission: kubernetesProxyPermission }],\n { credentials: await this.httpAuth.credentials(req) },\n );\n\n if (authorizeResponse[0].result === AuthorizeResult.DENY) {\n req.socket.removeListener('close', onSocketClose);\n auditSession.finalize(new NotAllowedError('Unauthorized'));\n res.status(403).json({\n error: serializeError(new NotAllowedError('Unauthorized')),\n });\n return false;\n }\n\n if (isClientDisconnected()) {\n auditSession.finalize(\n new Error('Client disconnected before proxy dispatch'),\n );\n return false;\n }\n\n const prepared = await this.prepareProxyTarget(req);\n auditSession.setResolvedClusterName(prepared.cluster.name);\n (req as any)[PROXY_PREPARED_TARGET] = prepared;\n const middleware = this.getOrCreateMiddleware(prepared.cluster);\n\n if (isClientDisconnected()) {\n auditSession.finalize(\n new Error('Client disconnected before proxy dispatch'),\n );\n return false;\n }\n\n return KubernetesProxy.dispatchToProxy(\n req,\n res,\n next,\n middleware,\n auditSession,\n onSocketClose,\n );\n }\n\n /**\n * Resolves the cluster, credentials, and target URL for a proxy request\n * before dispatching to the middleware. This ensures all async preparation\n * completes under the caller's try/catch, avoiding unhandled rejections\n * from http-proxy-middleware's fire-and-forget upgrade() path.\n */\n private async prepareProxyTarget(req: Request): Promise<PreparedProxyTarget> {\n const cluster = await this.getClusterForRequest(req);\n const url = new URL(cluster.url);\n\n const { bufferFromFileOrString } = await import('@kubernetes/client-node');\n\n const target: any = {\n protocol: url.protocol,\n host: url.hostname,\n port: url.port,\n ca: bufferFromFileOrString(cluster.caFile, cluster.caData)?.toString(),\n };\n\n const authHeader =\n req.headers[HEADER_KUBERNETES_AUTH.toLocaleLowerCase('en-US')];\n if (typeof authHeader === 'string') {\n req.headers.authorization = authHeader;\n } else {\n const authObj = KubernetesProxy.authHeadersToKubernetesRequestAuth(\n req.headers,\n );\n\n const credential = await this.authStrategy.getCredential(\n cluster,\n authObj,\n );\n\n if (credential.type === 'bearer token') {\n req.headers.authorization = `Bearer ${credential.token}`;\n } else if (credential.type === 'x509 client certificate') {\n target.key = credential.key;\n target.cert = credential.cert;\n }\n }\n\n const requestPath = req.originalUrl || req.url || '';\n const rewrittenPath = requestPath.replace(\n new RegExp(\n `^${(req.baseUrl || '').replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`,\n ),\n url.pathname || '',\n );\n\n return { proxyTarget: target, rewrittenPath, cluster };\n }\n\n // We create one middleware per remote cluster and hold on to them, because\n // the secure property isn't possible to decide on a per-request basis with a\n // single middleware instance. Target resolution and path rewriting are handled\n // per-request via the PROXY_PREPARED_TARGET symbol stashed on each req by\n // prepareProxyTarget. Entries are refreshed after a TTL or when cluster details\n // change.\n private getOrCreateMiddleware(cluster: ClusterDetails): RequestHandler {\n let middleware = this.middlewareCache.get(cluster);\n if (!middleware) {\n const logger = this.logger.child({ cluster: cluster.name });\n middleware = createProxyMiddleware({\n logProvider: () => ({\n log: logger.info.bind(logger),\n debug: logger.debug.bind(logger),\n info: logger.info.bind(logger),\n warn: logger.warn.bind(logger),\n error: logger.error.bind(logger),\n }),\n // ws must be false to prevent http-proxy-middleware from auto-subscribing\n // to the server's 'upgrade' event, which would bypass Express routing and\n // skip audit event creation for WebSocket requests.\n ws: false,\n secure: !cluster.skipTLSVerify,\n changeOrigin: true,\n router: req => {\n return (req as any)[PROXY_PREPARED_TARGET]?.proxyTarget;\n },\n pathRewrite: (_path, req) => {\n return (req as any)[PROXY_PREPARED_TARGET]?.rewrittenPath ?? _path;\n },\n onError: (error, req, res) => {\n const wrappedError = new ForwardedError(\n `Cluster '${cluster.name}' request error`,\n error,\n );\n\n logger.error('Kubernetes proxy error', wrappedError);\n\n if (typeof (res as { status?: unknown }).status !== 'function') {\n return;\n }\n\n const body: ErrorResponseBody = {\n error: serializeError(wrappedError, {\n includeStack: process.env.NODE_ENV === 'development',\n }),\n request: { method: req.method, url: req.originalUrl },\n response: { statusCode: 500 },\n };\n res.status(500).json(body);\n },\n onProxyReqWs: ProxyAuditSession.handleWebSocketProxyReq,\n });\n this.middlewareCache.set(cluster, middleware);\n }\n return middleware;\n }\n\n /**\n * Branches to WebSocket upgrade or HTTP middleware dispatch and wires\n * up the appropriate audit listeners. Returns `true` when HTTP finish\n * listeners have been attached (used by the caller's catch block to\n * avoid double-finalizing).\n */\n private static dispatchToProxy(\n req: Request,\n res: Response,\n next: NextFunction,\n middleware: RequestHandler,\n auditSession: ProxyAuditSession,\n onSocketClose: () => void,\n ): boolean {\n const isWebSocketUpgrade =\n (req.header('connection') ?? '')\n .toLowerCase()\n .split(',')\n .some(t => t.trim() === 'upgrade') &&\n req.header('upgrade')?.toLowerCase() === 'websocket';\n\n if (isWebSocketUpgrade) {\n auditSession.prepareForWebSocketUpgrade(req, onSocketClose);\n\n try {\n middleware.upgrade!(req, req.socket, undefined);\n } catch (error) {\n req.socket.removeListener('close', onSocketClose);\n auditSession.finalize(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n return false;\n }\n\n // HTTP path: remove the early close listener; HTTP audit listeners\n // on the response take over from here.\n req.socket.removeListener('close', onSocketClose);\n auditSession.attachToHttpResponse(res);\n middleware(req, res, next);\n return true;\n }\n\n private async getClusterForRequest(req: Request): Promise<ClusterDetails> {\n const { KubeConfig } = await import('@kubernetes/client-node');\n\n const clusterName = req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()];\n const clusters = await this.clusterSupplier.getClusters({\n credentials: await this.httpAuth.credentials(req),\n });\n\n if (!clusters || clusters.length <= 0) {\n throw new NotFoundError(`No Clusters configured`);\n }\n\n const hasClusterNameHeader =\n typeof clusterName === 'string' && clusterName.length > 0;\n\n let cluster: ClusterDetails | undefined;\n\n if (hasClusterNameHeader) {\n cluster = clusters.find(c => c.name === clusterName);\n } else if (clusters.length === 1) {\n cluster = clusters.at(0);\n }\n\n if (!cluster) {\n throw new NotFoundError(`Cluster '${clusterName}' not found`);\n }\n\n const authProvider =\n cluster.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (\n authProvider === 'serviceAccount' &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH) &&\n !cluster.authMetadata.serviceAccountToken\n ) {\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const clusterFromKubeConfig = kc.getCurrentCluster() as Cluster;\n\n const url = new URL(clusterFromKubeConfig.server);\n cluster.url = clusterFromKubeConfig.server;\n if (url.protocol === 'https:') {\n cluster.caFile = clusterFromKubeConfig.caFile;\n }\n }\n\n return cluster;\n }\n\n private static authHeadersToKubernetesRequestAuth(\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n return Object.keys(originalHeaders)\n .filter(header => header.startsWith('backstage-kubernetes-authorization'))\n .map(header =>\n KubernetesProxy.headerToDictionary(header, originalHeaders),\n )\n .filter(headerAsDic => Object.keys(headerAsDic).length !== 0)\n .reduce(KubernetesProxy.combineHeaders, {});\n }\n\n private static headerToDictionary(\n header: string,\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n const obj: KubernetesRequestAuth = {};\n const headerSplit = header.split('-');\n if (headerSplit.length >= 4) {\n const framework = headerSplit[3].toLowerCase();\n if (headerSplit.length >= 5) {\n const provider = headerSplit.slice(4).join('-').toLowerCase();\n obj[framework] = { [provider]: originalHeaders[header] };\n } else {\n obj[framework] = originalHeaders[header];\n }\n }\n return obj;\n }\n\n private static combineHeaders(\n authObj: any,\n header: any,\n ): KubernetesRequestAuth {\n const framework = Object.keys(header)[0];\n\n if (authObj[framework]) {\n authObj[framework] = {\n ...authObj[framework],\n ...header[framework],\n };\n } else {\n authObj[framework] = header[framework];\n }\n\n return authObj;\n }\n}\n"],"names":["ProxyMiddlewareCache","resolveProxyMiddlewareCacheOptions","ProxyAuditSession","kubernetesProxyPermission","AuthorizeResult","NotAllowedError","serializeError","createProxyMiddleware","ForwardedError","NotFoundError","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fs","SERVICEACCOUNT_CA_PATH"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KubernetesProxy.cjs.js","sources":["../../src/service/KubernetesProxy.ts"],"sourcesContent":["/*\n * Copyright 2022 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 {\n ErrorResponseBody,\n ForwardedError,\n NotAllowedError,\n NotFoundError,\n serializeError,\n} from '@backstage/errors';\nimport {\n ANNOTATION_KUBERNETES_AUTH_PROVIDER,\n SERVICEACCOUNT_CA_PATH,\n kubernetesProxyPermission,\n KubernetesRequestAuth,\n} from '@backstage/plugin-kubernetes-common';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport type { Cluster } from '@kubernetes/client-node';\nimport { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';\nimport fs from 'fs-extra';\n\nimport {\n AuthenticationStrategy,\n ClusterDetails,\n KubernetesClustersSupplier,\n} from '@backstage/plugin-kubernetes-node';\n\nimport {\n ProxyMiddlewareCache,\n resolveProxyMiddlewareCacheOptions,\n} from './ProxyMiddlewareCache';\n\nimport type { NextFunction, Request, Response } from 'express';\nimport { IncomingHttpHeaders } from 'node:http';\nimport {\n AuditorService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { ProxyAuditSession } from './ProxyAuditSession';\n\nconst PROXY_PREPARED_TARGET = Symbol('kubernetesProxyPreparedTarget');\n\ntype PreparedProxyTarget = {\n proxyTarget: any;\n rewrittenPath: string;\n cluster: ClusterDetails;\n};\n\n/**\n * The header that is used to specify the cluster name.\n *\n * @public\n */\nexport const HEADER_KUBERNETES_CLUSTER: string = 'Backstage-Kubernetes-Cluster';\n\n/**\n * The header that is used to specify the Authentication Authorities token.\n * e.x if using the google auth provider as your authentication authority then this field would be the google provided bearer token.\n * @public\n */\nexport const HEADER_KUBERNETES_AUTH: string =\n 'Backstage-Kubernetes-Authorization';\n\n/**\n * The options object expected to be passed as a parameter to KubernetesProxy.createRequestHandler().\n *\n * @public\n */\nexport type KubernetesProxyCreateRequestHandlerOptions = {\n permissionApi: PermissionsService;\n};\n\n/**\n * Options accepted as a parameter by the KubernetesProxy\n *\n * @public\n */\nexport type KubernetesProxyOptions = {\n logger: LoggerService;\n clusterSupplier: KubernetesClustersSupplier;\n authStrategy: AuthenticationStrategy;\n discovery: DiscoveryService;\n httpAuth: HttpAuthService;\n auditor?: AuditorService;\n middlewareCache?: {\n ttlMs?: number;\n maxSize?: number;\n };\n};\n\n/**\n * A proxy that routes requests to the Kubernetes API.\n *\n * @public\n */\nexport class KubernetesProxy {\n private readonly middlewareCache: ProxyMiddlewareCache;\n private readonly logger: LoggerService;\n private readonly clusterSupplier: KubernetesClustersSupplier;\n private readonly authStrategy: AuthenticationStrategy;\n private readonly httpAuth: HttpAuthService;\n private readonly auditor?: AuditorService;\n\n constructor(options: KubernetesProxyOptions) {\n this.logger = options.logger;\n this.clusterSupplier = options.clusterSupplier;\n this.authStrategy = options.authStrategy;\n this.httpAuth = options.httpAuth;\n this.auditor = options.auditor;\n this.middlewareCache = new ProxyMiddlewareCache(\n resolveProxyMiddlewareCacheOptions(options.middlewareCache),\n );\n }\n\n public createRequestHandler(\n options: KubernetesProxyCreateRequestHandlerOptions,\n ): RequestHandler {\n const { permissionApi } = options;\n return async (req, res, next) => {\n const clusterNameHeader =\n req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()];\n const clusterName =\n typeof clusterNameHeader === 'string' && clusterNameHeader.length > 0\n ? clusterNameHeader\n : 'unknown';\n const path = req.path || req.url.split('?')[0] || '';\n\n let clientDisconnected = false;\n const onSocketClose = () => {\n clientDisconnected = true;\n };\n req.socket.once('close', onSocketClose);\n\n let auditSession: ProxyAuditSession | undefined;\n let finishListenerAttached = false;\n try {\n auditSession = await ProxyAuditSession.start(this.auditor, {\n req,\n clusterName,\n method: req.method,\n path,\n logger: this.logger,\n });\n\n finishListenerAttached = await this.authorizeAndDispatch(\n req,\n res,\n next,\n permissionApi,\n auditSession,\n onSocketClose,\n () => clientDisconnected,\n );\n } catch (error) {\n req.socket.removeListener('close', onSocketClose);\n const err = error instanceof Error ? error : new Error(String(error));\n if (auditSession && !finishListenerAttached) {\n auditSession.finalize(err);\n }\n throw error;\n }\n };\n }\n\n /**\n * Authorizes the request, resolves the target cluster, and dispatches\n * to either the HTTP or WebSocket proxy path. Returns `true` when\n * HTTP finish listeners have been attached.\n */\n private async authorizeAndDispatch(\n req: Request,\n res: Response,\n next: NextFunction,\n permissionApi: PermissionsService,\n auditSession: ProxyAuditSession,\n onSocketClose: () => void,\n isClientDisconnected: () => boolean,\n ): Promise<boolean> {\n const authorizeResponse = await permissionApi.authorize(\n [{ permission: kubernetesProxyPermission }],\n { credentials: await this.httpAuth.credentials(req) },\n );\n\n if (authorizeResponse[0].result === AuthorizeResult.DENY) {\n req.socket.removeListener('close', onSocketClose);\n auditSession.finalize(new NotAllowedError('Unauthorized'));\n res.status(403).json({\n error: serializeError(new NotAllowedError('Unauthorized')),\n });\n return false;\n }\n\n if (isClientDisconnected()) {\n auditSession.finalize(\n new Error('Client disconnected before proxy dispatch'),\n );\n return false;\n }\n\n const prepared = await this.prepareProxyTarget(req);\n auditSession.setResolvedClusterName(prepared.cluster.name);\n (req as any)[PROXY_PREPARED_TARGET] = prepared;\n const middleware = this.getOrCreateMiddleware(prepared.cluster);\n\n if (isClientDisconnected()) {\n auditSession.finalize(\n new Error('Client disconnected before proxy dispatch'),\n );\n return false;\n }\n\n return KubernetesProxy.dispatchToProxy(\n req,\n res,\n next,\n middleware,\n auditSession,\n onSocketClose,\n );\n }\n\n /**\n * Resolves the cluster, credentials, and target URL for a proxy request\n * before dispatching to the middleware. This ensures all async preparation\n * completes under the caller's try/catch, avoiding unhandled rejections\n * from http-proxy-middleware's fire-and-forget upgrade() path.\n */\n private async prepareProxyTarget(req: Request): Promise<PreparedProxyTarget> {\n const cluster = await this.getClusterForRequest(req);\n const url = new URL(cluster.url);\n\n const { bufferFromFileOrString } = await import('@kubernetes/client-node');\n\n const target: any = {\n protocol: url.protocol,\n host: url.hostname,\n port: url.port,\n ca: bufferFromFileOrString(cluster.caFile, cluster.caData)?.toString(),\n };\n\n const authHeader = req.headers[HEADER_KUBERNETES_AUTH.toLowerCase()];\n if (typeof authHeader === 'string') {\n req.headers.authorization = authHeader;\n } else {\n const authObj = KubernetesProxy.authHeadersToKubernetesRequestAuth(\n req.headers,\n );\n\n const credential = await this.authStrategy.getCredential(\n cluster,\n authObj,\n );\n\n if (credential.type === 'bearer token') {\n req.headers.authorization = `Bearer ${credential.token}`;\n } else if (credential.type === 'x509 client certificate') {\n target.key = credential.key;\n target.cert = credential.cert;\n }\n }\n\n const requestPath = req.originalUrl || req.url || '';\n const rewrittenPath = requestPath.replace(\n new RegExp(\n `^${(req.baseUrl || '').replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`,\n ),\n url.pathname || '',\n );\n\n return { proxyTarget: target, rewrittenPath, cluster };\n }\n\n // We create one middleware per remote cluster and hold on to them, because\n // the secure property isn't possible to decide on a per-request basis with a\n // single middleware instance. Target resolution and path rewriting are handled\n // per-request via the PROXY_PREPARED_TARGET symbol stashed on each req by\n // prepareProxyTarget. Entries are refreshed after a TTL or when cluster details\n // change.\n private getOrCreateMiddleware(cluster: ClusterDetails): RequestHandler {\n let middleware = this.middlewareCache.get(cluster);\n if (!middleware) {\n const logger = this.logger.child({ cluster: cluster.name });\n middleware = createProxyMiddleware({\n logProvider: () => ({\n log: logger.info.bind(logger),\n debug: logger.debug.bind(logger),\n info: logger.info.bind(logger),\n warn: logger.warn.bind(logger),\n error: logger.error.bind(logger),\n }),\n // ws must be false to prevent http-proxy-middleware from auto-subscribing\n // to the server's 'upgrade' event, which would bypass Express routing and\n // skip audit event creation for WebSocket requests.\n ws: false,\n secure: !cluster.skipTLSVerify,\n changeOrigin: true,\n router: req => {\n return (req as any)[PROXY_PREPARED_TARGET]?.proxyTarget;\n },\n pathRewrite: (_path, req) => {\n return (req as any)[PROXY_PREPARED_TARGET]?.rewrittenPath ?? _path;\n },\n onError: (error, req, res) => {\n const wrappedError = new ForwardedError(\n `Cluster '${cluster.name}' request error`,\n error,\n );\n\n logger.error('Kubernetes proxy error', wrappedError);\n\n if (typeof (res as { status?: unknown }).status !== 'function') {\n return;\n }\n\n const body: ErrorResponseBody = {\n error: serializeError(wrappedError, {\n includeStack: process.env.NODE_ENV === 'development',\n }),\n request: { method: req.method, url: req.originalUrl },\n response: { statusCode: 500 },\n };\n res.status(500).json(body);\n },\n onProxyReqWs: ProxyAuditSession.handleWebSocketProxyReq,\n });\n this.middlewareCache.set(cluster, middleware);\n }\n return middleware;\n }\n\n /**\n * Branches to WebSocket upgrade or HTTP middleware dispatch and wires\n * up the appropriate audit listeners. Returns `true` when HTTP finish\n * listeners have been attached (used by the caller's catch block to\n * avoid double-finalizing).\n */\n private static dispatchToProxy(\n req: Request,\n res: Response,\n next: NextFunction,\n middleware: RequestHandler,\n auditSession: ProxyAuditSession,\n onSocketClose: () => void,\n ): boolean {\n const isWebSocketUpgrade =\n (req.header('connection') ?? '')\n .toLowerCase()\n .split(',')\n .some(t => t.trim() === 'upgrade') &&\n req.header('upgrade')?.toLowerCase() === 'websocket';\n\n if (isWebSocketUpgrade) {\n auditSession.prepareForWebSocketUpgrade(req, onSocketClose);\n\n try {\n middleware.upgrade!(req, req.socket, undefined);\n } catch (error) {\n req.socket.removeListener('close', onSocketClose);\n auditSession.finalize(\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n return false;\n }\n\n // HTTP path: remove the early close listener; HTTP audit listeners\n // on the response take over from here.\n req.socket.removeListener('close', onSocketClose);\n auditSession.attachToHttpResponse(res);\n middleware(req, res, next);\n return true;\n }\n\n private async getClusterForRequest(req: Request): Promise<ClusterDetails> {\n const { KubeConfig } = await import('@kubernetes/client-node');\n\n const clusterName = req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()];\n const clusters = await this.clusterSupplier.getClusters({\n credentials: await this.httpAuth.credentials(req),\n });\n\n if (!clusters || clusters.length <= 0) {\n throw new NotFoundError(`No Clusters configured`);\n }\n\n const hasClusterNameHeader =\n typeof clusterName === 'string' && clusterName.length > 0;\n\n let cluster: ClusterDetails | undefined;\n\n if (hasClusterNameHeader) {\n cluster = clusters.find(c => c.name === clusterName);\n } else if (clusters.length === 1) {\n cluster = clusters.at(0);\n }\n\n if (!cluster) {\n throw new NotFoundError(`Cluster '${clusterName}' not found`);\n }\n\n const authProvider =\n cluster.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];\n\n if (\n authProvider === 'serviceAccount' &&\n fs.pathExistsSync(SERVICEACCOUNT_CA_PATH) &&\n !cluster.authMetadata.serviceAccountToken\n ) {\n const kc = new KubeConfig();\n kc.loadFromCluster();\n const clusterFromKubeConfig = kc.getCurrentCluster() as Cluster;\n\n const url = new URL(clusterFromKubeConfig.server);\n cluster.url = clusterFromKubeConfig.server;\n if (url.protocol === 'https:') {\n cluster.caFile = clusterFromKubeConfig.caFile;\n }\n }\n\n return cluster;\n }\n\n private static authHeadersToKubernetesRequestAuth(\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n return Object.keys(originalHeaders)\n .filter(header => header.startsWith('backstage-kubernetes-authorization'))\n .map(header =>\n KubernetesProxy.headerToDictionary(header, originalHeaders),\n )\n .filter(headerAsDic => Object.keys(headerAsDic).length !== 0)\n .reduce(KubernetesProxy.combineHeaders, {});\n }\n\n private static headerToDictionary(\n header: string,\n originalHeaders: IncomingHttpHeaders,\n ): KubernetesRequestAuth {\n const obj: KubernetesRequestAuth = {};\n const headerSplit = header.split('-');\n if (headerSplit.length >= 4) {\n const framework = headerSplit[3].toLowerCase();\n if (headerSplit.length >= 5) {\n const provider = headerSplit.slice(4).join('-').toLowerCase();\n obj[framework] = { [provider]: originalHeaders[header] };\n } else {\n obj[framework] = originalHeaders[header];\n }\n }\n return obj;\n }\n\n private static combineHeaders(\n authObj: any,\n header: any,\n ): KubernetesRequestAuth {\n const framework = Object.keys(header)[0];\n\n if (authObj[framework]) {\n authObj[framework] = {\n ...authObj[framework],\n ...header[framework],\n };\n } else {\n authObj[framework] = header[framework];\n }\n\n return authObj;\n }\n}\n"],"names":["ProxyMiddlewareCache","resolveProxyMiddlewareCacheOptions","ProxyAuditSession","kubernetesProxyPermission","AuthorizeResult","NotAllowedError","serializeError","createProxyMiddleware","ForwardedError","NotFoundError","ANNOTATION_KUBERNETES_AUTH_PROVIDER","fs","SERVICEACCOUNT_CA_PATH"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var pluginKubernetesCommon = require('@backstage/plugin-kubernetes-common');
|
|
4
|
-
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
4
|
var express = require('express');
|
|
6
5
|
var Router = require('express-promise-router');
|
|
7
6
|
require('@aws-sdk/credential-providers');
|
|
@@ -113,11 +112,6 @@ class KubernetesRouter {
|
|
|
113
112
|
const router = Router__default.default();
|
|
114
113
|
router.use("/proxy", proxy.createRequestHandler({ permissionApi }));
|
|
115
114
|
router.use(express__default.default.json());
|
|
116
|
-
router.use(
|
|
117
|
-
pluginPermissionNode.createPermissionIntegrationRouter({
|
|
118
|
-
permissions: pluginKubernetesCommon.kubernetesPermissions
|
|
119
|
-
})
|
|
120
|
-
);
|
|
121
115
|
router.post("/services/:serviceId", async (req, res) => {
|
|
122
116
|
const serviceId = req.params.serviceId;
|
|
123
117
|
const requestBody = req.body;
|
|
@@ -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';\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 { parseEntityRef, 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\n const credentials = await httpAuth.credentials(req);\n\n let resolvedEntity = requestBody?.entity;\n if (requestBody?.entity) {\n if (!entityRef) {\n throw new NotAllowedError('Invalid entity reference');\n }\n const parsedRef = parseEntityRef(entityRef);\n const catalogEntity = await catalog.getEntityByRef(parsedRef, {\n credentials,\n });\n if (!catalogEntity) {\n throw new NotAllowedError(`Entity not found, ${entityRef}`);\n }\n resolvedEntity = catalogEntity;\n }\n\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity: resolvedEntity,\n auth: requestBody?.auth || {},\n },\n { credentials },\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","parseEntityRef","kubernetesClustersReadPermission","ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER","ANNOTATION_KUBERNETES_AUTH_PROVIDER","addResourceRoutesToRouter","toError"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAyEO,MAAM,gBAAA,CAAiB;AAAA,EAC5B,OAAO,OAAO,GAAA,EAA4B;AACxC,IAAA,OAAO,IAAI,iBAAiB,GAAG,CAAA;AAAA,EACjC;AAAA,EAEmB,GAAA;AAAA,EAEnB,YAAY,GAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AAAA,EACb;AAAA,EAEA,MAAa,SAAA,GAAY;AACvB,IAAA,MAAM;AAAA,MACJ,MAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAA;AAAA,MACA,eAAA;AAAA,MACA,eAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,QACE,IAAA,CAAK,GAAA;AAET,IAAA,MAAA,CAAO,KAAK,iCAAiC,CAAA;AAE7C,IAAA,IAAI,CAAC,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,EAAG;AAC7B,MAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,EAAe;AAC1C,QAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,MACvD;AACA,MAAA,MAAA,CAAO,IAAA;AAAA,QACL;AAAA,OACF;AACA,MAAA,OAAOA,uBAAA,EAAO;AAAA,IAChB;AAEA,IAAA,MAAM,IAAA,CAAK,gCAAA,CAAiC,MAAA,EAAQ,eAAe,CAAA;AAEnE,IAAA,MAAM,QAAQ,IAAA,CAAK,UAAA;AAAA,MACjB,MAAA;AAAA,MACA,eAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,OACE,YAAA,GAAe;AAAA,MACb,UAAA,EAAY,MACV,IAAA,CAAK,kBAAA;AAAA,QACH,eAAA;AAAA,QACA,eAAA;AAAA,QACA,OAAA;AAAA,QACA,KAAA;AAAA,QACA,WAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF;AAAA,MACF,eAAA;AAAA,MACA,eAAA;AAAA,MACA;AAAA,KACD,KACD,IAAA,CAAK,kBAAA;AAAA,MACH,eAAA;AAAA,MACA,eAAA;AAAA,MACA,OAAA;AAAA,MACA,KAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF;AAAA,EAEJ;AAAA,EAEQ,UAAA,CACN,MAAA,EACA,eAAA,EACA,SAAA,EACA,UACA,eAAA,EACiB;AACjB,IAAA,MAAM,YAAA,GAAe,IAAIC,iCAAA,CAAiB;AAAA,MACxC;AAAA,KACD,CAAA;AACD,IAAA,MAAM,qBAAA,GAAwB,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,iBAAA;AAAA,MAC5C;AAAA,KACF;AACA,IAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAQ,GAAIC,uDAAA,CAAmC;AAAA,MAC5D,KAAA,EAAO,qBAAA,EAAuB,iBAAA,CAAkB,kBAAkB,CAAA;AAAA,MAClE,OAAA,EAAS,qBAAA,EAAuB,iBAAA,CAAkB,SAAS;AAAA,KAC5D,CAAA;AAED,IAAA,OAAO,IAAIC,+BAAA,CAAgB;AAAA,MACzB,MAAA;AAAA,MACA,eAAA;AAAA,MACA,YAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,OAAA,EAAS,KAAK,GAAA,CAAI,OAAA;AAAA,MAClB,eAAA,EAAiB,EAAE,KAAA,EAAO,OAAA;AAAQ,KACnC,CAAA;AAAA,EACH;AAAA,EAEQ,mBACN,eAAA,EACA,eAAA,EACA,SACA,KAAA,EACA,aAAA,EACA,UACA,eAAA,EACgB;AAChB,IAAA,MAAM,MAAA,GAAS,KAAK,GAAA,CAAI,MAAA;AACxB,IAAA,MAAM,OAAA,GAAU,KAAK,GAAA,CAAI,OAAA;AACzB,IAAA,MAAM,SAASH,uBAAA,EAAO;AACtB,IAAA,MAAA,CAAO,IAAI,QAAA,EAAU,KAAA,CAAM,qBAAqB,EAAE,aAAA,EAAe,CAAC,CAAA;AAClE,IAAA,MAAA,CAAO,GAAA,CAAII,wBAAA,CAAQ,IAAA,EAAM,CAAA;AACzB,IAAA,MAAA,CAAO,GAAA;AAAA,MACLC,sDAAA,CAAkC;AAAA,QAChC,WAAA,EAAaC;AAAA,OACd;AAAA,KACH;AAGA,IAAA,MAAA,CAAO,IAAA,CAAK,sBAAA,EAAwB,OAAO,GAAA,EAAK,GAAA,KAAQ;AACtD,MAAA,MAAM,SAAA,GAAY,IAAI,MAAA,CAAO,SAAA;AAC7B,MAAA,MAAM,cAAsC,GAAA,CAAI,IAAA;AAChD,MAAA,IAAI,SAAA;AACJ,MAAA,IAAI,aAAa,MAAA,EAAQ;AACvB,QAAA,IAAI;AACF,UAAA,SAAA,GAAYC,+BAAA,CAAmB,YAAY,MAAM,CAAA;AAAA,QACnD,CAAA,CAAA,MAAQ;AACN,UAAA,SAAA,GAAY,MAAA;AAAA,QACd;AAAA,MACF;AAEA,MAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,WAAA,CAAY;AAAA,QAC7C,OAAA,EAAS,gBAAA;AAAA,QACT,OAAA,EAAS,GAAA;AAAA,QACT,IAAA,EAAM,EAAE,SAAA,EAAW,UAAA,EAAY,WAAW,SAAA;AAAU,OACrD,CAAA;AAED,MAAA,IAAI;AACF,QAAA,MAAMC,mCAAA;AAAA,UACJ,aAAA;AAAA,UACAC,wDAAA;AAAA,UACA,QAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAElD,QAAA,IAAI,iBAAiB,WAAA,EAAa,MAAA;AAClC,QAAA,IAAI,aAAa,MAAA,EAAQ;AACvB,UAAA,IAAI,CAAC,SAAA,EAAW;AACd,YAAA,MAAM,IAAIC,uBAAgB,0BAA0B,CAAA;AAAA,UACtD;AACA,UAAA,MAAM,SAAA,GAAYC,4BAAe,SAAS,CAAA;AAC1C,UAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,CAAQ,cAAA,CAAe,SAAA,EAAW;AAAA,YAC5D;AAAA,WACD,CAAA;AACD,UAAA,IAAI,CAAC,aAAA,EAAe;AAClB,YAAA,MAAM,IAAID,sBAAA,CAAgB,CAAA,kBAAA,EAAqB,SAAS,CAAA,CAAE,CAAA;AAAA,UAC5D;AACA,UAAA,cAAA,GAAiB,aAAA;AAAA,QACnB;AAEA,QAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,4BAAA;AAAA,UACrC;AAAA,YACE,MAAA,EAAQ,cAAA;AAAA,YACR,IAAA,EAAM,WAAA,EAAa,IAAA,IAAQ;AAAC,WAC9B;AAAA,UACA,EAAE,WAAA;AAAY,SAChB;AACA,QAAA,GAAA,CAAI,KAAK,QAAQ,CAAA;AACjB,QAAA,YAAA,CACG,SAAQ,CACR,KAAA;AAAA,UAAM,WACL,MAAA,CAAO,KAAA;AAAA,YACL,sDAAA;AAAA,YACA;AAAA;AACF,SACF;AAAA,MACJ,SAAS,CAAA,EAAG;AACV,QAAA,MAAM,GAAA,GAAM,aAAa,KAAA,GAAQ,CAAA,GAAI,IAAI,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAA;AACxD,QAAA,MAAM,YAAA,CAAa,IAAA,CAAK,EAAE,KAAA,EAAO,KAAK,CAAA;AACtC,QAAA,IAAI,aAAaA,sBAAA,EAAiB;AAChC,UAAA,MAAM,CAAA;AAAA,QACR;AACA,QAAA,MAAA,CAAO,KAAA;AAAA,UACL,CAAA,0CAAA,EAA6C,SAAS,CAAA,SAAA,EAAY,GAAG,CAAA;AAAA,SACvE;AACA,QAAA,IAAI,CAAC,IAAI,WAAA,EAAa;AACpB,UAAA,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,GAAA,CAAI,SAAS,CAAA;AAAA,QAC7C;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,GAAA,CAAI,WAAA,EAAa,OAAO,GAAA,EAAK,GAAA,KAAQ;AAC1C,MAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,WAAA,CAAY;AAAA,QAC7C,OAAA,EAAS,eAAA;AAAA,QACT,OAAA,EAAS,GAAA;AAAA,QACT,IAAA,EAAM,EAAE,SAAA,EAAW,MAAA;AAAO,OAC3B,CAAA;AAED,MAAA,IAAI;AACF,QAAA,MAAMF,mCAAA;AAAA,UACJ,aAAA;AAAA,UACAI,uDAAA;AAAA,UACA,QAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,WAAA,CAAY,GAAG,CAAA;AAClD,QAAA,MAAM,cAAA,GAAiB,MAAM,IAAA,CAAK,mBAAA,CAAoB,eAAA,EAAiB;AAAA,UACrE;AAAA,SACD,CAAA;AACD,QAAA,GAAA,CAAI,IAAA,CAAK;AAAA,UACP,KAAA,EAAO,cAAA,CAAe,GAAA,CAAI,CAAA,EAAA,KAAM;AAC9B,YAAA,MAAM,iBAAA,GACJ,EAAA,CAAG,YAAA,CAAaC,gEAAyC,CAAA;AAC3D,YAAA,MAAM,YAAA,GACJ,EAAA,CAAG,YAAA,CAAaC,0DAAmC,CAAA;AACrD,YAAA,MAAM,QAAA,GAAW,gBAAgB,YAAY,CAAA;AAC7C,YAAA,IAAI,OAAqB,EAAC;AAC1B,YAAA,IAAI,QAAA,EAAU;AACZ,cAAA,IAAA,GAAO,QAAA,CAAS,mBAAA,CAAoB,EAAA,CAAG,YAAY,CAAA;AAAA,YACrD;AAEA,YAAA,OAAO;AAAA,cACL,MAAM,EAAA,CAAG,IAAA;AAAA,cACT,OAAO,EAAA,CAAG,KAAA;AAAA,cACV,cAAc,EAAA,CAAG,YAAA;AAAA,cACjB,YAAA;AAAA,cACA,GAAI,iBAAA,IAAqB,EAAE,iBAAA,EAAkB;AAAA,cAC7C,GAAI,QAAQ,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,IAAK,EAAE,IAAA;AAAK,aACvD;AAAA,UACF,CAAC;AAAA,SACF,CAAA;AACD,QAAA,YAAA,CACG,SAAQ,CACR,KAAA;AAAA,UAAM,WACL,MAAA,CAAO,KAAA;AAAA,YACL,iDAAA;AAAA,YACA;AAAA;AACF,SACF;AAAA,MACJ,SAAS,CAAA,EAAG;AACV,QAAA,MAAM,GAAA,GAAM,aAAa,KAAA,GAAQ,CAAA,GAAI,IAAI,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAA;AACxD,QAAA,MAAM,YAAA,CAAa,IAAA,CAAK,EAAE,KAAA,EAAO,KAAK,CAAA;AACtC,QAAA,MAAM,CAAA;AAAA,MACR;AAAA,IACF,CAAC,CAAA;AAED,IAAAC,yCAAA;AAAA,MACE,MAAA;AAAA,MACA,OAAA;AAAA,MACA,eAAA;AAAA,MACA,QAAA;AAAA,MACA,aAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAc,mBAAA,CACZ,eAAA,EACA,OAAA,EACA;AACA,IAAA,MAAM,cAAA,GAAiB,MAAM,eAAA,CAAgB,WAAA,CAAY,OAAO,CAAA;AAEhE,IAAA,IAAA,CAAK,IAAI,MAAA,CAAO,KAAA;AAAA,MACd,CAAA,8CAAA,EAAiD,eAAe,MAAM,CAAA;AAAA,KACxE;AAEA,IAAA,OAAO,cAAA;AAAA,EACT;AAAA,EAEA,MAAc,gCAAA,CACZ,MAAA,EACA,eAAA,EACe;AACf,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,GAAc,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,wBAAA,EAAyB;AACjE,MAAA,MAAM,WAAW,MAAM,eAAA,CAAgB,WAAA,CAAY,EAAE,aAAa,CAAA;AAElE,MAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,QAAA,IAAI,QAAQ,aAAA,EAAe;AACzB,UAAA,MAAA,CAAO,IAAA;AAAA,YACL,CAAA,SAAA,EAAY,QAAQ,IAAI,CAAA,6HAAA;AAAA,WAC1B;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,CAAA,iDAAA,EACEC,cAAA,CAAQ,KAAK,CAAA,CAAE,OACjB,CAAA;AAAA,OACF;AAAA,IACF;AAAA,EACF;AACF;;;;"}
|
|
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 kubernetesResourcesReadPermission,\n} from '@backstage/plugin-kubernetes-common';\nimport { PermissionEvaluator } from '@backstage/plugin-permission-common';\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 { parseEntityRef, 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\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\n const credentials = await httpAuth.credentials(req);\n\n let resolvedEntity = requestBody?.entity;\n if (requestBody?.entity) {\n if (!entityRef) {\n throw new NotAllowedError('Invalid entity reference');\n }\n const parsedRef = parseEntityRef(entityRef);\n const catalogEntity = await catalog.getEntityByRef(parsedRef, {\n credentials,\n });\n if (!catalogEntity) {\n throw new NotAllowedError(`Entity not found, ${entityRef}`);\n }\n resolvedEntity = catalogEntity;\n }\n\n const response = await objectsProvider.getKubernetesObjectsByEntity(\n {\n entity: resolvedEntity,\n auth: requestBody?.auth || {},\n },\n { credentials },\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","stringifyEntityRef","requirePermission","kubernetesResourcesReadPermission","NotAllowedError","parseEntityRef","kubernetesClustersReadPermission","ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER","ANNOTATION_KUBERNETES_AUTH_PROVIDER","addResourceRoutesToRouter","toError"],"mappings":"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"}
|