diff --git a/charts/bitwarden-crd-operator/Chart.yaml b/charts/bitwarden-crd-operator/Chart.yaml index 7f1e104..b0d4180 100644 --- a/charts/bitwarden-crd-operator/Chart.yaml +++ b/charts/bitwarden-crd-operator/Chart.yaml @@ -4,9 +4,9 @@ description: Deploy the Bitwarden CRD Operator type: application -version: "v0.12.0" +version: "v0.13.0" -appVersion: "0.11.0" +appVersion: "0.12.0" keywords: - operator @@ -32,7 +32,7 @@ annotations: url: https://github.com/Lerentis/bitwarden-crd-operator artifacthub.io/crds: | - kind: BitwardenSecret - version: v1beta6 + version: v1beta7 name: bitwarden-secret displayName: Bitwarden Secret description: Management Object to create secrets from bitwarden @@ -47,8 +47,28 @@ annotations: displayName: Bitwarden Template description: Management Object to create secrets from a jinja template with a bitwarden lookup artifacthub.io/crdsExamples: | - - apiVersion: lerentis.uploadfilter24.eu/v1beta6 + - apiVersion: lerentis.uploadfilter24.eu/v1beta7 kind: BitwardenSecret + metadata: + name: test + spec: + content: + - element: + secretName: username + secretRef: nameofUser + - element: + secretName: password + secretRef: passwordOfUser + id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + name: "test-secret" + secretType: Obaque + namespace: "default" + labels: + key: value + annotations: + key: value + - apiVersion: lerentis.uploadfilter24.eu/v1beta1 + kind: BitwardenTLSSecret metadata: name: test spec: @@ -107,6 +127,8 @@ annotations: artifacthub.io/operator: "true" artifacthub.io/containsSecurityUpdates: "false" artifacthub.io/changes: | + - kind: changed + description: "Allow custom type for generated secrets" - kind: changed description: "Update python to 3.11.9-r0" - kind: changed @@ -125,4 +147,4 @@ annotations: description: "Set ownership of generated secrets if CRD is in the same namespace" artifacthub.io/images: | - name: bitwarden-crd-operator - image: ghcr.io/lerentis/bitwarden-crd-operator:0.11.0 + image: ghcr.io/lerentis/bitwarden-crd-operator:0.12.0 diff --git a/charts/bitwarden-crd-operator/crds/bitwarden-secrets.yaml b/charts/bitwarden-crd-operator/crds/bitwarden-secrets.yaml index 6a1221f..00b66fe 100644 --- a/charts/bitwarden-crd-operator/crds/bitwarden-secrets.yaml +++ b/charts/bitwarden-crd-operator/crds/bitwarden-secrets.yaml @@ -92,7 +92,8 @@ spec: - name - name: v1beta6 served: true - storage: true + storage: false + deprecated: true schema: openAPIV3Schema: type: object @@ -128,6 +129,50 @@ spec: annotations: type: object x-kubernetes-preserve-unknown-fields: true + required: + - id + - namespace + - name + - name: v1beta7 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + content: + type: array + items: + type: object + properties: + element: + type: object + properties: + secretName: + type: string + secretRef: + type: string + secretScope: + type: string + required: + - secretName + id: + type: string + namespace: + type: string + name: + type: string + secretType: + type: string + labels: + type: object + x-kubernetes-preserve-unknown-fields: true + annotations: + type: object + x-kubernetes-preserve-unknown-fields: true required: - id - namespace diff --git a/example.yaml b/example.yaml index 9579b48..c7667c1 100644 --- a/example.yaml +++ b/example.yaml @@ -1,5 +1,5 @@ --- -apiVersion: "lerentis.uploadfilter24.eu/v1beta6" +apiVersion: "lerentis.uploadfilter24.eu/v1beta7" kind: BitwardenSecret metadata: name: test @@ -16,6 +16,7 @@ spec: secretScope: login id: "88781348-c81c-4367-9801-550360c21295" name: "test-secret" + secretType: Opaque namespace: "default" labels: key: value diff --git a/skaffold.yaml b/skaffold.yaml index 7290a20..04043c5 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -14,4 +14,4 @@ deploy: chartPath: charts/bitwarden-crd-operator valuesFiles: - env/values.yaml - version: v0.7.4 + version: v0.13.0 diff --git a/src/kv.py b/src/kv.py index 921d849..60deee9 100644 --- a/src/kv.py +++ b/src/kv.py @@ -3,10 +3,9 @@ import kubernetes import base64 import json -from utils.utils import unlock_bw, get_secret_from_bitwarden, parse_login_scope, parse_fields_scope, bw_sync_interval +from utils.utils import unlock_bw, get_secret_from_bitwarden, parse_login_scope, parse_fields_scope, get_attachment, bw_sync_interval -def create_kv(secret, secret_json, content_def): - secret.type = "Opaque" +def create_kv(logger, id, secret, secret_json, content_def): secret.data = {} for eleml in content_def: for k, elem in eleml.items(): @@ -31,6 +30,13 @@ def create_kv(secret, secret_json, content_def): f"Field {_secret_key} has no value in bitwarden secret") secret.data[_secret_ref] = str(base64.b64encode( value.encode("utf-8")), "utf-8") + if _secret_scope == "attachment": + value = get_attachment(logger, id, _secret_key) + if value is None: + raise Exception( + f"Attachment {_secret_key} has no value in bitwarden secret") + secret.data[_secret_ref] = str(base64.b64encode( + value.encode("utf-8")), "utf-8") return secret @@ -43,6 +49,7 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): secret_namespace = spec.get('namespace') labels = spec.get('labels') custom_annotations = spec.get('annotations') + custom_secret_type = spec.get('secretType') unlock_bw(logger) logger.info(f"Locking up secret with ID: {id}") @@ -58,13 +65,17 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): if custom_annotations: annotations.update(custom_annotations) + if not custom_secret_type: + custom_secret_type = 'Opaque' + if not labels: labels = {} secret = kubernetes.client.V1Secret() secret.metadata = kubernetes.client.V1ObjectMeta( name=secret_name, annotations=annotations, labels=labels) - secret = create_kv(secret, secret_json_object, content_def) + secret.type = custom_secret_type + secret = create_kv(logger, id, secret, secret_json_object, content_def) # Garbage collection will delete the generated secret if the owner # Is not in the same namespace as the generated secret @@ -95,21 +106,27 @@ def update_managed_secret( old_config = None old_secret_name = None old_secret_namespace = None + old_secret_type = None if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations: old_config = json.loads( body.metadata.annotations['kopf.zalando.org/last-handled-configuration']) old_secret_name = old_config['spec'].get('name') old_secret_namespace = old_config['spec'].get('namespace') + old_secret_type = old_config['spec'].get('type') secret_name = spec.get('name') secret_namespace = spec.get('namespace') labels = spec.get('labels') custom_annotations = spec.get('annotations') + custom_secret_type = spec.get('secretType') + + if not custom_secret_type: + custom_secret_type = 'Opaque' if old_config is not None and ( - old_secret_name != secret_name or old_secret_namespace != secret_namespace): + old_secret_name != secret_name or old_secret_namespace != secret_namespace or old_secret_type != custom_secret_type): # If the name of the secret or the namespace of the secret is different # We have to delete the secret an recreate it - logger.info("Secret name or namespace changed, let's recreate it") + logger.info("Secret name, namespace or type changed, let's recreate it") delete_managed_secret( old_config['spec'], name, @@ -139,7 +156,8 @@ def update_managed_secret( secret = kubernetes.client.V1Secret() secret.metadata = kubernetes.client.V1ObjectMeta( name=secret_name, annotations=annotations, labels=labels) - secret = create_kv(secret, secret_json_object, content_def) + secret.type = custom_secret_type + secret = create_kv(logger, id, secret, secret_json_object, content_def) # Garbage collection will delete the generated secret if the owner # Is not in the same namespace as the generated secret