From 382b6776ce1677207ec85f9747bb874596edeeb0 Mon Sep 17 00:00:00 2001 From: Nico Angelo Date: Fri, 29 Sep 2023 17:16:12 +0200 Subject: [PATCH 1/4] fetch attachments of items --- src/lookups/bitwarden_lookup.py | 4 +++- src/utils/utils.py | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lookups/bitwarden_lookup.py b/src/lookups/bitwarden_lookup.py index 159542e..9329855 100644 --- a/src/lookups/bitwarden_lookup.py +++ b/src/lookups/bitwarden_lookup.py @@ -1,9 +1,11 @@ import json -from utils.utils import get_secret_from_bitwarden, parse_fields_scope, parse_login_scope +from utils.utils import get_secret_from_bitwarden, get_attachment, parse_fields_scope, parse_login_scope def bitwarden_lookup(id, scope, field): + if scope == "attachment": + return get_attachment(None, id, field) _secret_json = get_secret_from_bitwarden(None, id) if scope == "login": return parse_login_scope(_secret_json, field) diff --git a/src/utils/utils.py b/src/utils/utils.py index 45de998..0c0883c 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -39,6 +39,10 @@ def sync_bw(logger, force=False): logger.info(f"Sync successful {status_output}") +def get_attachment(logger, id, name): + return command_wrapper(logger, command=f"get attachment {name} {id}", raw=True) + + def unlock_bw(logger): status_output = command_wrapper(logger, "status", False) status = status_output['data']['template']['status'] @@ -50,17 +54,22 @@ def unlock_bw(logger): logger.info("Signin successful. Session exported") -def command_wrapper(logger, command, use_success: bool = True): +def command_wrapper(logger, command, use_success: bool = True, raw: bool = False): system_env = dict(os.environ) + response_flag = "--raw" if raw else "--response" sp = subprocess.Popen( - [f"bw --response {command}"], + [f"bw {response_flag} {command}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True, env=system_env) out, err = sp.communicate() - + if err: + logger.warn(err) + return None + if raw: + return out.decode(encoding='UTF-8') if "DEBUG" in system_env: logger.info(out.decode(encoding='UTF-8')) resp = json.loads(out.decode(encoding='UTF-8')) From f7a0f43cabd83846de16701178b5dee74b51458c Mon Sep 17 00:00:00 2001 From: Nico Angelo Date: Mon, 2 Oct 2023 14:50:03 +0200 Subject: [PATCH 2/4] fix get attachment command --- src/utils/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/utils.py b/src/utils/utils.py index 0c0883c..743954b 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -40,7 +40,7 @@ def sync_bw(logger, force=False): def get_attachment(logger, id, name): - return command_wrapper(logger, command=f"get attachment {name} {id}", raw=True) + return command_wrapper(logger, command=f"get attachment {name} --itemid {id}", raw=True) def unlock_bw(logger): From f3cba82c9fea834aa0608d1ae622e0fb6080a8fb Mon Sep 17 00:00:00 2001 From: Nico Angelo Date: Mon, 2 Oct 2023 15:06:30 +0200 Subject: [PATCH 3/4] also populate logger for lookup --- src/lookups/bitwarden_lookup.py | 23 +++++++++++++---------- src/template.py | 21 +++++++++------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lookups/bitwarden_lookup.py b/src/lookups/bitwarden_lookup.py index 9329855..522fbb8 100644 --- a/src/lookups/bitwarden_lookup.py +++ b/src/lookups/bitwarden_lookup.py @@ -1,13 +1,16 @@ -import json - from utils.utils import get_secret_from_bitwarden, get_attachment, parse_fields_scope, parse_login_scope -def bitwarden_lookup(id, scope, field): - if scope == "attachment": - return get_attachment(None, id, field) - _secret_json = get_secret_from_bitwarden(None, id) - if scope == "login": - return parse_login_scope(_secret_json, field) - if scope == "fields": - return parse_fields_scope(_secret_json, field) +class BitwardenLookupHandler: + + def __init__(self, logger) -> None: + self.logger = logger + + def bitwarden_lookup(self, id, scope, field): + if scope == "attachment": + return get_attachment(self.logger, id, field) + _secret_json = get_secret_from_bitwarden(self.logger, id) + if scope == "login": + return parse_login_scope(_secret_json, field) + if scope == "fields": + return parse_fields_scope(_secret_json, field) diff --git a/src/template.py b/src/template.py index f6d3ce9..ed9622c 100644 --- a/src/template.py +++ b/src/template.py @@ -4,27 +4,24 @@ import kubernetes import json from utils.utils import unlock_bw, bw_sync_interval -from lookups.bitwarden_lookup import bitwarden_lookup +from lookups.bitwarden_lookup import BitwardenLookupHandler from jinja2 import Environment, BaseLoader -lookup_func_dict = { - "bitwarden_lookup": bitwarden_lookup, -} - - -def render_template(template): +def render_template(logger, template): jinja_template = Environment(loader=BaseLoader()).from_string(template) - jinja_template.globals.update(lookup_func_dict) + jinja_template.globals.update({ + "bitwarden_lookup": BitwardenLookupHandler(logger).bitwarden_lookup, + }) return jinja_template.render() -def create_template_secret(secret, filename, template): +def create_template_secret(logger, secret, filename, template): secret.type = "Opaque" secret.data = {} secret.data[filename] = str( base64.b64encode( - render_template(template).encode("utf-8")), + render_template(logger, template).encode("utf-8")), "utf-8") return secret @@ -48,7 +45,7 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs): secret = kubernetes.client.V1Secret() secret.metadata = kubernetes.client.V1ObjectMeta( name=secret_name, annotations=annotations) - secret = create_template_secret(secret, filename, template) + secret = create_template_secret(logger, secret, filename, template) obj = api.create_namespaced_secret( secret_namespace, secret @@ -109,7 +106,7 @@ def update_managed_secret( secret = kubernetes.client.V1Secret() secret.metadata = kubernetes.client.V1ObjectMeta( name=secret_name, annotations=annotations) - secret = create_template_secret(secret, filename, template) + secret = create_template_secret(logger, secret, filename, template) try: obj = api.replace_namespaced_secret( From 58b990db2aa13a201fea2d1e074aba492ad1783e Mon Sep 17 00:00:00 2001 From: Nico Angelo Date: Mon, 16 Oct 2023 13:28:12 +0200 Subject: [PATCH 4/4] bump version --- README.md | 16 ++++++++++++---- charts/bitwarden-crd-operator/Chart.yaml | 14 +++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2a1a2bf..192b44d 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ type: dockerconfigjson ## BitwardenTemplate -One of the more freely defined types that can be used with this operator you can just pass a whole template: +One of the more freely defined types that can be used with this operator you can just pass a whole template. Also the lookup function `bitwarden_lookup` is available to reference parts of the secret: ```yaml --- @@ -145,11 +145,11 @@ spec: --- api: enabled: True - key: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields", "name of a field in bitwarden") }} + key: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }} allowCrossOrigin: false apps: "some.app.identifier:some_version": - pubkey: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields", "name of a field in bitwarden") }} + pubkey: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }} enabled: true ``` @@ -169,7 +169,15 @@ metadata: type: Opaque ``` -please note that the rendering engine for this template is jinja2, with an addition of a custom `bitwarden_lookup` function, so there are more possibilities to inject here. +The signature of `bitwarden_lookup` is `(item_id, scope, field)`: +- `item_id`: The item ID of the secret in Bitwarden +- `scope`: one of `login`, `fields` or `attachment` +- `field`: + - when `scope` is `login`: either `username` or `password` + - when `scope` is `fields`: the name of a custom field + - when `scope` is `attachment`: the filename of a file attached to the item + +Please note that the rendering engine for this template is jinja2, with an addition of a custom `bitwarden_lookup` function, so there are more possibilities to inject here. ## Configurations parameters diff --git a/charts/bitwarden-crd-operator/Chart.yaml b/charts/bitwarden-crd-operator/Chart.yaml index 5dd508f..f779973 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.9.0" +version: "v0.10.0" -appVersion: "0.8.0" +appVersion: "0.9.0" keywords: - operator @@ -89,18 +89,14 @@ annotations: allowCrossOrigin: false apps: "some.app.identifier:some_version": - pubkey: {{ bitwarden_lookup("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "fields", "public_key") }} + pubkey: {{ bitwarden_lookup("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "attachment", "public_key") }} enabled: true artifacthub.io/license: MIT artifacthub.io/operator: "true" artifacthub.io/containsSecurityUpdates: "false" artifacthub.io/changes: | - - kind: changed - description: "Unified scheduled none crd related operations (bw sync and login)" - kind: added - description: "Added relogin interval which can be finetuned with env `BW_RELOGIN_INTERVAL`. defaults to 3600 seconds" - - kind: chanced - description: "Updated python to 3.11.6-r0" + description: "Added attachment lookup to bitwarden_lookup in BitwardenTemplate CRD" artifacthub.io/images: | - name: bitwarden-crd-operator - image: ghcr.io/lerentis/bitwarden-crd-operator:0.8.0 + image: ghcr.io/lerentis/bitwarden-crd-operator:0.9.0