From f0716e7a28d052c38e3ee507f5a3ff6bf462c8af Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Sat, 21 Mar 2020 10:32:27 +0200 Subject: [PATCH] move kubeconfig route Signed-off-by: Jari Kolehmainen --- dashboard/client/components/app.tsx | 2 + dashboard/server/api/get-cluster-info.ts | 23 +---- dashboard/server/app.ts | 3 +- dashboard/server/routes/index.ts | 1 - dashboard/server/routes/kubeconfig-route.ts | 100 -------------------- src/main/router.ts | 3 + src/main/routes/kubeconfig.ts | 58 ++++++++++++ 7 files changed, 66 insertions(+), 124 deletions(-) delete mode 100644 dashboard/server/routes/kubeconfig-route.ts create mode 100644 src/main/routes/kubeconfig.ts diff --git a/dashboard/client/components/app.tsx b/dashboard/client/components/app.tsx index 882bcdfcab..b07d72749c 100755 --- a/dashboard/client/components/app.tsx +++ b/dashboard/client/components/app.tsx @@ -14,6 +14,7 @@ import { UserManagement } from "./+user-management/user-management"; import { ConfirmDialog } from "./confirm-dialog"; import { usersManagementRoute } from "./+user-management/user-management.routes"; import { clusterRoute, clusterURL } from "./+cluster"; +import { KubeConfigDialog } from "./kubeconfig-dialog/kubeconfig-dialog"; import { Nodes, nodesRoute } from "./+nodes"; import { Workloads, workloadsRoute, workloadsURL } from "./+workloads"; import { Namespaces, namespacesRoute } from "./+namespaces"; @@ -70,6 +71,7 @@ class App extends React.Component { + diff --git a/dashboard/server/api/get-cluster-info.ts b/dashboard/server/api/get-cluster-info.ts index c24178741a..339e225245 100644 --- a/dashboard/server/api/get-cluster-info.ts +++ b/dashboard/server/api/get-cluster-info.ts @@ -1,39 +1,20 @@ // Get cluster info -import config from "../config" import { kubeRequest } from "./kube-request"; -import { IClusterInfo, IClusterConfigMap } from "../common/cluster" +import { IClusterInfo } from "../common/cluster" export async function getClusterInfo(): Promise { - const [configMap, kubeVersion, pharosVersion] = await Promise.all([ - getClusterConfigMap().catch(() => ({} as IClusterConfigMap)), + const [kubeVersion] = await Promise.all([ getKubeVersion().catch(() => null), - getPharosVersion().catch(() => null), ]); return { - ...configMap, kubeVersion, - pharosVersion, }; } -export async function getClusterConfigMap() { - const res = await kubeRequest<{ data: IClusterConfigMap }>({ - path: `/api/v1/namespaces/${config.LENS_NAMESPACE}/configmaps/config`, - }); - return res.data; -} - export async function getKubeVersion() { const res = await kubeRequest<{ gitVersion: string }>({ path: "/version", }); return res.gitVersion.slice(1); } - -export async function getPharosVersion() { - const res = await kubeRequest<{ data: { "pharos-version": string } }>({ - path: `/api/v1/namespaces/kube-system/configmaps/pharos-config`, - }); - return res ? res.data["pharos-version"] : null; -} diff --git a/dashboard/server/app.ts b/dashboard/server/app.ts index 73da918e89..6de90d466a 100644 --- a/dashboard/server/app.ts +++ b/dashboard/server/app.ts @@ -8,7 +8,7 @@ import compression from "compression" import helmet from "helmet" import morgan from "morgan" import { logger } from "../server/utils/logger" -import { configRoute, kubeconfigRoute, kubewatchRoute, metricsRoute, readyStateRoute } from "../server/routes"; +import { configRoute, kubewatchRoute, metricsRoute, readyStateRoute } from "../server/routes"; import { useRequestHeaderToken } from "../server/middlewares"; const { @@ -25,7 +25,6 @@ app.set('trust proxy', 1); // trust first proxy localApis.use( configRoute(), readyStateRoute(), - kubeconfigRoute(), kubewatchRoute(), metricsRoute() ); diff --git a/dashboard/server/routes/index.ts b/dashboard/server/routes/index.ts index d7ca1a4cb1..3b2fdaaf8b 100644 --- a/dashboard/server/routes/index.ts +++ b/dashboard/server/routes/index.ts @@ -1,5 +1,4 @@ export * from "./config-route" -export * from "./kubeconfig-route" export * from "./kubewatch-route" export * from "./metrics-route" export * from "./ready-state-route" diff --git a/dashboard/server/routes/kubeconfig-route.ts b/dashboard/server/routes/kubeconfig-route.ts deleted file mode 100644 index 9ed0f445e6..0000000000 --- a/dashboard/server/routes/kubeconfig-route.ts +++ /dev/null @@ -1,100 +0,0 @@ -//-- Kubeconfig route - -import { Router } from "express"; -import { AxiosError } from "axios"; -import { userSession } from "../user-session"; -import { kubeRequest } from "../api/kube-request"; -import { Secret } from "../../client/api/endpoints"; -import { base64 } from "../../client/utils/base64"; -import { getCertificateAuthorityData } from "../api/get-cert-auth-data"; -import { getClusterConfigMap } from "../api/get-cluster-info"; - -interface IKubeConfigParams { - username: string; - authToken: string; - certificateAuthority: string; - namespace?: string; -} - -export function kubeconfigRoute() { - const router = Router(); - - router.route('/kubeconfig/user') - .get(async (req, res) => { - const { username = "kubernetes" } = userSession.get(req); - const authToken = userSession.getToken(req); - const cert = await getCertificateAuthorityData('base64'); - const data = await generateKubeConfig({ - username, - authToken: authToken, - certificateAuthority: cert - }); - res.json(data); - }); - - router.route('/kubeconfig/service-account/:namespace/:account') - .get(async (req, res) => { - const { authHeader } = userSession.get(req); - const { namespace, account } = req.params; - try { - const secret = await kubeRequest<{ items: Secret[] }>({ - path: `/api/v1/namespaces/${namespace}/secrets`, - authHeader: authHeader, - }).then(secrets => { - return secrets.items.find(secret => { - const { annotations } = secret.metadata; - return annotations && annotations["kubernetes.io/service-account.name"] == account; - }); - }); - const data = await generateKubeConfig({ - username: account, - namespace: namespace, - authToken: base64.decode(secret.data.token), - certificateAuthority: secret.data["ca.crt"], - }); - res.json(data); - } catch (err) { - const { response }: AxiosError = err; - res.status(403).json(response ? response.data : err.toString()); - } - }); - - return router; -} - -async function generateKubeConfig(params: IKubeConfigParams) { - const { clusterName, clusterUrl } = await getClusterConfigMap(); - const { authToken, username, certificateAuthority, namespace = "" } = params; - return { - 'apiVersion': 'v1', - 'kind': 'Config', - 'clusters': [ - { - 'name': clusterName, - 'cluster': { - 'server': clusterUrl, - 'certificate-authority-data': certificateAuthority - } - } - ], - 'users': [ - { - 'name': username, - 'user': { - 'token': authToken, - } - } - ], - 'contexts': [ - { - 'name': clusterName, - 'context': { - 'user': username, - 'cluster': clusterName, - 'namespace': namespace, - } - } - ], - 'current-context': clusterName - } -} diff --git a/src/main/router.ts b/src/main/router.ts index 04abad4c7c..7093189f2c 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -2,6 +2,7 @@ import * as http from "http"; import { Cluster } from "./cluster"; import { helmApi } from "./helm-api" import { resourceApplierApi } from "./resource-applier-api" +import { kubeconfigRoute } from "./routes/kubeconfig" // eslint-disable-next-line @typescript-eslint/no-var-requires const Call = require('@hapi/call'); @@ -65,6 +66,8 @@ export class Router { } protected addRoutes() { + this.router.add({ method: 'get', path: '/api/kubeconfig/service-account/{namespace}/{account}' }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute)) + // Helm API this.router.add({ method: 'get', path: '/api-helm/v2/charts' }, helmApi.listCharts.bind(helmApi)) this.router.add({ method: 'get', path: '/api-helm/v2/charts/{repo}/{chart}' }, helmApi.getChart.bind(helmApi)) diff --git a/src/main/routes/kubeconfig.ts b/src/main/routes/kubeconfig.ts new file mode 100644 index 0000000000..6a6f173f3a --- /dev/null +++ b/src/main/routes/kubeconfig.ts @@ -0,0 +1,58 @@ +import { LensApiRequest } from "../router" +import { LensApi } from "../lens-api" +import { Cluster } from "../cluster" +import { CoreV1Api, V1Secret } from "@kubernetes/client-node" + +function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster) { + const tokenData = new Buffer(secret.data["token"], "base64") + return { + 'apiVersion': 'v1', + 'kind': 'Config', + 'clusters': [ + { + 'name': cluster.contextName, + 'cluster': { + 'server': cluster.contextHandler.kc.getCurrentCluster().server, + 'certificate-authority-data': secret.data["ca.crt"] + } + } + ], + 'users': [ + { + 'name': username, + 'user': { + 'token': tokenData.toString("utf8"), + } + } + ], + 'contexts': [ + { + 'name': cluster.contextName, + 'context': { + 'user': username, + 'cluster': cluster.contextName, + 'namespace': secret.metadata.namespace, + } + } + ], + 'current-context': cluster.contextName + } +} + +class KubeconfigRoute extends LensApi { + + public async routeServiceAccountRoute(request: LensApiRequest) { + const { params, response, cluster} = request + + const client = cluster.contextHandler.kc.makeApiClient(CoreV1Api); + const secretList = await client.listNamespacedSecret(params.namespace) + const secret = secretList.body.items.find(secret => { + const { annotations } = secret.metadata; + return annotations && annotations["kubernetes.io/service-account.name"] == params.account; + }); + const data = generateKubeConfig(params.account, secret, cluster); + this.respondJson(response, data) + } +} + +export const kubeconfigRoute = new KubeconfigRoute()