68 lines
2.9 KiB
TypeScript
68 lines
2.9 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
|
|
import { readPacAdmissionContract } from "./platform-infra-pac-provenance";
|
|
|
|
export interface PacConsumerRbacDesiredIdentity {
|
|
readonly configSha256: string;
|
|
readonly namespaces: readonly string[];
|
|
}
|
|
|
|
const pacConsumerReadOnlyRules = [
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelineruns", "taskruns"], verbs: ["get", "list", "watch"] },
|
|
{ apiGroups: [""], resources: ["pods", "pods/log"], verbs: ["get", "list", "watch"] },
|
|
] as const;
|
|
|
|
const pacConsumerDefaultRoleRef = { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "unidesk-pac-consumer-runner" } as const;
|
|
|
|
export function renderPacConsumerRbacDesiredFragment(targetId: string): string {
|
|
const consumers = readPacAdmissionContract().consumers.filter((consumer) => consumer.node.toLowerCase() === targetId.toLowerCase());
|
|
const namespaces = [...new Set(consumers.map((consumer) => consumer.namespace))];
|
|
if (namespaces.length === 0) return "";
|
|
const configSha256 = pacConsumerRbacDesiredIdentity(targetId).configSha256;
|
|
const objects = namespaces.flatMap((namespace) => {
|
|
const labels = {
|
|
"app.kubernetes.io/managed-by": "unidesk",
|
|
"app.kubernetes.io/part-of": "platform-infra",
|
|
"unidesk.ai/pac-rbac-contract": "admission-provenance-required",
|
|
};
|
|
const annotations = {
|
|
"argocd.argoproj.io/sync-wave": "-1",
|
|
"unidesk.ai/effective-config-sha256": configSha256,
|
|
};
|
|
return [
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "Role",
|
|
metadata: { name: "unidesk-pac-consumer-runner", namespace, labels, annotations },
|
|
rules: pacConsumerReadOnlyRules,
|
|
},
|
|
{
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
kind: "RoleBinding",
|
|
metadata: { name: "unidesk-pac-consumer-runner-default", namespace, labels, annotations },
|
|
subjects: [{ kind: "ServiceAccount", name: "default", namespace }],
|
|
roleRef: pacConsumerDefaultRoleRef,
|
|
},
|
|
];
|
|
});
|
|
return `${objects.map((item) => Bun.YAML.stringify(item).trim()).join("\n---\n")}\n`;
|
|
}
|
|
|
|
export function pacConsumerRbacDesiredIdentity(targetId: string): PacConsumerRbacDesiredIdentity {
|
|
const consumers = readPacAdmissionContract().consumers.filter((consumer) => consumer.node.toLowerCase() === targetId.toLowerCase());
|
|
const namespaces = [...new Set(consumers.map((consumer) => consumer.namespace))].sort();
|
|
const desiredSpec = namespaces.map((namespace) => ({
|
|
namespace,
|
|
role: { name: "unidesk-pac-consumer-runner", rules: pacConsumerReadOnlyRules },
|
|
roleBinding: {
|
|
name: "unidesk-pac-consumer-runner-default",
|
|
subjects: [{ kind: "ServiceAccount", name: "default", namespace }],
|
|
roleRef: pacConsumerDefaultRoleRef,
|
|
},
|
|
}));
|
|
return {
|
|
configSha256: `sha256:${createHash("sha256").update(JSON.stringify({ targetId, desiredSpec, contract: "admission-provenance-required-v1" })).digest("hex")}`,
|
|
namespaces,
|
|
};
|
|
}
|