@jfvilas/plugin-kwirth-backend 0.12.2 → 0.12.4

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/dist/index.cjs.js CHANGED
@@ -184,7 +184,7 @@ const loadClusters = async (logger, config) => {
184
184
  }
185
185
  }
186
186
  }
187
- logger.info("Kwirthbackstage static data has been set including following clusters:");
187
+ logger.info("Kwirth static data has been set including following clusters:");
188
188
  for (var c of KwirthStaticData.clusterKwirthData.keys()) {
189
189
  logger.info(" " + c);
190
190
  }
@@ -297,8 +297,8 @@ async function createRouter(options) {
297
297
  const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;
298
298
  loggerSvc.info("Loading static config");
299
299
  if (!configSvc.has("kubernetes.clusterLocatorMethods")) {
300
- loggerSvc.error(`Kwirthbackstage will not start, there is no 'clusterLocatorMethods' defined in app-config.`);
301
- throw new Error("Kwirthbackstage backend will not be available.");
300
+ loggerSvc.error(`Kwirth will not start, there is no 'clusterLocatorMethods' defined in app-config.`);
301
+ throw new Error("Kwirth backend will not be available.");
302
302
  }
303
303
  try {
304
304
  loadClusters(loggerSvc, configSvc);
@@ -310,14 +310,14 @@ async function createRouter(options) {
310
310
  if (configSvc.subscribe) {
311
311
  configSvc.subscribe(() => {
312
312
  try {
313
- loggerSvc.warn("Change detected on app-config, Kwirthbackstage will update config.");
313
+ loggerSvc.warn("Change detected on app-config, Kwirth will update config.");
314
314
  loadClusters(loggerSvc, configSvc);
315
315
  } catch (err) {
316
316
  loggerSvc.error(`Errors detected reading new configuration: ${err}`);
317
317
  }
318
318
  });
319
319
  } else {
320
- loggerSvc.info("Kwirthbackstage cannot subscribe to config changes.");
320
+ loggerSvc.info("Kwirth cannot subscribe to config changes.");
321
321
  }
322
322
  const router = Router__default.default();
323
323
  router.use(express__default.default.json());
@@ -379,8 +379,6 @@ async function createRouter(options) {
379
379
  var fetchResp = await fetch(kwirthHome + "/key", { method: "POST", body: JSON.stringify(payload), headers: { "Content-Type": "application/json", Authorization: "Bearer " + kwirthApiKey } });
380
380
  if (fetchResp.status === 200) {
381
381
  var data = await fetchResp.json();
382
- console.log("data");
383
- console.log(data);
384
382
  return data.accessKey;
385
383
  } else {
386
384
  loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`);
@@ -413,8 +411,6 @@ async function createRouter(options) {
413
411
  }
414
412
  if (podList.length > 0) {
415
413
  let accessKey = await createAccessKey(reqScope, foundCluster, podList, username);
416
- console.log("accessKey");
417
- console.log(accessKey);
418
414
  foundCluster.accessKeys.set(reqScope, accessKey);
419
415
  } else {
420
416
  console.log(`No pods on podList for ${channel} and ${reqScope}`);
@@ -469,7 +465,7 @@ async function createRouter(options) {
469
465
  }
470
466
 
471
467
  const kwirthPlugin = backendPluginApi.createBackendPlugin({
472
- pluginId: "kwirthbackstage",
468
+ pluginId: "kwirth",
473
469
  register(env) {
474
470
  env.registerInit({
475
471
  deps: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/model/KwirthStaticData.ts","../src/service/config.ts","../src/service/permissions.ts","../src/service/router.ts","../src/plugin.ts"],"sourcesContent":["import { KwirthClusterData } from './KwirthClusterData'\r\n\r\n/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nconst VERSION='0.0.1'\r\nconst MIN_KWIRTH_VERSION='0.3.128'\r\n\r\nclass KwirthStaticData {\r\n public static clusterKwirthData : Map<string,KwirthClusterData> = new Map()\r\n}\r\n\r\nexport { KwirthStaticData, VERSION, MIN_KWIRTH_VERSION }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\")\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { LoggerService, RootConfigService } from '@backstage/backend-plugin-api'\r\nimport { KwirthStaticData, MIN_KWIRTH_VERSION } from '../model/KwirthStaticData'\r\nimport { KwirthClusterData, KwirthNamespacePermissions, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { Config } from '@backstage/config'\r\nimport { KwirthData, versionGreatOrEqualThan } from '@jfvilas/kwirth-common'\r\n\r\n\r\n/**\r\n * loads Namespace Permissions setting from app-config xml\r\n * @param block name of the Kwirthbackstage block te read ('chart', 'log', 'audit'...)\r\n * @param logger Logger service\r\n */\r\nconst loadNamespacePermissions = (block:Config, logger:LoggerService):KwirthNamespacePermissions[] => {\r\n var namespacePermissions:KwirthNamespacePermissions[] = []\r\n if (block.has('namespacePermissions')) {\r\n logger.info(` Namespace permisson evaluation will be performed.`)\r\n var permNamespaces= (block.getOptionalConfigArray('namespacePermissions'))!\r\n for (var ns of permNamespaces) {\r\n var namespace=ns.keys()[0]\r\n var identityRefs=ns.getStringArray(namespace)\r\n identityRefs=identityRefs.map(g => g.toLowerCase())\r\n namespacePermissions.push ({ namespace, identityRefs })\r\n }\r\n }\r\n else {\r\n logger.info(` No namespace restrictions.`)\r\n namespacePermissions=[]\r\n }\r\n return namespacePermissions\r\n}\r\n\r\n/**\r\n * read rules about permissions to a pply to a set of pods\r\n * @param config an object config read from app-config\r\n * @param category a permission category, one of: allow, deny unless, except\r\n * @returns an array of PodPermissionRule's\r\n */\r\nconst loadPodRules = (config:Config, category:string):PodPermissionRule[] => {\r\n var rules:PodPermissionRule[]=[]\r\n for (var rule of config.getConfigArray(category)) {\r\n var podsStringArray = rule.getOptionalStringArray('pods') || ['.*']\r\n var podsRegexArray:RegExp[]=[]\r\n for (var expr of podsStringArray) {\r\n podsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n var refsStringArray = rule.getOptionalStringArray('refs') || ['.*']\r\n var refsRegexArray:RegExp[]=[]\r\n for (var expr of refsStringArray) {\r\n refsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n var prr:PodPermissionRule={\r\n pods:podsRegexArray,\r\n refs:refsRegexArray\r\n }\r\n rules.push(prr)\r\n }\r\n return rules\r\n}\r\n\r\n/**\r\n * loads pod permissions (namespace and pod) for a specific Kwirthbackstage block\r\n * @param block then name of the key (inside app-config) to read config from\r\n * @param logger Logger service\r\n * @returns an array of pod permissions\r\n */\r\nconst loadPodPermissions = (block:Config, logger:LoggerService):KwirthPodPermissions[] => {\r\n var clusterPodPermissions:KwirthPodPermissions[]=[]\r\n if (block.has('podPermissions')) {\r\n var namespaceList=block.getConfigArray('podPermissions')\r\n for (var ns of namespaceList) {\r\n var namespaceName=ns.keys()[0]\r\n var podPermissions:KwirthPodPermissions={ namespace:namespaceName }\r\n\r\n if (ns.getConfig(namespaceName).has('allow')) {\r\n podPermissions.allow=loadPodRules(ns.getConfig(namespaceName), 'allow')\r\n if (ns.getConfig(namespaceName).has('except')) podPermissions.except=loadPodRules(ns.getConfig(namespaceName), 'except')\r\n if (ns.getConfig(namespaceName).has('deny')) podPermissions.deny=loadPodRules(ns.getConfig(namespaceName), 'deny')\r\n if (ns.getConfig(namespaceName).has('unless')) podPermissions.unless=loadPodRules(ns.getConfig(namespaceName), 'unless')\r\n }\r\n else {\r\n podPermissions.allow=[]\r\n podPermissions.allow.push({\r\n pods: [new RegExp('.*')],\r\n refs: [new RegExp('.*')]\r\n })\r\n }\r\n clusterPodPermissions.push(podPermissions)\r\n }\r\n }\r\n else {\r\n logger.info(` No pod permissions will be applied.`)\r\n }\r\n return clusterPodPermissions\r\n}\r\n\r\n/**\r\n * Reads permissions for a kwirthbackstage block (like log, metrics...)\r\n * @param channel name of the block\r\n * @param logger bs logger service\r\n * @param cluster the app-config object containing the cluster to process\r\n * @param kdata current KwirthbackstageClusterData object to add block permissions\r\n */\r\nconst addChannelPermissions = (channel: string, logger:LoggerService, cluster:Config, kdata:KwirthClusterData) => {\r\n var keyName = 'kwirth'+channel\r\n if (cluster.has(keyName)) {\r\n logger.info(`Load permissions for block ${channel}.`)\r\n var configBlock=cluster.getConfig(keyName);\r\n if (configBlock.has('namespacePermissions')) {\r\n logger.info(` Loading namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, loadNamespacePermissions(configBlock, logger))\r\n }\r\n else {\r\n logger.info(` No namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, [])\r\n }\r\n if (configBlock.has('podPermissions')) {\r\n logger.info(` Loading pod permissions.`)\r\n kdata.podPermissions.set(channel, loadPodPermissions(configBlock, logger))\r\n }\r\n else {\r\n logger.info(` No pod permissions.`)\r\n kdata.podPermissions.set(channel, [])\r\n }\r\n }\r\n else {\r\n logger.info(`Cluster ${cluster.getString('name')} will have no channel '${channel}' restrictions.`)\r\n kdata.namespacePermissions.set(channel,[])\r\n kdata.podPermissions.set(channel,[])\r\n }\r\n}\r\n\r\n/**\r\n * reads app-config and builds a list of valid clusters\r\n * @param logger core service for logging\r\n * @param config core service for reading config info\r\n */\r\nconst loadClusters = async (logger:LoggerService, config:RootConfigService) => {\r\n KwirthStaticData.clusterKwirthData.clear()\r\n\r\n var locatingMethods=config.getConfigArray('kubernetes.clusterLocatorMethods')\r\n for (var method of locatingMethods) {\r\n\r\n var clusters=(method.getConfigArray('clusters'))\r\n for (var cluster of clusters) {\r\n\r\n var name=cluster.getString('name')\r\n if (cluster.has('kwirthHome') && cluster.has('kwirthApiKey')) { \r\n var kwirthHome:string = cluster.getOptionalString('kwirthHome')!\r\n var kwirthApiKey:string = cluster.getOptionalString('kwirthApiKey')!\r\n var title:string = (cluster.has('title')?cluster.getString('title'):'No name')\r\n var kwirthClusterData:KwirthClusterData={\r\n name,\r\n kwirthHome,\r\n kwirthApiKey,\r\n kwirthData: {\r\n version: '',\r\n clusterName: '',\r\n inCluster: false,\r\n namespace: '',\r\n deployment: '',\r\n lastVersion: ''\r\n },\r\n title,\r\n namespacePermissions: new Map(),\r\n podPermissions: new Map(),\r\n enabled: false\r\n }\r\n\r\n logger.info(`Kwirth for ${name} is located at ${kwirthClusterData.kwirthHome}. Testing connection...`)\r\n let enableCluster = false\r\n try {\r\n /*\r\n /config/version endpoint returns JSON (KwirthData object):\r\n {\r\n \"clusterName\": \"inCluster\",\r\n \"namespace\": \"default\",\r\n \"deployment\": \"kwirth\",\r\n \"inCluster\": true,\r\n \"version\": \"0.2.213\",\r\n \"lastVersion\": \"0.2.213\"\r\n }\r\n */\r\n var response = await fetch (kwirthClusterData.kwirthHome+'/config/version')\r\n try {\r\n var data = await response.text()\r\n try {\r\n var kwirthData=JSON.parse(data) as KwirthData\r\n logger.info(`Kwirth info at cluster '${kwirthClusterData.name}': ${JSON.stringify(kwirthData)}`)\r\n kwirthClusterData.kwirthData=kwirthData\r\n if (versionGreatOrEqualThan(kwirthData.version, MIN_KWIRTH_VERSION)) {\r\n enableCluster = true\r\n }\r\n else {\r\n logger.error(`Unsupported Kwirth version on cluster '${name}' (${title}) [${kwirthData.version}]. Min version is ${MIN_KWIRTH_VERSION}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.error(`Kwirth at cluster ${kwirthClusterData.name} returned errors: ${err}`)\r\n logger.info('Returned data is:')\r\n logger.info(data)\r\n kwirthClusterData.kwirthData = {\r\n version:'0.0.0',\r\n clusterName:'unknown',\r\n inCluster:false,\r\n namespace:'unknown',\r\n deployment:'unknown',\r\n lastVersion:'0.0.0'\r\n }\r\n }\r\n }\r\n catch (err) {\r\n logger.warn(`Error parsing version response from cluster '${kwirthClusterData.name}': ${err}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.info(`Kwirth access error: ${err}.`)\r\n logger.warn(`Kwirth home URL (${kwirthClusterData.kwirthHome}) at cluster '${kwirthClusterData.name}' cannot be accessed right now.`)\r\n }\r\n\r\n if (enableCluster) {\r\n addChannelPermissions('log',logger, cluster, kwirthClusterData)\r\n addChannelPermissions('alert',logger, cluster, kwirthClusterData)\r\n addChannelPermissions('metrics',logger, cluster, kwirthClusterData)\r\n KwirthStaticData.clusterKwirthData.set(name, kwirthClusterData)\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} will be disabled`)\r\n }\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} has no Kwirth information (kwirthHome and kwirthApiKey are missing).`)\r\n }\r\n }\r\n }\r\n\r\n logger.info('Kwirthbackstage static data has been set including following clusters:')\r\n for (var c of KwirthStaticData.clusterKwirthData.keys()) {\r\n logger.info (' '+c)\r\n }\r\n for (var c of KwirthStaticData.clusterKwirthData.keys()) {\r\n console.log(KwirthStaticData.clusterKwirthData.get(c))\r\n }\r\n\r\n}\r\n\r\nexport { loadClusters }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { ClusterValidPods, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { KwirthClusterData, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { KwirthStaticData } from '../model/KwirthStaticData';\r\n//import { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\nconst checkNamespaceAccess = (channel:string, cluster:ClusterValidPods, podData:PodData, userEntityRef:string, userGroups:string[]) : boolean => {\r\n let allowedToNamespace=false\r\n let namespacePermissions = KwirthStaticData.clusterKwirthData.get(cluster.name)?.namespacePermissions\r\n\r\n if (namespacePermissions?.has(channel)) {\r\n let rule = namespacePermissions?.get(channel)!.find(ns => ns.namespace===podData.namespace)\r\n if (rule) {\r\n if (rule.identityRefs.includes(userEntityRef.toLowerCase())) {\r\n // a user ref has been found on a namespace rule\r\n allowedToNamespace=true\r\n }\r\n else {\r\n var groupResult=rule.identityRefs.some(identityRef => userGroups.includes(identityRef));\r\n if (groupResult) {\r\n // a group ref match has been found\r\n allowedToNamespace=true\r\n }\r\n }\r\n }\r\n else {\r\n // no restrictions for this namespace\r\n allowedToNamespace=true\r\n }\r\n }\r\n else {\r\n console.log(`Invalid channel: ${channel}`)\r\n }\r\n return allowedToNamespace\r\n}\r\n\r\nconst checkPodPermissionRule = (ppr:PodPermissionRule, entityName:string, userEntityRef:string, userGroups:string[]) : boolean => {\r\n var refMatch:boolean=false;\r\n\r\n for (var podNameRegex of ppr.pods) {\r\n if (podNameRegex.test(entityName)) {\r\n for (var refRegex of ppr.refs) {\r\n // find userRef\r\n refMatch=refRegex.test(userEntityRef.toLowerCase())\r\n if (refMatch) {\r\n break;\r\n }\r\n else {\r\n // find group ref\r\n refMatch = userGroups.some(g => refRegex.test(g))\r\n if (refMatch) {\r\n break\r\n }\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n if (refMatch) break\r\n }\r\n return refMatch\r\n}\r\n\r\n// const getPodPermissionSet = (reqScope:InstanceConfigScopeEnum, cluster:KwirthClusterData) => {\r\n// switch (reqScope) {\r\n// // case InstanceConfigScopeEnum.FILTER:\r\n// case InstanceConfigScopeEnum.SNAPSHOT:\r\n// case InstanceConfigScopeEnum.STREAM:\r\n// console.log(cluster.podPermissions)\r\n// return cluster.podPermissions.get(reqScope)\r\n// default:\r\n// console.log(`Invalid scope ${reqScope} for permission set`)\r\n// }\r\n// return undefined\r\n// }\r\nconst getPodPermissionSet = (channel:string, cluster:KwirthClusterData) => {\r\n if (cluster.podPermissions.has(channel)) {\r\n return cluster.podPermissions.get(channel)\r\n }\r\n else {\r\n console.log(`Invalid channel ${channel} for permission set`)\r\n return undefined\r\n }\r\n}\r\n\r\n/**\r\n * This funciton checks permissions according to app-config rules (not kwirth rules), that is, namespace rules,\r\n * viewing rules and restarting rules\r\n * @param loggerSvc Backstage logger\r\n * @param reqCluster the cluster the pod belongs to\r\n * @param reqPod data about the pod the user wants to access\r\n * @param podPermissionSet a set of permission for the cluster (extracted from app-config)\r\n * @param entityName the name of the entity to search for\r\n * @param userEntityRef the canonical identity reference for the user ('type:namespace/ref')\r\n * @param userGroups ana array containing a list of groups the user belongs to\r\n * @returns booelan indicating if the user can access the pod for doing what scaope says (view or restart)\r\n */\r\n\r\n// const checkPodAccess = (loggerSvc:LoggerService, reqCluster:ClusterValidPods, reqPod:PodData, podPermissionSet:KubelogPodPermissions[], entityName:string, userEntityRef:string, userGroups:string[]):boolean => {\r\n// var cluster = KubelogStaticData.clusterKubelogData.get(reqCluster.name)\r\n\r\n// if (!cluster) {\r\n// loggerSvc.warn(`Invalid cluster specified ${reqCluster.name}`)\r\n// return false\r\n// }\r\n\r\nconst checkPodAccess = (reqPod:PodData, podPermissionSet:KwirthPodPermissions[], entityName:string, userEntityRef:string, userGroups:string[]):boolean => {\r\n // we check all pod permissions until one of them evaluates to true (must be true on allow/except and false on deny/unless)\r\n\r\n // we use 'filter' here beacause a namespace can be specified more than once\r\n for (var podPermission of podPermissionSet.filter(pp => pp.namespace===reqPod.namespace)) {\r\n if (podPermission.allow) {\r\n \r\n // **** evaluate allow/except rules ****\r\n var allowMatches=false;\r\n var exceptMatches=false;\r\n // we test all allow rules, we stop if one matches\r\n for (var prr of podPermission.allow) {\r\n allowMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);\r\n }\r\n if (allowMatches) {\r\n if (podPermission.except) {\r\n // we test all except rules, will stop if found one that matches, no need to continue\r\n for (var prr of podPermission.except) {\r\n //exceptMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups);\r\n exceptMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n // if there is a exception the process finishes now for this podPermission)\r\n if (exceptMatches) {\r\n break\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n }\r\n\r\n if (allowMatches && !exceptMatches) {\r\n // **** evaluate deny/unless rules ****\r\n if (podPermission.deny) {\r\n var denyMatches=false\r\n var unlessMatches=false\r\n for (var prr of podPermission.deny) {\r\n //denyMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n denyMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (denyMatches) {\r\n break;\r\n }\r\n }\r\n if (denyMatches && podPermission.unless) {\r\n for (var prr of podPermission.unless) {\r\n //unlessMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n unlessMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (unlessMatches) {\r\n break;\r\n }\r\n }\r\n }\r\n if (!denyMatches || (denyMatches && unlessMatches)) {\r\n return true\r\n }\r\n }\r\n else {\r\n return true\r\n }\r\n }\r\n else {\r\n // do nothing, just continue podpermissionset loop\r\n }\r\n }\r\n else {\r\n // if no 'allow' is specified everybody has access\r\n // we continue loop checking other namespaces\r\n return true\r\n }\r\n }\r\n \r\n return false\r\n}\r\n\r\nexport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport express from 'express'\r\nimport Router from 'express-promise-router'\r\nimport { AuthService, BackstageUserInfo, DiscoveryService, HttpAuthService, LoggerService, RootConfigService, UserInfoService } from '@backstage/backend-plugin-api'\r\nimport { CatalogClient } from '@backstage/catalog-client'\r\nimport { UserEntity } from '@backstage/catalog-model'\r\nimport { FetchApi } from '@backstage/core-plugin-api'\r\n\r\n// Kwirthbackstage\r\nimport { ClusterValidPods, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { loadClusters } from './config'\r\nimport { KwirthStaticData, VERSION } from '../model/KwirthStaticData'\r\nimport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet } from './permissions'\r\nimport { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\nexport type KwirthRouterOptions = {\r\n discoverySvc: DiscoveryService\r\n configSvc: RootConfigService\r\n loggerSvc: LoggerService\r\n userInfoSvc: UserInfoService\r\n authSvc: AuthService\r\n httpAuthSvc: HttpAuthService\r\n}\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\n/**\r\n * \r\n * @param options core services we need for Kwirthbackstage to work\r\n * @returns an express Router\r\n */\r\nasync function createRouter(options: KwirthRouterOptions) : Promise<express.Router> {\r\n const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;\r\n\r\n loggerSvc.info('Loading static config')\r\n\r\n if (!configSvc.has('kubernetes.clusterLocatorMethods')) {\r\n loggerSvc.error(`Kwirthbackstage will not start, there is no 'clusterLocatorMethods' defined in app-config.`)\r\n throw new Error('Kwirthbackstage backend will not be available.')\r\n }\r\n\r\n try {\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch (err) {\r\n var txt=`Errors detected reading static configuration: ${err}`\r\n loggerSvc.error(txt)\r\n throw new Error(txt)\r\n }\r\n\r\n // subscribe to changes on app-config\r\n if (configSvc.subscribe) {\r\n configSvc.subscribe( () => {\r\n try {\r\n loggerSvc.warn('Change detected on app-config, Kwirthbackstage will update config.')\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch(err) {\r\n loggerSvc.error(`Errors detected reading new configuration: ${err}`)\r\n }\r\n })\r\n }\r\n else {\r\n loggerSvc.info('Kwirthbackstage cannot subscribe to config changes.')\r\n }\r\n\r\n const router = Router()\r\n\r\n router.use(express.json())\r\n\r\n // we need this function to be able to invoke another backend plugin passing an authorization token\r\n const createAuthFetchApi = (token: string) : FetchApi => {\r\n return {\r\n fetch: async (input, init) => {\r\n init = init || {}\r\n init.headers = {\r\n ...init.headers,\r\n Authorization: `Bearer ${token}`,\r\n }\r\n return fetch(input, init)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Invokes Kwirth to obtain a list of pods that are tagged with the kubernetes-id of the entity we are looking for.\r\n * @param entityName name of the tagge dentity\r\n * @returns a ClusterValidPods[] (each ClusterValidPods is a cluster info with a list of pods tagged with the entityName).\r\n */\r\n const getValidClusters = async (entityName:string) : Promise<ClusterValidPods[]> => {\r\n var clusterList:ClusterValidPods[]=[]\r\n\r\n for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {\r\n var url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome as string\r\n var apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey\r\n var title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title\r\n var queryUrl=url+`/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entityName}&type=pod&data=containers`\r\n try {\r\n var fetchResp = await fetch (queryUrl, {headers:{'Authorization':'Bearer '+apiKeyStr}})\r\n if (fetchResp.status===200) {\r\n var jsonResp=await fetchResp.json()\r\n if (jsonResp) {\r\n let podData:ClusterValidPods = {\r\n name: clusterName, url, title, data: jsonResp, accessKeys: new Map()\r\n }\r\n clusterList.push(podData)\r\n }\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`)\r\n console.log(await fetchResp.text())\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n\r\n }\r\n catch (err) {\r\n loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`)\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n }\r\n\r\n return clusterList\r\n }\r\n\r\n const createAccessKey = async (reqScope:InstanceConfigScopeEnum, cluster:ClusterValidPods, reqPods:PodData[], userName:string) : Promise<any> => {\r\n var resources = reqPods.map(podData => `${reqScope}:${podData.namespace}::${podData.name}:`).join(',')\r\n\r\n var kwirthHome = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthHome as string\r\n var kwirthApiKey = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthApiKey\r\n var payload={\r\n type:'bearer',\r\n resource: resources,\r\n description:`Backstage API key for user ${userName}`,\r\n expire:Date.now()+60*60*1000\r\n }\r\n var fetchResp=await fetch(kwirthHome+'/key',{method:'POST', body:JSON.stringify(payload), headers:{'Content-Type':'application/json', Authorization:'Bearer '+kwirthApiKey}})\r\n if (fetchResp.status===200) {\r\n var data = await fetchResp.json();\r\n console.log('data')\r\n console.log(data)\r\n return data.accessKey\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`)\r\n return {}\r\n }\r\n }\r\n\r\n const addAccessKeys = async (channel:string, reqScope:InstanceConfigScopeEnum, foundClusters:ClusterValidPods[], entityName:string, userEntityRef:string, userGroups:string[]) => {\r\n //var reqScope:InstanceConfigScopeEnum = reqScopeStr as InstanceConfigScopeEnum\r\n if (!reqScope) {\r\n loggerSvc.info(`Invalid scope requested: ${reqScope}`)\r\n return\r\n }\r\n var principal=userEntityRef.split(':')[1]\r\n var username=principal.split('/')[1]\r\n\r\n for (var foundCluster of foundClusters) {\r\n var podList:PodData[]=[]\r\n\r\n // for each pod we've found on the cluster we check all namespace permissions\r\n\r\n for (var podData of foundCluster.data) {\r\n // first we check if user is allowed to acccess namespace\r\n var allowedToNamespace = checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups)\r\n\r\n if (allowedToNamespace) {\r\n // then we check if required pod namespace has pod access restriccions for requested namespace\r\n var clusterDef = KwirthStaticData.clusterKwirthData.get(foundCluster.name)\r\n var podPermissionSet = getPodPermissionSet(channel, clusterDef!)\r\n if (!podPermissionSet) {\r\n loggerSvc.warn(`Pod permission set not found: ${channel}`)\r\n continue\r\n }\r\n var namespaceRestricted = podPermissionSet.some(pp => pp.namespace===podData.namespace);\r\n if (!namespaceRestricted || checkPodAccess(podData, podPermissionSet, entityName, userEntityRef, userGroups)) {\r\n // there are no namespace restrictions specified in the pod permission set\r\n podList.push(podData)\r\n }\r\n else {\r\n }\r\n }\r\n else {\r\n // user is not allowed to namespace, so we don't need to check pod permissions\r\n // the loop cotinues with other pods\r\n }\r\n }\r\n if (podList.length>0) {\r\n let accessKey = await createAccessKey(reqScope, foundCluster, podList, username)\r\n console.log('accessKey')\r\n console.log(accessKey)\r\n foundCluster.accessKeys.set(reqScope, accessKey)\r\n }\r\n else {\r\n console.log(`No pods on podList for ${channel} and ${reqScope}`)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * builds a list of groups (expressed as identity refs) that the user belongs to.\r\n * @param userInfo Backstage user info of the user to search groups for\r\n * @returns an array of group refs in canonical form\r\n */\r\n const getUserGroups = async (userInfo:BackstageUserInfo) : Promise<string[]> => {\r\n const { token } = await authSvc.getPluginRequestToken({\r\n onBehalfOf: await authSvc.getOwnServiceCredentials(),\r\n targetPluginId: 'catalog'\r\n });\r\n const catalogClient = new CatalogClient({\r\n discoveryApi: discoverySvc,\r\n fetchApi: createAuthFetchApi(token),\r\n });\r\n\r\n const entity = await catalogClient.getEntityByRef(userInfo.userEntityRef) as UserEntity\r\n var userGroupsRefs:string[]=[]\r\n //+++ future use: recursive memberOf\r\n if (entity?.spec.memberOf) userGroupsRefs=entity?.spec.memberOf\r\n return userGroupsRefs\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processVersion = async (_req:any, res:any) => {\r\n res.status(200).send({ version:VERSION })\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processAccess = async (req:express.Request, res:express.Response) => {\r\n if (!req.query['scopes'] || !req.query['scopes']) {\r\n res.status(400).send(`'scopes' and 'channel' are required`)\r\n return\r\n }\r\n let reqScopes = (req.query['scopes'].toString()).split(',')\r\n let reqChannel = req.query['channel']?.toString()!\r\n \r\n // obtain basic user info\r\n const credentials = await httpAuthSvc.credentials(req, { allow: ['user'] })\r\n const userInfo = await userInfoSvc.getUserInfo(credentials)\r\n // get user groups list\r\n let userGroupsRefs=await getUserGroups(userInfo)\r\n\r\n loggerSvc.info(`Checking reqScopes '${req.query['scopes']}' scopes to pod: '${req.body.metadata.namespace+'/'+req.body.metadata.name}' for user '${userInfo.userEntityRef}'`)\r\n\r\n // get a list of clusters that contain pods related to entity\r\n //+++ control errors here (maybe we cannot conntact the cluster, for example)\r\n let foundClusters:ClusterValidPods[]=await getValidClusters(req.body.metadata.name)\r\n\r\n // add access keys to authorized resources (according to group membership and Kwirthbackstage config in app-config (namespace and pod permissions))\r\n for (var reqScopeStr of reqScopes) {\r\n var reqScope = reqScopeStr as InstanceConfigScopeEnum\r\n await addAccessKeys(reqChannel, reqScope, foundClusters, req.body.metadata.name, userInfo.userEntityRef, userGroupsRefs)\r\n }\r\n \r\n // we build a stringn of arrays from the Map /that can not be serialized)\r\n for (var c of foundClusters) {\r\n (c as any).accessKeys = JSON.stringify(Array.from(c.accessKeys.entries()))\r\n }\r\n res.status(200).send(foundClusters)\r\n }\r\n\r\n router.post(['/access'], (req, res) => {\r\n processAccess(req,res)\r\n })\r\n\r\n router.get(['/version'], (req, res) => {\r\n processVersion(req,res)\r\n })\r\n\r\n return router\r\n}\r\n\r\nexport { createRouter }\r\n","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'\r\nimport { createRouter } from './service/router'\r\n\r\nexport const kwirthPlugin = createBackendPlugin({\r\n pluginId: 'kwirthbackstage',\r\n register(env) {\r\n env.registerInit({\r\n deps: {\r\n discovery: coreServices.discovery,\r\n config: coreServices.rootConfig,\r\n logger: coreServices.logger,\r\n auth: coreServices.auth,\r\n httpAuth: coreServices.httpAuth,\r\n httpRouter: coreServices.httpRouter,\r\n userInfo: coreServices.userInfo\r\n },\r\n async init({ discovery, config, httpRouter, logger, auth, httpAuth, userInfo }) {\r\n httpRouter.use(\r\n await createRouter({\r\n discoverySvc: discovery,\r\n configSvc: config,\r\n loggerSvc: logger,\r\n authSvc: auth,\r\n httpAuthSvc: httpAuth,\r\n userInfoSvc: userInfo\r\n })\r\n )\r\n }\r\n })\r\n }\r\n})\r\n"],"names":["versionGreatOrEqualThan","Router","express","catalogClient","CatalogClient","createBackendPlugin","coreServices"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/model/KwirthStaticData.ts","../src/service/config.ts","../src/service/permissions.ts","../src/service/router.ts","../src/plugin.ts"],"sourcesContent":["import { KwirthClusterData } from './KwirthClusterData'\r\n\r\n/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nconst VERSION='0.0.1'\r\nconst MIN_KWIRTH_VERSION='0.3.128'\r\n\r\nclass KwirthStaticData {\r\n public static clusterKwirthData : Map<string,KwirthClusterData> = new Map()\r\n}\r\n\r\nexport { KwirthStaticData, VERSION, MIN_KWIRTH_VERSION }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\")\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { LoggerService, RootConfigService } from '@backstage/backend-plugin-api'\r\nimport { KwirthStaticData, MIN_KWIRTH_VERSION } from '../model/KwirthStaticData'\r\nimport { KwirthClusterData, KwirthNamespacePermissions, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { Config } from '@backstage/config'\r\nimport { KwirthData, versionGreatOrEqualThan } from '@jfvilas/kwirth-common'\r\n\r\n\r\n/**\r\n * loads Namespace Permissions setting from app-config xml\r\n * @param block name of the Kwirth block te read ('chart', 'log', 'audit'...)\r\n * @param logger Logger service\r\n */\r\nconst loadNamespacePermissions = (block:Config, logger:LoggerService):KwirthNamespacePermissions[] => {\r\n var namespacePermissions:KwirthNamespacePermissions[] = []\r\n if (block.has('namespacePermissions')) {\r\n logger.info(` Namespace permisson evaluation will be performed.`)\r\n var permNamespaces= (block.getOptionalConfigArray('namespacePermissions'))!\r\n for (var ns of permNamespaces) {\r\n var namespace=ns.keys()[0]\r\n var identityRefs=ns.getStringArray(namespace)\r\n identityRefs=identityRefs.map(g => g.toLowerCase())\r\n namespacePermissions.push ({ namespace, identityRefs })\r\n }\r\n }\r\n else {\r\n logger.info(` No namespace restrictions.`)\r\n namespacePermissions=[]\r\n }\r\n return namespacePermissions\r\n}\r\n\r\n/**\r\n * read rules about permissions to a pply to a set of pods\r\n * @param config an object config read from app-config\r\n * @param category a permission category, one of: allow, deny unless, except\r\n * @returns an array of PodPermissionRule's\r\n */\r\nconst loadPodRules = (config:Config, category:string):PodPermissionRule[] => {\r\n var rules:PodPermissionRule[]=[]\r\n for (var rule of config.getConfigArray(category)) {\r\n var podsStringArray = rule.getOptionalStringArray('pods') || ['.*']\r\n var podsRegexArray:RegExp[]=[]\r\n for (var expr of podsStringArray) {\r\n podsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n var refsStringArray = rule.getOptionalStringArray('refs') || ['.*']\r\n var refsRegexArray:RegExp[]=[]\r\n for (var expr of refsStringArray) {\r\n refsRegexArray.push(new RegExp(expr))\r\n }\r\n\r\n var prr:PodPermissionRule={\r\n pods:podsRegexArray,\r\n refs:refsRegexArray\r\n }\r\n rules.push(prr)\r\n }\r\n return rules\r\n}\r\n\r\n/**\r\n * loads pod permissions (namespace and pod) for a specific Kwirth block\r\n * @param block then name of the key (inside app-config) to read config from\r\n * @param logger Logger service\r\n * @returns an array of pod permissions\r\n */\r\nconst loadPodPermissions = (block:Config, logger:LoggerService):KwirthPodPermissions[] => {\r\n var clusterPodPermissions:KwirthPodPermissions[]=[]\r\n if (block.has('podPermissions')) {\r\n var namespaceList=block.getConfigArray('podPermissions')\r\n for (var ns of namespaceList) {\r\n var namespaceName=ns.keys()[0]\r\n var podPermissions:KwirthPodPermissions={ namespace:namespaceName }\r\n\r\n if (ns.getConfig(namespaceName).has('allow')) {\r\n podPermissions.allow=loadPodRules(ns.getConfig(namespaceName), 'allow')\r\n if (ns.getConfig(namespaceName).has('except')) podPermissions.except=loadPodRules(ns.getConfig(namespaceName), 'except')\r\n if (ns.getConfig(namespaceName).has('deny')) podPermissions.deny=loadPodRules(ns.getConfig(namespaceName), 'deny')\r\n if (ns.getConfig(namespaceName).has('unless')) podPermissions.unless=loadPodRules(ns.getConfig(namespaceName), 'unless')\r\n }\r\n else {\r\n podPermissions.allow=[]\r\n podPermissions.allow.push({\r\n pods: [new RegExp('.*')],\r\n refs: [new RegExp('.*')]\r\n })\r\n }\r\n clusterPodPermissions.push(podPermissions)\r\n }\r\n }\r\n else {\r\n logger.info(` No pod permissions will be applied.`)\r\n }\r\n return clusterPodPermissions\r\n}\r\n\r\n/**\r\n * Reads permissions for a kwirth block (like log, metrics...)\r\n * @param channel name of the block\r\n * @param logger bs logger service\r\n * @param cluster the app-config object containing the cluster to process\r\n * @param kdata current KwirthClusterData object to add block permissions\r\n */\r\nconst addChannelPermissions = (channel: string, logger:LoggerService, cluster:Config, kdata:KwirthClusterData) => {\r\n var keyName = 'kwirth'+channel\r\n if (cluster.has(keyName)) {\r\n logger.info(`Load permissions for block ${channel}.`)\r\n var configBlock=cluster.getConfig(keyName);\r\n if (configBlock.has('namespacePermissions')) {\r\n logger.info(` Loading namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, loadNamespacePermissions(configBlock, logger))\r\n }\r\n else {\r\n logger.info(` No namespace permissions.`)\r\n kdata.namespacePermissions.set(channel, [])\r\n }\r\n if (configBlock.has('podPermissions')) {\r\n logger.info(` Loading pod permissions.`)\r\n kdata.podPermissions.set(channel, loadPodPermissions(configBlock, logger))\r\n }\r\n else {\r\n logger.info(` No pod permissions.`)\r\n kdata.podPermissions.set(channel, [])\r\n }\r\n }\r\n else {\r\n logger.info(`Cluster ${cluster.getString('name')} will have no channel '${channel}' restrictions.`)\r\n kdata.namespacePermissions.set(channel,[])\r\n kdata.podPermissions.set(channel,[])\r\n }\r\n}\r\n\r\n/**\r\n * reads app-config and builds a list of valid clusters\r\n * @param logger core service for logging\r\n * @param config core service for reading config info\r\n */\r\nconst loadClusters = async (logger:LoggerService, config:RootConfigService) => {\r\n KwirthStaticData.clusterKwirthData.clear()\r\n\r\n var locatingMethods=config.getConfigArray('kubernetes.clusterLocatorMethods')\r\n for (var method of locatingMethods) {\r\n\r\n var clusters=(method.getConfigArray('clusters'))\r\n for (var cluster of clusters) {\r\n\r\n var name=cluster.getString('name')\r\n if (cluster.has('kwirthHome') && cluster.has('kwirthApiKey')) { \r\n var kwirthHome:string = cluster.getOptionalString('kwirthHome')!\r\n var kwirthApiKey:string = cluster.getOptionalString('kwirthApiKey')!\r\n var title:string = (cluster.has('title')?cluster.getString('title'):'No name')\r\n var kwirthClusterData:KwirthClusterData={\r\n name,\r\n kwirthHome,\r\n kwirthApiKey,\r\n kwirthData: {\r\n version: '',\r\n clusterName: '',\r\n inCluster: false,\r\n namespace: '',\r\n deployment: '',\r\n lastVersion: ''\r\n },\r\n title,\r\n namespacePermissions: new Map(),\r\n podPermissions: new Map(),\r\n enabled: false\r\n }\r\n\r\n logger.info(`Kwirth for ${name} is located at ${kwirthClusterData.kwirthHome}. Testing connection...`)\r\n let enableCluster = false\r\n try {\r\n /*\r\n /config/version endpoint returns JSON (KwirthData object):\r\n {\r\n \"clusterName\": \"inCluster\",\r\n \"namespace\": \"default\",\r\n \"deployment\": \"kwirth\",\r\n \"inCluster\": true,\r\n \"version\": \"0.2.213\",\r\n \"lastVersion\": \"0.2.213\"\r\n }\r\n */\r\n var response = await fetch (kwirthClusterData.kwirthHome+'/config/version')\r\n try {\r\n var data = await response.text()\r\n try {\r\n var kwirthData=JSON.parse(data) as KwirthData\r\n logger.info(`Kwirth info at cluster '${kwirthClusterData.name}': ${JSON.stringify(kwirthData)}`)\r\n kwirthClusterData.kwirthData=kwirthData\r\n if (versionGreatOrEqualThan(kwirthData.version, MIN_KWIRTH_VERSION)) {\r\n enableCluster = true\r\n }\r\n else {\r\n logger.error(`Unsupported Kwirth version on cluster '${name}' (${title}) [${kwirthData.version}]. Min version is ${MIN_KWIRTH_VERSION}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.error(`Kwirth at cluster ${kwirthClusterData.name} returned errors: ${err}`)\r\n logger.info('Returned data is:')\r\n logger.info(data)\r\n kwirthClusterData.kwirthData = {\r\n version:'0.0.0',\r\n clusterName:'unknown',\r\n inCluster:false,\r\n namespace:'unknown',\r\n deployment:'unknown',\r\n lastVersion:'0.0.0'\r\n }\r\n }\r\n }\r\n catch (err) {\r\n logger.warn(`Error parsing version response from cluster '${kwirthClusterData.name}': ${err}`)\r\n }\r\n }\r\n catch (err) {\r\n logger.info(`Kwirth access error: ${err}.`)\r\n logger.warn(`Kwirth home URL (${kwirthClusterData.kwirthHome}) at cluster '${kwirthClusterData.name}' cannot be accessed right now.`)\r\n }\r\n\r\n if (enableCluster) {\r\n addChannelPermissions('log',logger, cluster, kwirthClusterData)\r\n addChannelPermissions('alert',logger, cluster, kwirthClusterData)\r\n addChannelPermissions('metrics',logger, cluster, kwirthClusterData)\r\n KwirthStaticData.clusterKwirthData.set(name, kwirthClusterData)\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} will be disabled`)\r\n }\r\n }\r\n else {\r\n logger.warn(`Cluster ${name} has no Kwirth information (kwirthHome and kwirthApiKey are missing).`)\r\n }\r\n }\r\n }\r\n\r\n logger.info('Kwirth static data has been set including following clusters:')\r\n for (var c of KwirthStaticData.clusterKwirthData.keys()) {\r\n logger.info (' '+c)\r\n }\r\n for (var c of KwirthStaticData.clusterKwirthData.keys()) {\r\n console.log(KwirthStaticData.clusterKwirthData.get(c))\r\n }\r\n\r\n}\r\n\r\nexport { loadClusters }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { ClusterValidPods, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { KwirthClusterData, KwirthPodPermissions, PodPermissionRule } from '../model/KwirthClusterData'\r\nimport { KwirthStaticData } from '../model/KwirthStaticData';\r\n//import { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\nconst checkNamespaceAccess = (channel:string, cluster:ClusterValidPods, podData:PodData, userEntityRef:string, userGroups:string[]) : boolean => {\r\n let allowedToNamespace=false\r\n let namespacePermissions = KwirthStaticData.clusterKwirthData.get(cluster.name)?.namespacePermissions\r\n\r\n if (namespacePermissions?.has(channel)) {\r\n let rule = namespacePermissions?.get(channel)!.find(ns => ns.namespace===podData.namespace)\r\n if (rule) {\r\n if (rule.identityRefs.includes(userEntityRef.toLowerCase())) {\r\n // a user ref has been found on a namespace rule\r\n allowedToNamespace=true\r\n }\r\n else {\r\n var groupResult=rule.identityRefs.some(identityRef => userGroups.includes(identityRef));\r\n if (groupResult) {\r\n // a group ref match has been found\r\n allowedToNamespace=true\r\n }\r\n }\r\n }\r\n else {\r\n // no restrictions for this namespace\r\n allowedToNamespace=true\r\n }\r\n }\r\n else {\r\n console.log(`Invalid channel: ${channel}`)\r\n }\r\n return allowedToNamespace\r\n}\r\n\r\nconst checkPodPermissionRule = (ppr:PodPermissionRule, entityName:string, userEntityRef:string, userGroups:string[]) : boolean => {\r\n var refMatch:boolean=false;\r\n\r\n for (var podNameRegex of ppr.pods) {\r\n if (podNameRegex.test(entityName)) {\r\n for (var refRegex of ppr.refs) {\r\n // find userRef\r\n refMatch=refRegex.test(userEntityRef.toLowerCase())\r\n if (refMatch) {\r\n break;\r\n }\r\n else {\r\n // find group ref\r\n refMatch = userGroups.some(g => refRegex.test(g))\r\n if (refMatch) {\r\n break\r\n }\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n if (refMatch) break\r\n }\r\n return refMatch\r\n}\r\n\r\n// const getPodPermissionSet = (reqScope:InstanceConfigScopeEnum, cluster:KwirthClusterData) => {\r\n// switch (reqScope) {\r\n// // case InstanceConfigScopeEnum.FILTER:\r\n// case InstanceConfigScopeEnum.SNAPSHOT:\r\n// case InstanceConfigScopeEnum.STREAM:\r\n// console.log(cluster.podPermissions)\r\n// return cluster.podPermissions.get(reqScope)\r\n// default:\r\n// console.log(`Invalid scope ${reqScope} for permission set`)\r\n// }\r\n// return undefined\r\n// }\r\nconst getPodPermissionSet = (channel:string, cluster:KwirthClusterData) => {\r\n if (cluster.podPermissions.has(channel)) {\r\n return cluster.podPermissions.get(channel)\r\n }\r\n else {\r\n console.log(`Invalid channel ${channel} for permission set`)\r\n return undefined\r\n }\r\n}\r\n\r\n/**\r\n * This funciton checks permissions according to app-config rules (not kwirth rules), that is, namespace rules,\r\n * viewing rules and restarting rules\r\n * @param loggerSvc Backstage logger\r\n * @param reqCluster the cluster the pod belongs to\r\n * @param reqPod data about the pod the user wants to access\r\n * @param podPermissionSet a set of permission for the cluster (extracted from app-config)\r\n * @param entityName the name of the entity to search for\r\n * @param userEntityRef the canonical identity reference for the user ('type:namespace/ref')\r\n * @param userGroups ana array containing a list of groups the user belongs to\r\n * @returns booelan indicating if the user can access the pod for doing what scaope says (view or restart)\r\n */\r\n\r\nconst checkPodAccess = (reqPod:PodData, podPermissionSet:KwirthPodPermissions[], entityName:string, userEntityRef:string, userGroups:string[]):boolean => {\r\n // we check all pod permissions until one of them evaluates to true (must be true on allow/except and false on deny/unless)\r\n\r\n // we use 'filter' here beacause a namespace can be specified more than once\r\n for (var podPermission of podPermissionSet.filter(pp => pp.namespace===reqPod.namespace)) {\r\n if (podPermission.allow) {\r\n \r\n // **** evaluate allow/except rules ****\r\n var allowMatches=false;\r\n var exceptMatches=false;\r\n // we test all allow rules, we stop if one matches\r\n for (var prr of podPermission.allow) {\r\n allowMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups);\r\n }\r\n if (allowMatches) {\r\n if (podPermission.except) {\r\n // we test all except rules, will stop if found one that matches, no need to continue\r\n for (var prr of podPermission.except) {\r\n //exceptMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups);\r\n exceptMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n // if there is a exception the process finishes now for this podPermission)\r\n if (exceptMatches) {\r\n break\r\n }\r\n }\r\n }\r\n else {\r\n }\r\n }\r\n\r\n if (allowMatches && !exceptMatches) {\r\n // **** evaluate deny/unless rules ****\r\n if (podPermission.deny) {\r\n var denyMatches=false\r\n var unlessMatches=false\r\n for (var prr of podPermission.deny) {\r\n //denyMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n denyMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (denyMatches) {\r\n break;\r\n }\r\n }\r\n if (denyMatches && podPermission.unless) {\r\n for (var prr of podPermission.unless) {\r\n //unlessMatches = checkPodPermissionRule(prr, reqPod, entityName, userEntityRef, userGroups)\r\n unlessMatches = checkPodPermissionRule(prr, entityName, userEntityRef, userGroups)\r\n if (unlessMatches) {\r\n break;\r\n }\r\n }\r\n }\r\n if (!denyMatches || (denyMatches && unlessMatches)) {\r\n return true\r\n }\r\n }\r\n else {\r\n return true\r\n }\r\n }\r\n else {\r\n // do nothing, just continue podpermissionset loop\r\n }\r\n }\r\n else {\r\n // if no 'allow' is specified everybody has access\r\n // we continue loop checking other namespaces\r\n return true\r\n }\r\n }\r\n \r\n return false\r\n}\r\n\r\nexport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet }","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport express from 'express'\r\nimport Router from 'express-promise-router'\r\nimport { AuthService, BackstageUserInfo, DiscoveryService, HttpAuthService, LoggerService, RootConfigService, UserInfoService } from '@backstage/backend-plugin-api'\r\nimport { CatalogClient } from '@backstage/catalog-client'\r\nimport { UserEntity } from '@backstage/catalog-model'\r\nimport { FetchApi } from '@backstage/core-plugin-api'\r\n\r\n// Kwirth\r\nimport { ClusterValidPods, PodData } from '@jfvilas/plugin-kwirth-common'\r\nimport { loadClusters } from './config'\r\nimport { KwirthStaticData, VERSION } from '../model/KwirthStaticData'\r\nimport { checkNamespaceAccess, checkPodAccess, getPodPermissionSet } from './permissions'\r\nimport { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\nexport type KwirthRouterOptions = {\r\n discoverySvc: DiscoveryService\r\n configSvc: RootConfigService\r\n loggerSvc: LoggerService\r\n userInfoSvc: UserInfoService\r\n authSvc: AuthService\r\n httpAuthSvc: HttpAuthService\r\n}\r\n\r\n// const debug = (a:any) => {\r\n// if (process.env.KWIRTHDEBUG) console.log(a)\r\n// }\r\n\r\n/**\r\n * \r\n * @param options core services we need for Kwirth to work\r\n * @returns an express Router\r\n */\r\nasync function createRouter(options: KwirthRouterOptions) : Promise<express.Router> {\r\n const { configSvc, loggerSvc, userInfoSvc, authSvc, httpAuthSvc, discoverySvc } = options;\r\n\r\n loggerSvc.info('Loading static config')\r\n\r\n if (!configSvc.has('kubernetes.clusterLocatorMethods')) {\r\n loggerSvc.error(`Kwirth will not start, there is no 'clusterLocatorMethods' defined in app-config.`)\r\n throw new Error('Kwirth backend will not be available.')\r\n }\r\n\r\n try {\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch (err) {\r\n var txt=`Errors detected reading static configuration: ${err}`\r\n loggerSvc.error(txt)\r\n throw new Error(txt)\r\n }\r\n\r\n // subscribe to changes on app-config\r\n if (configSvc.subscribe) {\r\n configSvc.subscribe( () => {\r\n try {\r\n loggerSvc.warn('Change detected on app-config, Kwirth will update config.')\r\n loadClusters(loggerSvc, configSvc)\r\n }\r\n catch(err) {\r\n loggerSvc.error(`Errors detected reading new configuration: ${err}`)\r\n }\r\n })\r\n }\r\n else {\r\n loggerSvc.info('Kwirth cannot subscribe to config changes.')\r\n }\r\n\r\n const router = Router()\r\n\r\n router.use(express.json())\r\n\r\n // we need this function to be able to invoke another backend plugin passing an authorization token\r\n const createAuthFetchApi = (token: string) : FetchApi => {\r\n return {\r\n fetch: async (input, init) => {\r\n init = init || {}\r\n init.headers = {\r\n ...init.headers,\r\n Authorization: `Bearer ${token}`,\r\n }\r\n return fetch(input, init)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Invokes Kwirth to obtain a list of pods that are tagged with the kubernetes-id of the entity we are looking for.\r\n * @param entityName name of the tagge dentity\r\n * @returns a ClusterValidPods[] (each ClusterValidPods is a cluster info with a list of pods tagged with the entityName).\r\n */\r\n const getValidClusters = async (entityName:string) : Promise<ClusterValidPods[]> => {\r\n var clusterList:ClusterValidPods[]=[]\r\n\r\n for (const clusterName of KwirthStaticData.clusterKwirthData.keys()) {\r\n var url = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthHome as string\r\n var apiKeyStr = KwirthStaticData.clusterKwirthData.get(clusterName)?.kwirthApiKey\r\n var title = KwirthStaticData.clusterKwirthData.get(clusterName)?.title\r\n var queryUrl=url+`/managecluster/find?label=backstage.io%2fkubernetes-id&entity=${entityName}&type=pod&data=containers`\r\n try {\r\n var fetchResp = await fetch (queryUrl, {headers:{'Authorization':'Bearer '+apiKeyStr}})\r\n if (fetchResp.status===200) {\r\n var jsonResp=await fetchResp.json()\r\n if (jsonResp) {\r\n let podData:ClusterValidPods = {\r\n name: clusterName, url, title, data: jsonResp, accessKeys: new Map()\r\n }\r\n clusterList.push(podData)\r\n }\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response from cluster ${clusterName}: ${fetchResp.status}`)\r\n console.log(await fetchResp.text())\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n\r\n }\r\n catch (err) {\r\n loggerSvc.warn(`Cannot access cluster ${clusterName} (URL: ${queryUrl}): ${err}`)\r\n clusterList.push({ name: clusterName, url, title, data:[], accessKeys:new Map() })\r\n }\r\n }\r\n\r\n return clusterList\r\n }\r\n\r\n const createAccessKey = async (reqScope:InstanceConfigScopeEnum, cluster:ClusterValidPods, reqPods:PodData[], userName:string) : Promise<any> => {\r\n var resources = reqPods.map(podData => `${reqScope}:${podData.namespace}::${podData.name}:`).join(',')\r\n\r\n var kwirthHome = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthHome as string\r\n var kwirthApiKey = KwirthStaticData.clusterKwirthData.get(cluster.name)?.kwirthApiKey\r\n var payload={\r\n type:'bearer',\r\n resource: resources,\r\n description:`Backstage API key for user ${userName}`,\r\n expire:Date.now()+60*60*1000\r\n }\r\n var fetchResp=await fetch(kwirthHome+'/key',{method:'POST', body:JSON.stringify(payload), headers:{'Content-Type':'application/json', Authorization:'Bearer '+kwirthApiKey}})\r\n if (fetchResp.status===200) {\r\n var data = await fetchResp.json();\r\n return data.accessKey\r\n }\r\n else {\r\n loggerSvc.warn(`Invalid response asking for a key from cluster ${cluster.name}: ${fetchResp.status}`)\r\n return {}\r\n }\r\n }\r\n\r\n const addAccessKeys = async (channel:string, reqScope:InstanceConfigScopeEnum, foundClusters:ClusterValidPods[], entityName:string, userEntityRef:string, userGroups:string[]) => {\r\n //var reqScope:InstanceConfigScopeEnum = reqScopeStr as InstanceConfigScopeEnum\r\n if (!reqScope) {\r\n loggerSvc.info(`Invalid scope requested: ${reqScope}`)\r\n return\r\n }\r\n var principal=userEntityRef.split(':')[1]\r\n var username=principal.split('/')[1]\r\n\r\n for (var foundCluster of foundClusters) {\r\n var podList:PodData[]=[]\r\n\r\n // for each pod we've found on the cluster we check all namespace permissions\r\n\r\n for (var podData of foundCluster.data) {\r\n // first we check if user is allowed to acccess namespace\r\n var allowedToNamespace = checkNamespaceAccess(channel, foundCluster, podData, userEntityRef, userGroups)\r\n\r\n if (allowedToNamespace) {\r\n // then we check if required pod namespace has pod access restriccions for requested namespace\r\n var clusterDef = KwirthStaticData.clusterKwirthData.get(foundCluster.name)\r\n var podPermissionSet = getPodPermissionSet(channel, clusterDef!)\r\n if (!podPermissionSet) {\r\n loggerSvc.warn(`Pod permission set not found: ${channel}`)\r\n continue\r\n }\r\n var namespaceRestricted = podPermissionSet.some(pp => pp.namespace===podData.namespace);\r\n if (!namespaceRestricted || checkPodAccess(podData, podPermissionSet, entityName, userEntityRef, userGroups)) {\r\n // there are no namespace restrictions specified in the pod permission set\r\n podList.push(podData)\r\n }\r\n else {\r\n }\r\n }\r\n else {\r\n // user is not allowed to namespace, so we don't need to check pod permissions\r\n // the loop cotinues with other pods\r\n }\r\n }\r\n if (podList.length>0) {\r\n let accessKey = await createAccessKey(reqScope, foundCluster, podList, username)\r\n foundCluster.accessKeys.set(reqScope, accessKey)\r\n }\r\n else {\r\n console.log(`No pods on podList for ${channel} and ${reqScope}`)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * builds a list of groups (expressed as identity refs) that the user belongs to.\r\n * @param userInfo Backstage user info of the user to search groups for\r\n * @returns an array of group refs in canonical form\r\n */\r\n const getUserGroups = async (userInfo:BackstageUserInfo) : Promise<string[]> => {\r\n const { token } = await authSvc.getPluginRequestToken({\r\n onBehalfOf: await authSvc.getOwnServiceCredentials(),\r\n targetPluginId: 'catalog'\r\n });\r\n const catalogClient = new CatalogClient({\r\n discoveryApi: discoverySvc,\r\n fetchApi: createAuthFetchApi(token),\r\n });\r\n\r\n const entity = await catalogClient.getEntityByRef(userInfo.userEntityRef) as UserEntity\r\n var userGroupsRefs:string[]=[]\r\n //+++ future use: recursive memberOf\r\n if (entity?.spec.memberOf) userGroupsRefs=entity?.spec.memberOf\r\n return userGroupsRefs\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processVersion = async (_req:any, res:any) => {\r\n res.status(200).send({ version:VERSION })\r\n }\r\n\r\n // this is and API endpoint controller\r\n const processAccess = async (req:express.Request, res:express.Response) => {\r\n if (!req.query['scopes'] || !req.query['scopes']) {\r\n res.status(400).send(`'scopes' and 'channel' are required`)\r\n return\r\n }\r\n let reqScopes = (req.query['scopes'].toString()).split(',')\r\n let reqChannel = req.query['channel']?.toString()!\r\n \r\n // obtain basic user info\r\n const credentials = await httpAuthSvc.credentials(req, { allow: ['user'] })\r\n const userInfo = await userInfoSvc.getUserInfo(credentials)\r\n // get user groups list\r\n let userGroupsRefs=await getUserGroups(userInfo)\r\n\r\n loggerSvc.info(`Checking reqScopes '${req.query['scopes']}' scopes to pod: '${req.body.metadata.namespace+'/'+req.body.metadata.name}' for user '${userInfo.userEntityRef}'`)\r\n\r\n // get a list of clusters that contain pods related to entity\r\n //+++ control errors here (maybe we cannot conntact the cluster, for example)\r\n let foundClusters:ClusterValidPods[]=await getValidClusters(req.body.metadata.name)\r\n\r\n // add access keys to authorized resources (according to group membership and Kwirth config in app-config (namespace and pod permissions))\r\n for (var reqScopeStr of reqScopes) {\r\n var reqScope = reqScopeStr as InstanceConfigScopeEnum\r\n await addAccessKeys(reqChannel, reqScope, foundClusters, req.body.metadata.name, userInfo.userEntityRef, userGroupsRefs)\r\n }\r\n \r\n // we build a stringn of arrays from the Map /that can not be serialized)\r\n for (var c of foundClusters) {\r\n (c as any).accessKeys = JSON.stringify(Array.from(c.accessKeys.entries()))\r\n }\r\n res.status(200).send(foundClusters)\r\n }\r\n\r\n router.post(['/access'], (req, res) => {\r\n processAccess(req,res)\r\n })\r\n\r\n router.get(['/version'], (req, res) => {\r\n processVersion(req,res)\r\n })\r\n\r\n return router\r\n}\r\n\r\nexport { createRouter }\r\n","/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'\r\nimport { createRouter } from './service/router'\r\n\r\nexport const kwirthPlugin = createBackendPlugin({\r\n pluginId: 'kwirth',\r\n register(env) {\r\n env.registerInit({\r\n deps: {\r\n discovery: coreServices.discovery,\r\n config: coreServices.rootConfig,\r\n logger: coreServices.logger,\r\n auth: coreServices.auth,\r\n httpAuth: coreServices.httpAuth,\r\n httpRouter: coreServices.httpRouter,\r\n userInfo: coreServices.userInfo\r\n },\r\n async init({ discovery, config, httpRouter, logger, auth, httpAuth, userInfo }) {\r\n httpRouter.use(\r\n await createRouter({\r\n discoverySvc: discovery,\r\n configSvc: config,\r\n loggerSvc: logger,\r\n authSvc: auth,\r\n httpAuthSvc: httpAuth,\r\n userInfoSvc: userInfo\r\n })\r\n )\r\n }\r\n })\r\n }\r\n})\r\n"],"names":["versionGreatOrEqualThan","Router","express","catalogClient","CatalogClient","createBackendPlugin","coreServices"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jfvilas/plugin-kwirth-backend",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
4
4
  "description": "Backstage backend plugin for Kwirth plugins",
5
5
  "keywords": [
6
6
  "Backstage",