Skip to content

Commit

Permalink
rbac: needs tests, account for non-scoped mode, and the regular yaml …
Browse files Browse the repository at this point in the history
…files

Signed-off-by: Case Wylie <cmwylie19@defenseunicorns.com>
  • Loading branch information
cmwylie19 committed Oct 21, 2024
1 parent 6f239a9 commit fc78a28
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 6 deletions.
14 changes: 14 additions & 0 deletions src/lib/assets/helm.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 7 additions & 5 deletions src/lib/assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 }));

Expand Down
42 changes: 41 additions & 1 deletion src/lib/assets/yaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Rule> = {}
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"),
},
Expand Down
8 changes: 8 additions & 0 deletions src/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 12 additions & 0 deletions src/lib/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>;
Expand All @@ -35,6 +44,9 @@ export type ModuleConfig = {
env?: Record<string, string>;
/** Custom Labels for Kubernetes Objects */
customLabels?: CustomLabels;
/** Add Configuration for RBAC */
// Get from KFC
rbac?: Rule[];
};

export type PackageJSON = {
Expand Down

0 comments on commit fc78a28

Please sign in to comment.