mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Lens app source code (#119)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
936cbd53d4
commit
1d0815abd2
797 files changed
+73714
-215
No files matched your search
@@ -0,0 +1,23 @@
|
||||
// Get certificate auth data
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as util from "util";
|
||||
import config from "../config";
|
||||
|
||||
let caData: string = null
|
||||
|
||||
export async function getCertificateAuthorityData(encoding = 'utf8'): Promise<string> {
|
||||
if (caData) {
|
||||
return caData
|
||||
}
|
||||
if (!fs.existsSync(config.KUBERNETES_CA_CERT)) {
|
||||
caData = config.KUBERNETES_CA_CERT
|
||||
return caData
|
||||
}
|
||||
try {
|
||||
const ca = await util.promisify(fs.readFile)(config.KUBERNETES_CA_CERT);
|
||||
return Buffer.from(ca).toString(encoding);
|
||||
} catch (error) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Get cluster info
|
||||
|
||||
import config from "../config"
|
||||
import { kubeRequest } from "./kube-request";
|
||||
import { IClusterInfo, IClusterConfigMap } from "../common/cluster"
|
||||
|
||||
export async function getClusterInfo(): Promise<IClusterInfo> {
|
||||
const [configMap, kubeVersion, pharosVersion] = await Promise.all([
|
||||
getClusterConfigMap().catch(() => ({} as IClusterConfigMap)),
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Get namespaces
|
||||
|
||||
import config from "../config";
|
||||
import { KubeJsonApiDataList } from "../../client/api/kube-json-api";
|
||||
import { IKubeRequestParams, kubeRequest } from "./kube-request";
|
||||
import { reviewResourceAccess } from "./review-resource-access";
|
||||
import { getServiceAccountToken } from "./get-service-account-token"
|
||||
|
||||
export async function getNamespaces(params: Partial<IKubeRequestParams> = {}) {
|
||||
return kubeRequest<KubeJsonApiDataList>({
|
||||
...params,
|
||||
path: "/api/v1/namespaces",
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAllowedNamespaces(
|
||||
params: Partial<IKubeRequestParams>,
|
||||
fallbackNs = config.KUBERNETES_NAMESPACE,
|
||||
): Promise<string[]> {
|
||||
try {
|
||||
const allNamespaces = await getNamespaces(params);
|
||||
const nsAccessStatuses = await Promise.all(
|
||||
allNamespaces.items.map(ns => {
|
||||
const { name } = ns.metadata;
|
||||
return reviewResourceAccess(params, {
|
||||
namespace: name,
|
||||
resource: "pods",
|
||||
verb: "list",
|
||||
});
|
||||
})
|
||||
);
|
||||
return allNamespaces.items
|
||||
.filter((ns, i) => nsAccessStatuses[i].allowed)
|
||||
.map(ns => ns.metadata.name);
|
||||
} catch (e) {
|
||||
const serviceToken = await getServiceAccountToken();
|
||||
if (!serviceToken) {
|
||||
return fallbackNs ? [fallbackNs] : [];
|
||||
}
|
||||
// fetch namespaces with service-account token (cluster-wide)
|
||||
// and for every namespace make additional request to check if namespace available for user-token
|
||||
const allNamespaces = await getNamespaces({
|
||||
authHeader: `Bearer ${serviceToken}`
|
||||
});
|
||||
const nsAccessStatuses = await Promise.all(
|
||||
allNamespaces.items.map(ns => {
|
||||
const { name } = ns.metadata;
|
||||
return reviewResourceAccess(params, {
|
||||
namespace: name,
|
||||
resource: "pods",
|
||||
verb: "list",
|
||||
});
|
||||
})
|
||||
);
|
||||
return allNamespaces.items
|
||||
.filter((ns, i) => nsAccessStatuses[i].allowed)
|
||||
.map(ns => ns.metadata.name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Get service-account token
|
||||
|
||||
import { existsSync, readFile } from "fs";
|
||||
import { promisify } from "util";
|
||||
import config from "../config"
|
||||
|
||||
const tokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token";
|
||||
|
||||
export async function getServiceAccountToken() {
|
||||
const { SERVICE_ACCOUNT_TOKEN } = config;
|
||||
|
||||
if (SERVICE_ACCOUNT_TOKEN) {
|
||||
return SERVICE_ACCOUNT_TOKEN;
|
||||
}
|
||||
|
||||
if (existsSync(tokenPath)) {
|
||||
const token = await promisify(readFile)(tokenPath);
|
||||
return token.toString().trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Check cluster-admin rights for auth-token
|
||||
// CLI: kubectl auth can-i '*' '*' --all-namespaces
|
||||
|
||||
import { reviewResourceAccess } from "./review-resource-access";
|
||||
import { IKubeRequestParams } from "./kube-request";
|
||||
|
||||
export async function isClusterAdmin(params: Partial<IKubeRequestParams>): Promise<boolean> {
|
||||
try {
|
||||
const accessCheck = await reviewResourceAccess(params, {
|
||||
resource: "*",
|
||||
namespace: "*",
|
||||
group: "*",
|
||||
verb: "*",
|
||||
});
|
||||
return accessCheck.allowed;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Kubernetes request api helper
|
||||
|
||||
import config, { isSecure } from "../config";
|
||||
import axios, { AxiosError, AxiosRequestConfig } from "axios"
|
||||
import * as https from "https";
|
||||
import { getCertificateAuthorityData } from "./get-cert-auth-data";
|
||||
import { logger, sanitizeHeaders } from "../utils/logger";
|
||||
import { getServiceAccountToken } from "./get-service-account-token";
|
||||
|
||||
export interface IKubeRequestParams extends AxiosRequestConfig {
|
||||
path: string;
|
||||
authHeader?: string;
|
||||
}
|
||||
|
||||
export async function kubeRequest<T>(params: IKubeRequestParams): Promise<T> {
|
||||
const { KUBE_CLUSTER_URL, KUBERNETES_CLIENT_CERT, KUBERNETES_CLIENT_KEY } = config;
|
||||
const serviceToken = await getServiceAccountToken();
|
||||
const defaultAuthHeader = serviceToken ? `Bearer ${serviceToken}` : "";
|
||||
const {
|
||||
authHeader = defaultAuthHeader,
|
||||
url = KUBE_CLUSTER_URL,
|
||||
path = "",
|
||||
...reqConfig
|
||||
} = params;
|
||||
|
||||
// add access token
|
||||
reqConfig.headers = Object.assign({}, reqConfig.headers, {
|
||||
"Content-type": "application/json",
|
||||
});
|
||||
|
||||
if (!KUBERNETES_CLIENT_CERT && authHeader) {
|
||||
reqConfig.headers["Authorization"] = authHeader;
|
||||
}
|
||||
|
||||
// allow requests to kube-cluster without valid ssl certs..
|
||||
reqConfig.httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: isSecure(),
|
||||
cert: KUBERNETES_CLIENT_CERT,
|
||||
key: KUBERNETES_CLIENT_KEY,
|
||||
ca: await getCertificateAuthorityData(),
|
||||
});
|
||||
|
||||
const reqUrl = url + path;
|
||||
|
||||
return axios(reqUrl, reqConfig)
|
||||
.then(res => res.data)
|
||||
.catch((error: AxiosError<T>) => {
|
||||
const { message, config } = error;
|
||||
logger.error(`[KUBE-REQUEST]: ${message}`, {
|
||||
code: error.code,
|
||||
method: config.method,
|
||||
url: config.url,
|
||||
headers: sanitizeHeaders(config.headers),
|
||||
params: config.params,
|
||||
});
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Get resource access review
|
||||
// Docs: https://kubernetes.io/docs/reference/access-authn-authz/authorization/
|
||||
import { IKubeRequestParams, kubeRequest } from "./kube-request";
|
||||
|
||||
interface IResourceAccess {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
status: IResourceAccessStatus;
|
||||
}
|
||||
|
||||
export interface IResourceAccessStatus {
|
||||
allowed: boolean;
|
||||
denied?: boolean;
|
||||
reason?: string;
|
||||
evaluationError?: string;
|
||||
}
|
||||
|
||||
interface IResourceAccessAttributes {
|
||||
group?: string | "*";
|
||||
resource?: string | "*";
|
||||
verb?: "get" | "list" | "create" | "update" | "patch" | "watch" | "proxy" | "redirect" | "delete" | "deletecollection" | "*";
|
||||
namespace?: string | "*";
|
||||
}
|
||||
|
||||
export async function reviewResourceAccess(
|
||||
params: Partial<IKubeRequestParams> = {},
|
||||
attrs: IResourceAccessAttributes
|
||||
): Promise<IResourceAccessStatus> {
|
||||
try {
|
||||
const accessReview = await kubeRequest<IResourceAccess>({
|
||||
...params,
|
||||
method: "POST",
|
||||
path: "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews",
|
||||
data: {
|
||||
spec: {
|
||||
resourceAttributes: attrs
|
||||
}
|
||||
}
|
||||
});
|
||||
return accessReview.status;
|
||||
} catch (err) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: err.toString(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Check validity of auth-token
|
||||
import { kubeRequest } from "./kube-request";
|
||||
|
||||
export interface ITokenReview {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
status: ITokenReviewStatus;
|
||||
}
|
||||
|
||||
export interface ITokenReviewStatus {
|
||||
authenticated: boolean;
|
||||
user: {
|
||||
username?: string;
|
||||
uid?: string;
|
||||
groups?: string[];
|
||||
};
|
||||
error?: string[];
|
||||
}
|
||||
|
||||
export async function reviewToken(authToken: string): Promise<ITokenReviewStatus> {
|
||||
try {
|
||||
const tokenReview = await kubeRequest<ITokenReview>({
|
||||
path: "/apis/authentication.k8s.io/v1/tokenreviews",
|
||||
method: "POST",
|
||||
data: {
|
||||
spec: {
|
||||
token: authToken
|
||||
}
|
||||
}
|
||||
});
|
||||
return tokenReview.status;
|
||||
} catch (err) {
|
||||
return {
|
||||
authenticated: false,
|
||||
user: {},
|
||||
error: [err.toString()],
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user