From 56f15f6eae1d0e4e02990b51b54de959c6a2f7a1 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 31 Oct 2024 16:30:45 +0100 Subject: [PATCH 01/19] issue 100 new url in qr for evidence instead of device --- workbench-script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/workbench-script.py b/workbench-script.py index cc85629..3ec9662 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -346,6 +346,7 @@ def send_snapshot_to_devicehub(snapshot, token, url, legacy): logger.error(_("Snapshot %s not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), snapshot["uuid"], url, e) + def load_config(config_file="settings.ini"): """ Tries to load configuration from a config file. From 03a25a4a63fd98893f85be0448876165018096ab Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 6 Nov 2024 14:10:02 +0100 Subject: [PATCH 02/19] create credential from snapshot --- settings.ini.example | 9 ++--- workbench-script.py | 88 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 75 insertions(+), 22 deletions(-) diff --git a/settings.ini.example b/settings.ini.example index d63b976..e435964 100644 --- a/settings.ini.example +++ b/settings.ini.example @@ -1,9 +1,8 @@ [settings] -url = http://localhost:8000/api/v1/snapshot/ -#url = https://demo.ereuse.org/api/v1/snapshot/ -# sample token that works with default deployment such as the previous two urls -token = 5018dd65-9abd-4a62-8896-80f34ac66150 - +url = http://localhost:8000/api/snapshot/ +token = '1234' +# wb_sign_token = "27de6ad7-cee2-4fe8-84d4-c7eea9c969c8" +# url_wallet = "http://localhost" # path = /path/to/save # device = your_device_name # # erase = basic diff --git a/workbench-script.py b/workbench-script.py index 3ec9662..537175a 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -13,13 +13,30 @@ import gettext import locale import logging +from pathlib import Path +from string import Template from datetime import datetime +BASE_DIR = Path(__file__).resolve().parent + + +SNAPSHOT_BASE = { + 'timestamp': str(datetime.now()), + 'type': 'Snapshot', + 'uuid': str(uuid.uuid4()), + 'software': "workbench-script", + 'version': "0.0.1", + 'token_hash': "", + 'data': {}, + 'erase': [] +} + + ## Legacy Functions ## def convert_to_legacy_snapshot(snapshot): - snapshot["sid"] = str(uuid.uuid4()).split("-")[0] + snapshot["sid"] = str(uuid.uuid4()).split("-")[1] snapshot["software"] = "workbench-script" snapshot["version"] = "dev" snapshot["schema_api"] = "1.0.0" @@ -29,6 +46,7 @@ def convert_to_legacy_snapshot(snapshot): snapshot["data"]["lshw"] = json.loads(snapshot["data"]["lshw"]) snapshot["data"].pop("disks") snapshot.pop("erase") + snapshot.pop("token_hash") ## End Legacy Functions ## @@ -59,15 +77,14 @@ def exec_cmd_erase(cmd): ## End Utility functions ## -SNAPSHOT_BASE = { - 'timestamp': str(datetime.now()), - 'type': 'Snapshot', - 'uuid': str(uuid.uuid4()), - 'software': "workbench-script", - 'version': "0.0.1", - 'data': {}, - 'erase': [] -} +def convert_to_credential(snapshot): + snapshot["data"] = json.dumps(snapshot["data"]) + file_path = os.path.join(BASE_DIR, "templates", "snapshot.json") + with open(file_path) as f: + ff = f.read() + template = Template(ff) + cred = template.substitute(**snapshot) + return cred ## Command Functions ## @@ -266,20 +283,20 @@ def gen_snapshot(all_disks): return snapshot -def save_snapshot_in_disk(snapshot, path): +def save_snapshot_in_disk(snapshot, path, snap_uuid): snapshot_path = os.path.join(path, 'snapshots') filename = "{}/{}_{}.json".format( snapshot_path, datetime.now().strftime("%Y%m%d-%H_%M_%S"), - snapshot['uuid']) + snap_uuid) try: if not os.path.exists(snapshot_path): os.makedirs(snapshot_path) logger.info(_("Created snapshots directory at '%s'"), snapshot_path) with open(filename, "w") as f: - f.write(json.dumps(snapshot)) + f.write(snapshot) logger.info(_("Snapshot written in path '%s'"), filename) except Exception as e: try: @@ -287,13 +304,37 @@ def save_snapshot_in_disk(snapshot, path): fallback_filename = "{}/{}_{}.json".format( path, datetime.now().strftime("%Y%m%d-%H_%M_%S"), - snapshot['uuid']) + snap_uuid) with open(fallback_filename, "w") as f: - f.write(json.dumps(snapshot)) + f.write(snapshot) logger.warning(_("Snapshot written in fallback path '%s'"), fallback_filename) except Exception as e: logger.error(_("Could not save snapshot locally. Reason: Failed to write in fallback path:\n %s"), e) + +def send_to_sign_credential(cred, token, url): + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + } + + try: + data = json.dumps(cred).encode('utf-8') + request = urllib.request.Request(url, data=data, headers=headers) + with urllib.request.urlopen(request) as response: + status_code = response.getcode() + #response_text = response.read().decode('utf-8') + + if 200 <= status_code < 300: + logger.info(_("Credential successfully signed")) + else: + logger.error(_("Credential cannot signed in '%s'"), url) + + except Exception as e: + logger.error(_("Credential not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) + + + # TODO sanitize url, if url is like this, it fails # url = 'http://127.0.0.1:8000/api/snapshot/' def send_snapshot_to_devicehub(snapshot, token, url, legacy): @@ -366,6 +407,8 @@ def load_config(config_file="settings.ini"): device = config.get('settings', 'device', fallback=None) erase = config.get('settings', 'erase', fallback=None) legacy = config.get('settings', 'legacy', fallback=None) + url_wallet = config.get('settings', 'url_wallet', fallback=None) + wb_sign_token = config.get('settings', 'wb_sign_token', fallback=None) else: logger.error(_("Config file '%s' not found. Using default values."), config_file) path = os.path.join(os.getcwd()) @@ -377,7 +420,9 @@ def load_config(config_file="settings.ini"): 'token': token, 'device': device, 'erase': erase, - 'legacy': legacy + 'legacy': legacy, + 'wb_sign_token': wb_sign_token, + 'url_wallet': url_wallet } def parse_args(): @@ -440,6 +485,7 @@ def main(): all_disks = get_disks() snapshot = gen_snapshot(all_disks) + snap_uuid = snapshot["uuid"] if config['erase'] and config['device'] and not config.get("legacy"): snapshot['erase'] = gen_erase(all_disks, config['erase'], user_disk=config['device']) @@ -448,8 +494,16 @@ def main(): if legacy: convert_to_legacy_snapshot(snapshot) + snapshot = json.dumps(snapshot) + else: + snapshot = convert_to_credential(snapshot) + url_wallet = config.get("url_wallet") + wb_sign_token = config.get("wb_sign_token") + if url_wallet and wb_sign_token: + snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) - save_snapshot_in_disk(snapshot, config['path']) + + save_snapshot_in_disk(snapshot, config['path'], snap_uuid) if config['url']: send_snapshot_to_devicehub(snapshot, config['token'], config['url'], legacy) From 5a50be493b7494b72eb58c228bc1eeffc54636e8 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 7 Nov 2024 17:33:36 +0100 Subject: [PATCH 03/19] fix lshw json --- workbench-script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workbench-script.py b/workbench-script.py index 537175a..429cfc7 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -262,7 +262,7 @@ def smartctl(all_disks, disk=None): # TODO permitir selección # TODO permitir que vaya más rápido def get_data(all_disks): - lshw = 'sudo lshw -json' + lshw = 'sudo lshw -xml' hwinfo = 'sudo hwinfo --reallyall' dmidecode = 'sudo dmidecode' lspci = 'sudo lspci -vv' From 57deefdcf4810daf8cf5a39db87ee80dabb57e79 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 8 Nov 2024 12:23:45 +0100 Subject: [PATCH 04/19] fix flow --- workbench-script.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 429cfc7..b16c6ed 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -13,14 +13,9 @@ import gettext import locale import logging -from pathlib import Path -from string import Template from datetime import datetime -BASE_DIR = Path(__file__).resolve().parent - - SNAPSHOT_BASE = { 'timestamp': str(datetime.now()), 'type': 'Snapshot', @@ -496,9 +491,13 @@ def main(): convert_to_legacy_snapshot(snapshot) snapshot = json.dumps(snapshot) else: - snapshot = convert_to_credential(snapshot) url_wallet = config.get("url_wallet") wb_sign_token = config.get("wb_sign_token") + + if wb_sign_token: + tk = wb_sign_token.encode("utf8") + snapshot["token_hash"] = hashlib.hash256(tk).hexdigest() + if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) From 9832cbf5f2d05c56901a34938308a63978bb5250 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Mon, 11 Nov 2024 18:15:14 +0100 Subject: [PATCH 05/19] . --- workbench-script.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workbench-script.py b/workbench-script.py index b16c6ed..f57a9a9 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -407,7 +407,7 @@ def load_config(config_file="settings.ini"): else: logger.error(_("Config file '%s' not found. Using default values."), config_file) path = os.path.join(os.getcwd()) - url, token, device, erase, legacy = None, None, None, None, None + url, token, device, erase, legacy, url_wallet, wb_sign_token = None, None, None, None, None, None, None return { 'path': path, @@ -500,6 +500,8 @@ def main(): if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) + else: + snapshot = json.dumps(snapshot) save_snapshot_in_disk(snapshot, config['path'], snap_uuid) From 83e717fb5e604910ee2156c2a0e66c03bb08d35e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 12 Nov 2024 10:50:49 +0100 Subject: [PATCH 06/19] add inxi --- workbench-script.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/workbench-script.py b/workbench-script.py index f57a9a9..dde1d46 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -251,6 +251,19 @@ def smartctl(all_disks, disk=None): return data_list +@logs +def inxi(): + filename = "hw.json" + command = 'sudo inxi' + options = '-a -x 3 -f -m -n --edid -G -E -M -A -B -D --output json --output-file' + cmd = f"{command} {options} {filename}" + exec_cmd(cmd) + with open(filename) ad f: + data = f.read() + rm = f"rm -f {filename}" + os.popen(rm).read() + return data + ## End Command Functions ## @@ -261,12 +274,14 @@ def get_data(all_disks): hwinfo = 'sudo hwinfo --reallyall' dmidecode = 'sudo dmidecode' lspci = 'sudo lspci -vv' + data = { 'lshw': exec_cmd(lshw) or "{}", 'disks': smartctl(all_disks), 'hwinfo': exec_cmd(hwinfo), 'dmidecode': exec_cmd(dmidecode), 'lspci': exec_cmd(lspci) + 'inxi': exec_inxi() } return data From b3003a4e004b770a62d86571a69820e1af843011 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 15 Nov 2024 10:45:26 +0100 Subject: [PATCH 07/19] inxi --- workbench-script.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index dde1d46..467d028 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -251,37 +251,19 @@ def smartctl(all_disks, disk=None): return data_list -@logs -def inxi(): - filename = "hw.json" - command = 'sudo inxi' - options = '-a -x 3 -f -m -n --edid -G -E -M -A -B -D --output json --output-file' - cmd = f"{command} {options} {filename}" - exec_cmd(cmd) - with open(filename) ad f: - data = f.read() - rm = f"rm -f {filename}" - os.popen(rm).read() - return data - ## End Command Functions ## # TODO permitir selección # TODO permitir que vaya más rápido def get_data(all_disks): - lshw = 'sudo lshw -xml' - hwinfo = 'sudo hwinfo --reallyall' dmidecode = 'sudo dmidecode' - lspci = 'sudo lspci -vv' + inxi = "sudo inxi -afmnGEMABD -x 3 --edid --output json --output-file print" data = { - 'lshw': exec_cmd(lshw) or "{}", 'disks': smartctl(all_disks), - 'hwinfo': exec_cmd(hwinfo), 'dmidecode': exec_cmd(dmidecode), - 'lspci': exec_cmd(lspci) - 'inxi': exec_inxi() + 'inxi': exec_cmd(inxi) } return data From 68a2df1b2817130657a6147fe4798f902c23ae9f Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 15 Nov 2024 11:05:24 +0100 Subject: [PATCH 08/19] add inxi in main --- workbench-script.py | 48 +++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 467d028..418cc6e 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -3,7 +3,6 @@ import os import json import uuid -import hashlib import argparse import configparser import urllib.parse @@ -81,6 +80,32 @@ def convert_to_credential(snapshot): cred = template.substitute(**snapshot) return cred +def convert_to_legacy_snapshot(snapshot): + snapshot["sid"] = str(uuid.uuid4()).split("-")[1] + snapshot["software"] = "workbench-script" + snapshot["version"] = "dev" + snapshot["schema_api"] = "1.0.0" + snapshot["settings_version"] = "No Settings Version (NaN)" + snapshot["timestamp"] = snapshot["timestamp"].replace(" ", "T") + snapshot["data"]["smart"] = snapshot["data"]["disks"] + snapshot["data"].pop("disks") + snapshot["data"].pop("inxi") + snapshot.pop("erase") + snapshot.pop("token_hash") + + lshw = 'sudo lshw -xml' + hwinfo = 'sudo hwinfo --reallyall' + lspci = 'sudo lspci -vv' + + data = { + 'lshw': exec_cmd(lshw) or "{}", + 'hwinfo': exec_cmd(hwinfo), + 'lspci': exec_cmd(lspci) + } + snapshot['data'].update(data) + +## End Legacy Functions ## + ## Command Functions ## ## Erase Functions ## @@ -329,12 +354,15 @@ def send_to_sign_credential(cred, token, url): # TODO sanitize url, if url is like this, it fails # url = 'http://127.0.0.1:8000/api/snapshot/' -def send_snapshot_to_devicehub(snapshot, token, url, legacy): +def send_snapshot_to_devicehub(snapshot, token, url): url_components = urllib.parse.urlparse(url) ev_path = "evidence/{}".format(snapshot["uuid"]) - components = (url_components.scheme, url_components.netloc, ev_path, '', '', '') + components = (url_components.schema, url_components.netloc, ev_path, '', '', '') ev_url = urllib.parse.urlunparse(components) # apt install qrencode + qr = "echo {} | qrencode -t ANSI".format(ev_url) + print(exec_cmd(qr)) + print(ev_url) headers = { "Authorization": f"Bearer {token}", @@ -345,7 +373,7 @@ def send_snapshot_to_devicehub(snapshot, token, url, legacy): request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() - response_text = response.read().decode('utf-8') + #response_text = response.read().decode('utf-8') if 200 <= status_code < 300: logger.info(_("Snapshot successfully sent to '%s'"), url) @@ -368,15 +396,10 @@ def send_snapshot_to_devicehub(snapshot, token, url, legacy): print(exec_cmd(qr)) print(f"url: {ev_url}") else: - logger.error(_("Snapshot %s could not be sent to URL '%s'"), snapshot["uuid"], url) - # TODO review all the try-except thing here; maybe the try inside legacy does not make sense anymore - except urllib.error.HTTPError as e: - error_details = e.read().decode('utf-8') # Get the error response body - logger.error(_("Snapshot %s not remotely sent to URL '%s'. Server responded with error:\n %s"), - snapshot["uuid"], url, error_details) + logger.error(_("Snapshot cannot sent to '%s'"), url) except Exception as e: - logger.error(_("Snapshot %s not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), snapshot["uuid"], url, e) + logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) @@ -468,7 +491,6 @@ def main(): config_file = args.config config = load_config(config_file) - legacy = config.get("legacy") # TODO show warning if non root, means data is not complete # if annotate as potentially invalid snapshot (pending the new API to be done) @@ -504,7 +526,7 @@ def main(): save_snapshot_in_disk(snapshot, config['path'], snap_uuid) if config['url']: - send_snapshot_to_devicehub(snapshot, config['token'], config['url'], legacy) + send_snapshot_to_devicehub(snapshot, config['token'], config['url']) logger.info(_("END")) From b4e18e52da21d3cb699352784cd7329ed37819d2 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 5 Dec 2024 19:24:34 +0100 Subject: [PATCH 09/19] inxi and credentials fix --- workbench-script.py | 49 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 418cc6e..a6ba90c 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -21,7 +21,7 @@ SNAPSHOT_BASE = { 'uuid': str(uuid.uuid4()), 'software': "workbench-script", 'version': "0.0.1", - 'token_hash': "", + 'operator_id': "", 'data': {}, 'erase': [] } @@ -87,11 +87,11 @@ def convert_to_legacy_snapshot(snapshot): snapshot["schema_api"] = "1.0.0" snapshot["settings_version"] = "No Settings Version (NaN)" snapshot["timestamp"] = snapshot["timestamp"].replace(" ", "T") - snapshot["data"]["smart"] = snapshot["data"]["disks"] - snapshot["data"].pop("disks") + snapshot["data"]["smart"] = snapshot["data"]["smartctl"] + snapshot["data"].pop("smartctl") snapshot["data"].pop("inxi") + snapshot.pop("operator_id") snapshot.pop("erase") - snapshot.pop("token_hash") lshw = 'sudo lshw -xml' hwinfo = 'sudo hwinfo --reallyall' @@ -274,7 +274,7 @@ def smartctl(all_disks, disk=None): data = exec_smart(disk['name']) data_list.append(data) - return data_list + return json.dumps(data_list) ## End Command Functions ## @@ -286,7 +286,7 @@ def get_data(all_disks): inxi = "sudo inxi -afmnGEMABD -x 3 --edid --output json --output-file print" data = { - 'disks': smartctl(all_disks), + 'smartctl': smartctl(all_disks), 'dmidecode': exec_cmd(dmidecode), 'inxi': exec_cmd(inxi) } @@ -329,35 +329,53 @@ def save_snapshot_in_disk(snapshot, path, snap_uuid): logger.error(_("Could not save snapshot locally. Reason: Failed to write in fallback path:\n %s"), e) -def send_to_sign_credential(cred, token, url): +def send_to_sign_credential(snapshot, token, url): headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } try: + cred = { + "type": "DeviceSnapshotV1", + "save": False, + "data": { + "operator_id": snapshot["operator_id"], + "dmidecode": snapshot["data"]["dmidecode"], + "inxi": snapshot["data"]["inxi"], + "smartctl": snapshot["data"]["smartctl"], + "uuid": snapshot["uuid"], + } + } + data = json.dumps(cred).encode('utf-8') request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() - #response_text = response.read().decode('utf-8') + response_text = response.read().decode('utf-8') if 200 <= status_code < 300: logger.info(_("Credential successfully signed")) + res = json.loads(response_text) + if res.get("status") == "success" and res.get("data"): + return res["data"] + return snapshot else: logger.error(_("Credential cannot signed in '%s'"), url) + return snapshot except Exception as e: - logger.error(_("Credential not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) + logger.error(_("Credential not remotely builded to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) + return json.dumps(snapshot) # TODO sanitize url, if url is like this, it fails # url = 'http://127.0.0.1:8000/api/snapshot/' -def send_snapshot_to_devicehub(snapshot, token, url): +def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid): url_components = urllib.parse.urlparse(url) - ev_path = "evidence/{}".format(snapshot["uuid"]) - components = (url_components.schema, url_components.netloc, ev_path, '', '', '') + ev_path = f"evidence/{ev_uuid}" + components = (url_components.scheme, url_components.netloc, ev_path, '', '', '') ev_url = urllib.parse.urlunparse(components) # apt install qrencode qr = "echo {} | qrencode -t ANSI".format(ev_url) @@ -369,7 +387,7 @@ def send_snapshot_to_devicehub(snapshot, token, url): "Content-Type": "application/json" } try: - data = json.dumps(snapshot).encode('utf-8') + data = snapshot.encode('utf-8') request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() @@ -515,18 +533,17 @@ def main(): if wb_sign_token: tk = wb_sign_token.encode("utf8") - snapshot["token_hash"] = hashlib.hash256(tk).hexdigest() + snapshot["operator_id"] = hashlib.sha3_256(tk).hexdigest() if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) else: snapshot = json.dumps(snapshot) - save_snapshot_in_disk(snapshot, config['path'], snap_uuid) if config['url']: - send_snapshot_to_devicehub(snapshot, config['token'], config['url']) + send_snapshot_to_devicehub(snapshot, config['token'], config['url'], snap_uuid) logger.info(_("END")) From 8914c309ec3f25050e8a374eedde43d68362df40 Mon Sep 17 00:00:00 2001 From: pedro Date: Mon, 9 Dec 2024 19:19:08 +0100 Subject: [PATCH 10/19] bugfixes contributed by thomas related to PR #7 --- deploy-workbench.sh | 2 +- install-dependencies.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy-workbench.sh b/deploy-workbench.sh index d0884bc..3effbef 100755 --- a/deploy-workbench.sh +++ b/deploy-workbench.sh @@ -327,7 +327,7 @@ echo 'Install requirements' apt-get install -y --no-install-recommends \ sudo locales keyboard-configuration console-setup qrencode \ python-is-python3 python3 python3-dev python3-pip pipenv \ - dmidecode smartmontools hwinfo pciutils lshw nfs-common < /dev/null + dmidecode smartmontools hwinfo pciutils lshw nfs-common inxi < /dev/null # Install lshw B02.19 utility using backports (DEPRECATED in Debian 12) #apt install -y -t ${VERSION_CODENAME}-backports lshw < /dev/null diff --git a/install-dependencies.sh b/install-dependencies.sh index bb97b97..e1f7345 100755 --- a/install-dependencies.sh +++ b/install-dependencies.sh @@ -9,7 +9,7 @@ set -u set -x main() { - sudo apt install smartmontools lshw hwinfo dmidecode + sudo apt install qrencode smartmontools lshw hwinfo dmidecode inxi } main "${@}" From ad2b375346b1402fa1410e4a56d7a398445fbf6c Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 31 Oct 2024 16:30:45 +0100 Subject: [PATCH 11/19] issue 100 new url in qr for evidence instead of device --- workbench-script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workbench-script.py b/workbench-script.py index a6ba90c..bb89e6b 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -352,7 +352,7 @@ def send_to_sign_credential(snapshot, token, url): request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() - response_text = response.read().decode('utf-8') + #response_text = response.read().decode('utf-8') if 200 <= status_code < 300: logger.info(_("Credential successfully signed")) From 7c1e0a4870875257968d3bce3498176e4ceff2fa Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 6 Nov 2024 14:10:02 +0100 Subject: [PATCH 12/19] create credential from snapshot --- settings.ini.example | 7 ++++++- workbench-script.py | 11 ++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/settings.ini.example b/settings.ini.example index e435964..33f4dad 100644 --- a/settings.ini.example +++ b/settings.ini.example @@ -1,8 +1,13 @@ [settings] url = http://localhost:8000/api/snapshot/ -token = '1234' +#url = https://demo.ereuse.org/api/v1/snapshot/ +# sample token that works with default deployment such as the previous two urls +token = 5018dd65-9abd-4a62-8896-80f34ac66150 + +# Idhub # wb_sign_token = "27de6ad7-cee2-4fe8-84d4-c7eea9c969c8" # url_wallet = "http://localhost" + # path = /path/to/save # device = your_device_name # # erase = basic diff --git a/workbench-script.py b/workbench-script.py index bb89e6b..93515af 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -12,6 +12,8 @@ import gettext import locale import logging +from pathlib import Path +from string import Template from datetime import datetime @@ -528,17 +530,12 @@ def main(): convert_to_legacy_snapshot(snapshot) snapshot = json.dumps(snapshot) else: + snapshot = convert_to_credential(snapshot) url_wallet = config.get("url_wallet") wb_sign_token = config.get("wb_sign_token") - - if wb_sign_token: - tk = wb_sign_token.encode("utf8") - snapshot["operator_id"] = hashlib.sha3_256(tk).hexdigest() - if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) - else: - snapshot = json.dumps(snapshot) + save_snapshot_in_disk(snapshot, config['path'], snap_uuid) From 69654d1e7f63c60f35644b13eb5f97833af47fc5 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 8 Nov 2024 12:23:45 +0100 Subject: [PATCH 13/19] fix flow --- workbench-script.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 93515af..924d2f9 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -12,8 +12,6 @@ import gettext import locale import logging -from pathlib import Path -from string import Template from datetime import datetime @@ -530,9 +528,13 @@ def main(): convert_to_legacy_snapshot(snapshot) snapshot = json.dumps(snapshot) else: - snapshot = convert_to_credential(snapshot) url_wallet = config.get("url_wallet") wb_sign_token = config.get("wb_sign_token") + + if wb_sign_token: + tk = wb_sign_token.encode("utf8") + snapshot["token_hash"] = hashlib.hash256(tk).hexdigest() + if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) From f90d67ff4dc44841dcc54e571569b022ed0f13ac Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 15 Nov 2024 11:05:24 +0100 Subject: [PATCH 14/19] add inxi in main --- workbench-script.py | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/workbench-script.py b/workbench-script.py index 924d2f9..3670ee6 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -329,6 +329,7 @@ def save_snapshot_in_disk(snapshot, path, snap_uuid): logger.error(_("Could not save snapshot locally. Reason: Failed to write in fallback path:\n %s"), e) + def send_to_sign_credential(snapshot, token, url): headers = { "Authorization": f"Bearer {token}", @@ -363,6 +364,59 @@ def send_to_sign_credential(snapshot, token, url): else: logger.error(_("Credential cannot signed in '%s'"), url) return snapshot + except Exception as e: + logger.error(_("Credential not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) + + + +# TODO sanitize url, if url is like this, it fails +# url = 'http://127.0.0.1:8000/api/snapshot/' +def send_snapshot_to_devicehub(snapshot, token, url): + url_components = urllib.parse.urlparse(url) + ev_path = "evidence/{}".format(snapshot["uuid"]) + components = (url_components.schema, url_components.netloc, ev_path, '', '', '') + ev_url = urllib.parse.urlunparse(components) + # apt install qrencode + qr = "echo {} | qrencode -t ANSI".format(ev_url) + print(exec_cmd(qr)) + print(ev_url) + + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json" + } + try: + data = json.dumps(snapshot).encode('utf-8') + request = urllib.request.Request(url, data=data, headers=headers) + with urllib.request.urlopen(request) as response: + status_code = response.getcode() + #response_text = response.read().decode('utf-8') + + if 200 <= status_code < 300: + logger.info(_("Snapshot successfully sent to '%s'"), url) + if legacy: + try: + response = json.loads(response_text) + public_url = response.get('public_url') + dhid = response.get('dhid') + if public_url: + # apt install qrencode + qr = "echo {} | qrencode -t ANSI".format(public_url) + print(exec_cmd(qr)) + print("url: {}".format(public_url)) + if dhid: + print("dhid: {}".format(dhid)) + except Exception: + logger.error(response_text) + else: + qr = "echo {} | qrencode -t ANSI".format(ev_url) + print(exec_cmd(qr)) + print(f"url: {ev_url}") + else: + logger.error(_("Snapshot cannot sent to '%s'"), url) + + except Exception as e: + logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) except Exception as e: logger.error(_("Credential not remotely builded to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) From 2eb6154267a5d5c79b14eda7e12d7ca65af4e873 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 5 Dec 2024 19:24:34 +0100 Subject: [PATCH 15/19] inxi and credentials fix --- workbench-script.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 3670ee6..0f554d2 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -329,7 +329,6 @@ def save_snapshot_in_disk(snapshot, path, snap_uuid): logger.error(_("Could not save snapshot locally. Reason: Failed to write in fallback path:\n %s"), e) - def send_to_sign_credential(snapshot, token, url): headers = { "Authorization": f"Bearer {token}", @@ -353,7 +352,7 @@ def send_to_sign_credential(snapshot, token, url): request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() - #response_text = response.read().decode('utf-8') + response_text = response.read().decode('utf-8') if 200 <= status_code < 300: logger.info(_("Credential successfully signed")) @@ -364,17 +363,18 @@ def send_to_sign_credential(snapshot, token, url): else: logger.error(_("Credential cannot signed in '%s'"), url) return snapshot - except Exception as e: - logger.error(_("Credential not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) + except Exception as e: + logger.error(_("Credential not remotely builded to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) + return json.dumps(snapshot) # TODO sanitize url, if url is like this, it fails # url = 'http://127.0.0.1:8000/api/snapshot/' -def send_snapshot_to_devicehub(snapshot, token, url): +def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid): url_components = urllib.parse.urlparse(url) - ev_path = "evidence/{}".format(snapshot["uuid"]) - components = (url_components.schema, url_components.netloc, ev_path, '', '', '') + ev_path = f"evidence/{ev_uuid}" + components = (url_components.scheme, url_components.netloc, ev_path, '', '', '') ev_url = urllib.parse.urlunparse(components) # apt install qrencode qr = "echo {} | qrencode -t ANSI".format(ev_url) @@ -386,7 +386,7 @@ def send_snapshot_to_devicehub(snapshot, token, url): "Content-Type": "application/json" } try: - data = json.dumps(snapshot).encode('utf-8') + data = snapshot.encode('utf-8') request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() @@ -587,12 +587,11 @@ def main(): if wb_sign_token: tk = wb_sign_token.encode("utf8") - snapshot["token_hash"] = hashlib.hash256(tk).hexdigest() + snapshot["operator_id"] = hashlib.sha3_256(tk).hexdigest() if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) - save_snapshot_in_disk(snapshot, config['path'], snap_uuid) if config['url']: From ea6fcf174bd7b2253b65e3206792b84d63b6df70 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 Dec 2024 19:48:48 +0100 Subject: [PATCH 16/19] fix rebase with main --- workbench-script.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 0f554d2..4d31835 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -3,6 +3,7 @@ import os import json import uuid +import hashlib import argparse import configparser import urllib.parse @@ -62,6 +63,7 @@ def exec_cmd(cmd): logger.info(_('Running command `%s`'), cmd) return os.popen(cmd).read() + @logs def exec_cmd_erase(cmd): logger.info(_('Running command `%s`'), cmd) @@ -93,7 +95,7 @@ def convert_to_legacy_snapshot(snapshot): snapshot.pop("operator_id") snapshot.pop("erase") - lshw = 'sudo lshw -xml' + lshw = 'sudo lshw -json' hwinfo = 'sudo hwinfo --reallyall' lspci = 'sudo lspci -vv' @@ -359,10 +361,10 @@ def send_to_sign_credential(snapshot, token, url): res = json.loads(response_text) if res.get("status") == "success" and res.get("data"): return res["data"] - return snapshot + return json.dumps(snapshot) else: logger.error(_("Credential cannot signed in '%s'"), url) - return snapshot + return json.dumps(snapshot) except Exception as e: logger.error(_("Credential not remotely builded to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) @@ -371,15 +373,12 @@ def send_to_sign_credential(snapshot, token, url): # TODO sanitize url, if url is like this, it fails # url = 'http://127.0.0.1:8000/api/snapshot/' -def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid): +def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid, legacy): url_components = urllib.parse.urlparse(url) ev_path = f"evidence/{ev_uuid}" components = (url_components.scheme, url_components.netloc, ev_path, '', '', '') ev_url = urllib.parse.urlunparse(components) # apt install qrencode - qr = "echo {} | qrencode -t ANSI".format(ev_url) - print(exec_cmd(qr)) - print(ev_url) headers = { "Authorization": f"Bearer {token}", @@ -390,7 +389,7 @@ def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid): request = urllib.request.Request(url, data=data, headers=headers) with urllib.request.urlopen(request) as response: status_code = response.getcode() - #response_text = response.read().decode('utf-8') + response_text = response.read().decode('utf-8') if 200 <= status_code < 300: logger.info(_("Snapshot successfully sent to '%s'"), url) @@ -413,7 +412,7 @@ def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid): print(exec_cmd(qr)) print(f"url: {ev_url}") else: - logger.error(_("Snapshot cannot sent to '%s'"), url) + logger.error(_("Snapshot %s not remotely sent to URL '%s'. Server responded with error:\n %s"), ev_uuid, url, response_text) except Exception as e: logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) @@ -499,7 +498,7 @@ def load_config(config_file="settings.ini"): else: logger.error(_("Config file '%s' not found. Using default values."), config_file) path = os.path.join(os.getcwd()) - url, token, device, erase, legacy, url_wallet, wb_sign_token = None, None, None, None, None, None, None + url, token, device, erase, legacy, url_wallet, wb_sign_token = (None,)*7 return { 'path': path, @@ -563,6 +562,7 @@ def main(): config_file = args.config config = load_config(config_file) + legacy = config.get("legacy") # TODO show warning if non root, means data is not complete # if annotate as potentially invalid snapshot (pending the new API to be done) @@ -573,9 +573,9 @@ def main(): snapshot = gen_snapshot(all_disks) snap_uuid = snapshot["uuid"] - if config['erase'] and config['device'] and not config.get("legacy"): + if config['erase'] and config['device'] and not legacy: snapshot['erase'] = gen_erase(all_disks, config['erase'], user_disk=config['device']) - elif config['erase'] and not config.get("legacy"): + elif config['erase'] and not legacy: snapshot['erase'] = gen_erase(all_disks, config['erase']) if legacy: @@ -595,7 +595,13 @@ def main(): save_snapshot_in_disk(snapshot, config['path'], snap_uuid) if config['url']: - send_snapshot_to_devicehub(snapshot, config['token'], config['url'], snap_uuid) + send_snapshot_to_devicehub( + snapshot, + config['token'], + config['url'], + snap_uuid, + legacy + ) logger.info(_("END")) From 7f224618ee26eac5e9fe527e3fe10a8579fd780e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 12 Dec 2024 20:09:25 +0100 Subject: [PATCH 17/19] fix rebase 4 time --- workbench-script.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workbench-script.py b/workbench-script.py index 4d31835..1562f26 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -591,6 +591,8 @@ def main(): if url_wallet and wb_sign_token: snapshot = send_to_sign_credential(snapshot, wb_sign_token, url_wallet) + else: + snapshot = json.dumps(snapshot) save_snapshot_in_disk(snapshot, config['path'], snap_uuid) From 4d2905799f3422aba783bfb965836bfcd5d0f792 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 13 Dec 2024 09:50:01 +0100 Subject: [PATCH 18/19] fix smart legacy as a list --- workbench-script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workbench-script.py b/workbench-script.py index 1562f26..45a8f4d 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -89,7 +89,7 @@ def convert_to_legacy_snapshot(snapshot): snapshot["schema_api"] = "1.0.0" snapshot["settings_version"] = "No Settings Version (NaN)" snapshot["timestamp"] = snapshot["timestamp"].replace(" ", "T") - snapshot["data"]["smart"] = snapshot["data"]["smartctl"] + snapshot["data"]["smart"] = json.loads(snapshot["data"]["smartctl"]) snapshot["data"].pop("smartctl") snapshot["data"].pop("inxi") snapshot.pop("operator_id") From 82e60dff248a320da93fd5f72a0ec0f0f0d3ebbf Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 17 Dec 2024 08:43:39 +0100 Subject: [PATCH 19/19] refactor rebase --- workbench-script.py | 82 +-------------------------------------------- 1 file changed, 1 insertion(+), 81 deletions(-) diff --git a/workbench-script.py b/workbench-script.py index 45a8f4d..7d49b9b 100644 --- a/workbench-script.py +++ b/workbench-script.py @@ -28,23 +28,6 @@ SNAPSHOT_BASE = { } -## Legacy Functions ## - -def convert_to_legacy_snapshot(snapshot): - snapshot["sid"] = str(uuid.uuid4()).split("-")[1] - snapshot["software"] = "workbench-script" - snapshot["version"] = "dev" - snapshot["schema_api"] = "1.0.0" - snapshot["settings_version"] = "No Settings Version (NaN)" - snapshot["timestamp"] = snapshot["timestamp"].replace(" ", "T") - snapshot["data"]["smart"] = snapshot["data"]["disks"] - snapshot["data"]["lshw"] = json.loads(snapshot["data"]["lshw"]) - snapshot["data"].pop("disks") - snapshot.pop("erase") - snapshot.pop("token_hash") - -## End Legacy Functions ## - ## Utility Functions ## def logs(f): @@ -73,14 +56,7 @@ def exec_cmd_erase(cmd): ## End Utility functions ## -def convert_to_credential(snapshot): - snapshot["data"] = json.dumps(snapshot["data"]) - file_path = os.path.join(BASE_DIR, "templates", "snapshot.json") - with open(file_path) as f: - ff = f.read() - template = Template(ff) - cred = template.substitute(**snapshot) - return cred +## Legacy Functions ## def convert_to_legacy_snapshot(snapshot): snapshot["sid"] = str(uuid.uuid4()).split("-")[1] @@ -417,62 +393,6 @@ def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid, legacy): except Exception as e: logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) - except Exception as e: - logger.error(_("Credential not remotely builded to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) - return json.dumps(snapshot) - - - -# TODO sanitize url, if url is like this, it fails -# url = 'http://127.0.0.1:8000/api/snapshot/' -def send_snapshot_to_devicehub(snapshot, token, url, ev_uuid): - url_components = urllib.parse.urlparse(url) - ev_path = f"evidence/{ev_uuid}" - components = (url_components.scheme, url_components.netloc, ev_path, '', '', '') - ev_url = urllib.parse.urlunparse(components) - # apt install qrencode - qr = "echo {} | qrencode -t ANSI".format(ev_url) - print(exec_cmd(qr)) - print(ev_url) - - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json" - } - try: - data = snapshot.encode('utf-8') - request = urllib.request.Request(url, data=data, headers=headers) - with urllib.request.urlopen(request) as response: - status_code = response.getcode() - #response_text = response.read().decode('utf-8') - - if 200 <= status_code < 300: - logger.info(_("Snapshot successfully sent to '%s'"), url) - if legacy: - try: - response = json.loads(response_text) - public_url = response.get('public_url') - dhid = response.get('dhid') - if public_url: - # apt install qrencode - qr = "echo {} | qrencode -t ANSI".format(public_url) - print(exec_cmd(qr)) - print("url: {}".format(public_url)) - if dhid: - print("dhid: {}".format(dhid)) - except Exception: - logger.error(response_text) - else: - qr = "echo {} | qrencode -t ANSI".format(ev_url) - print(exec_cmd(qr)) - print(f"url: {ev_url}") - else: - logger.error(_("Snapshot cannot sent to '%s'"), url) - - except Exception as e: - logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e) - - def load_config(config_file="settings.ini"): """