From fc78a28b4ec66c18e495734ec779d9c44369d4b2 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Mon, 21 Oct 2024 13:33:32 -0400 Subject: [PATCH] rbac: needs tests, account for non-scoped mode, and the regular yaml files Signed-off-by: Case Wylie --- src/lib/assets/helm.ts | 14 ++++++++++++++ src/lib/assets/index.ts | 12 +++++++----- src/lib/assets/yaml.ts | 42 ++++++++++++++++++++++++++++++++++++++++- src/lib/helpers.ts | 8 ++++++++ src/lib/module.ts | 12 ++++++++++++ 5 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/lib/assets/helm.ts b/src/lib/assets/helm.ts index 89aef5309..8f6525e3f 100644 --- a/src/lib/assets/helm.ts +++ b/src/lib/assets/helm.ts @@ -1,6 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023-Present The Pepr Authors +export function clusterRoleTemplate() { + return ` + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: {{ .Values.uuid }} + namespace: pepr-system + rules: + {{- if .Values.rbac }} + {{- toYaml .Values.rbac | nindent 2 }} + {{- end }} + ` +} + export function nsTemplate() { return ` apiVersion: v1 diff --git a/src/lib/assets/index.ts b/src/lib/assets/index.ts index e876f4ac5..b90f98f5e 100644 --- a/src/lib/assets/index.ts +++ b/src/lib/assets/index.ts @@ -13,7 +13,7 @@ import { allYaml, zarfYaml, overridesFile, zarfYamlChart } from "./yaml"; import { namespaceComplianceValidator, replaceString } from "../helpers"; import { createDirectoryIfNotExists, dedent } from "../helpers"; import { resolve } from "path"; -import { chartYaml, nsTemplate, admissionDeployTemplate, watcherDeployTemplate, serviceMonitorTemplate } from "./helm"; +import { chartYaml, nsTemplate, admissionDeployTemplate, watcherDeployTemplate, clusterRoleTemplate, serviceMonitorTemplate } from "./helm"; import { promises as fs } from "fs"; import { webhookConfig } from "./webhooks"; import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking"; @@ -123,10 +123,12 @@ export class Assets { await fs.writeFile(moduleSecretPath, dumpYaml(moduleSecret(this.name, code, this.hash), { noRefs: true })); await fs.writeFile(storeRolePath, dumpYaml(storeRole(this.name), { noRefs: true })); await fs.writeFile(storeRoleBindingPath, dumpYaml(storeRoleBinding(this.name), { noRefs: true })); - await fs.writeFile( - clusterRolePath, - dumpYaml(clusterRole(this.name, this.capabilities, "rbac"), { noRefs: true }), - ); + // change this to a helm chart template + await fs.writeFile(clusterRolePath, dedent(clusterRoleTemplate())); + // await fs.writeFile( + // clusterRolePath, + // dumpYaml(clusterRole(this.name, this.capabilities, "rbac"), { noRefs: true }), + // ); await fs.writeFile(clusterRoleBindingPath, dumpYaml(clusterRoleBinding(this.name), { noRefs: true })); await fs.writeFile(serviceAccountPath, dumpYaml(serviceAccount(this.name), { noRefs: true })); diff --git a/src/lib/assets/yaml.ts b/src/lib/assets/yaml.ts index 4e75115af..d446a240f 100644 --- a/src/lib/assets/yaml.ts +++ b/src/lib/assets/yaml.ts @@ -10,9 +10,49 @@ import { deployment, moduleSecret, namespace, watcher } from "./pods"; import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac"; import { webhookConfig } from "./webhooks"; import { genEnv } from "./pods"; +import { createRBACMap } from "../helpers"; +import { Rule } from "../module"; +// import { mergeAll } from "ramda"; // Helm Chart overrides file (values.yaml) generated from assets -export async function overridesFile({ hash, name, image, config, apiToken }: Assets, path: string) { +export async function overridesFile({ hash, name, image, config, apiToken, capabilities }: Assets, path: string) { + // check if config has rbac defined + const customRbac = config.rbac || []; + // if no scoped mode - use cluster admin mode + const rbacMap = createRBACMap(capabilities); + const scopedRules = [ + ...Object.keys(rbacMap).map(key => { + // let group:string, version:string, kind:string; + let group: string; + key.split("/").length < 3 ? (group = "") : (group = key.split("/")[0]); + + return { + apiGroups: [group], + resources: [rbacMap[key].plural], + verbs: rbacMap[key].verbs, + }; + }), + ] + // merge and deduplicate the custom rbac and scoped rules + const mergedRBAC = [...customRbac, ...scopedRules] + const deduper:Record = {} + mergedRBAC.forEach((rule, index) => { + const key = `${rule.apiGroups}/${rule.resources}` + // check the values + if(deduper[key]) { + // if it does not have this rules verbs, add them + deduper[key].verbs?.map((verb) => { + if(!rule.verbs?.includes(verb)) { + rule.verbs?.push(verb) + } + }) + } + deduper[key] = rule + }) + + + const overrides = { + rbac: Object.values(deduper), secrets: { apiToken: Buffer.from(apiToken).toString("base64"), }, diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index 1df162a9a..23300aca2 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -170,6 +170,14 @@ export function createRBACMap(capabilities: CapabilityExport[]): RBACMap { plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`, }; } + + // Add finalizer rbac + if(binding.isFinalize){ + acc[key] = { + verbs: ["patch"], + plural: binding.kind.plural || `${binding.kind.kind.toLowerCase()}s`, + } + } }); return acc; diff --git a/src/lib/module.ts b/src/lib/module.ts index 1a68b4ce8..4da129858 100644 --- a/src/lib/module.ts +++ b/src/lib/module.ts @@ -9,6 +9,15 @@ import { CapabilityExport, AdmissionRequest } from "./types"; import { setupWatch } from "./watch-processor"; import { Log } from "../lib"; +// This should be the upstream type, needs to be exported from KFC +export interface Rule { + apiGroups?: string[]; + nonResourceURLs?: string[]; + apiVersions?: string[]; + resourceNames?: string[]; + resources?: string[]; + verbs?: string[]; +} /** Custom Labels Type for package.json */ export interface CustomLabels { namespace?: Record; @@ -35,6 +44,9 @@ export type ModuleConfig = { env?: Record; /** Custom Labels for Kubernetes Objects */ customLabels?: CustomLabels; + /** Add Configuration for RBAC */ + // Get from KFC + rbac?: Rule[]; }; export type PackageJSON = {