From cc0c731271411f037cd43b540519847585a2dda3 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Tue, 16 Jul 2024 12:26:04 +0200 Subject: [PATCH] feat(helm): Automatically create role & bindings for namespace containing platform secret Signed-off-by: Thomas Kosiewski --- Justfile | 1 + chart/templates/_rbac.tpl | 33 ++++++ chart/templates/platform-rbac.yaml | 31 ++++++ chart/tests/platform-secret-role_test.yaml | 114 +++++++++++++++++++++ config/config.go | 6 +- 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 chart/templates/platform-rbac.yaml create mode 100644 chart/tests/platform-secret-role_test.yaml diff --git a/Justfile b/Justfile index c77995b6a3..423c30d2f0 100644 --- a/Justfile +++ b/Justfile @@ -74,6 +74,7 @@ generate-vcluster-images version="0.0.0": generate-cli-docs: go run -mod vendor -tags pro ./hack/docs/main.go +# Generate the vcluster.yaml config schema generate-config-schema: go run -mod vendor ./hack/schema/main.go diff --git a/chart/templates/_rbac.tpl b/chart/templates/_rbac.tpl index b3ff6b249a..91ab3c4816 100644 --- a/chart/templates/_rbac.tpl +++ b/chart/templates/_rbac.tpl @@ -143,3 +143,36 @@ {{- end }} {{- end }} {{- end -}} + +{{/* + Whether to create a role and role binding to access the platform API key secret +*/}} +{{- define "vcluster.rbac.createPlatformSecretRole" -}} +{{- $createRBAC := dig "platform" "apiKey" "createRBAC" true .Values.external -}} +{{- if and $createRBAC (ne (include "vcluster.rbac.platformSecretNamespace" .) .Release.Namespace) }} +{{- true -}} +{{- end }} +{{- end -}} + +{{/* + Namespace containing the vCluster platform secret +*/}} +{{- define "vcluster.rbac.platformSecretNamespace" -}} +{{- dig "platform" "apiKey" "namespace" .Release.Namespace .Values.external | default .Release.Namespace -}} +{{- end -}} + +{{/* + Name specifies the secret name containing the vCluster platform licenses and tokens +*/}} +{{- define "vcluster.rbac.platformSecretName" -}} +{{- dig "platform" "apiKey" "secretName" "vcluster-platform-api-key" .Values.external | quote -}} +{{- end -}} + +{{- define "vcluster.rbac.platformRoleName" -}} +{{- printf "vc-%s-v-%s-platform-role" .Release.Name .Release.Namespace | trunc 63 | trimSuffix "-" -}} +{{- end -}} + + +{{- define "vcluster.rbac.platformRoleBindingName" -}} +{{- printf "vc-%s-v-%s-platform-role-binding" .Release.Name .Release.Namespace | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/chart/templates/platform-rbac.yaml b/chart/templates/platform-rbac.yaml new file mode 100644 index 0000000000..cb56bef504 --- /dev/null +++ b/chart/templates/platform-rbac.yaml @@ -0,0 +1,31 @@ +{{- if include "vcluster.rbac.createPlatformSecretRole" . }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "vcluster.rbac.platformRoleName" . }} + namespace: {{ include "vcluster.rbac.platformSecretNamespace" .}} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] + resourceNames: + - {{ include "vcluster.rbac.platformSecretName" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "vcluster.rbac.platformRoleBindingName" . }} + namespace: {{ include "vcluster.rbac.platformSecretNamespace" .}} +subjects: + - kind: ServiceAccount + {{- if .Values.controlPlane.advanced.serviceAccount.name }} + name: {{ .Values.controlPlane.advanced.serviceAccount.name }} + {{- else }} + name: vc-{{ .Release.Name }} + {{- end }} + namespace: {{ include "vcluster.rbac.platformSecretNamespace" .}} +roleRef: + kind: Role + name: {{ include "vcluster.rbac.platformRoleName" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/chart/tests/platform-secret-role_test.yaml b/chart/tests/platform-secret-role_test.yaml new file mode 100644 index 0000000000..4ad0323197 --- /dev/null +++ b/chart/tests/platform-secret-role_test.yaml @@ -0,0 +1,114 @@ +suite: Platform Secret Role +templates: + - platform-rbac.yaml + +tests: + - it: check explicitly disabled + set: + external: + platform: + apiKey: + namespace: "some-other-namespace" + createRBAC: false + asserts: + - hasDocuments: + count: 0 + + - it: check disabled on empty namespace + set: + external: + platform: + apiKey: + namespace: "" + asserts: + - hasDocuments: + count: 0 + + - it: check disabled on implicit same namespace + set: + external: + platform: + apiKey: + secretName: "some-other-secret" + asserts: + - hasDocuments: + count: 0 + + - it: automatically create role for specific secret for reading & patching + set: + external: + platform: + apiKey: + secretName: "my-secret-name" + namespace: "some-other-namespace" + asserts: + - hasDocuments: + count: 2 + - documentIndex: 0 + lengthEqual: + path: rules + count: 1 + - documentIndex: 0 + equal: + path: metadata.name + value: "vc-RELEASE-NAME-v-NAMESPACE-platform-role" + - documentIndex: 1 + equal: + path: metadata.name + value: "vc-RELEASE-NAME-v-NAMESPACE-platform-role-binding" + - documentIndex: 0 + contains: + path: rules + count: 1 + content: + apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] + resourceNames: ["my-secret-name"] + - documentIndex: 1 + contains: + path: subjects + count: 1 + content: + kind: ServiceAccount + name: vc-RELEASE-NAME + namespace: some-other-namespace + + - it: automatically create role for default secret for reading & patching + set: + external: + platform: + apiKey: + namespace: "some-other-namespace" + asserts: + - hasDocuments: + count: 2 + - documentIndex: 0 + lengthEqual: + path: rules + count: 1 + - documentIndex: 0 + equal: + path: metadata.name + value: "vc-RELEASE-NAME-v-NAMESPACE-platform-role" + - documentIndex: 1 + equal: + path: metadata.name + value: "vc-RELEASE-NAME-v-NAMESPACE-platform-role-binding" + - documentIndex: 0 + contains: + path: rules + count: 1 + content: + apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] + resourceNames: ["vcluster-platform-api-key"] + - documentIndex: 1 + contains: + path: subjects + count: 1 + content: + kind: ServiceAccount + name: vc-RELEASE-NAME + namespace: some-other-namespace diff --git a/config/config.go b/config/config.go index 079ab05a3d..eca52ad7fb 100644 --- a/config/config.go +++ b/config/config.go @@ -1795,7 +1795,6 @@ type ExperimentalDeployHelmChart struct { type PlatformConfig struct { // APIKey defines where to find the platform access key and host. By default, vCluster will search in the following locations in this precedence: - // * platform.api.accessKey // * environment variable called LICENSE // * secret specified under external.platform.apiKey.secretName // * secret called "vcluster-platform-api-key" in the vCluster namespace @@ -1810,6 +1809,11 @@ type PlatformAPIKey struct { // Namespace defines the namespace where the access key secret should be retrieved from. If this is not equal to the namespace // where the vCluster instance is deployed, you need to make sure vCluster has access to this other namespace. Namespace string `json:"namespace,omitempty"` + + // CreateRBAC will automatically create the necessary RBAC roles and role bindings to allow vCluster to read the secret specified + // in the above namespace, if specified. + // This defaults to true. + CreateRBAC *bool `json:"createRBAC,omitempty"` } type ExperimentalGenericSync struct {