diff --git a/docs/BOOTSTRAP.md b/docs/BOOTSTRAP.md index aa5433c..9c47ddb 100644 --- a/docs/BOOTSTRAP.md +++ b/docs/BOOTSTRAP.md @@ -54,6 +54,9 @@ you accomplish your mission. - [mutt](../salt/mutt/README.md) - [signal](../salt/signal/README.md) +- Electronic cash: + - [electrum](../salt/electrum/README.md) + ### Files - USB: diff --git a/salt/electrum/README.md b/salt/electrum/README.md new file mode 100644 index 0000000..5c7455d --- /dev/null +++ b/salt/electrum/README.md @@ -0,0 +1,86 @@ +# electrum + +Electrum Bitcoin Wallet in Qubes OS. + +## Table of Contents + +* [Description](#description) +* [Installation](#installation) +* [Usage](#usage) + * [Wallet cooperation](#wallet-cooperation) + * [Cold wallet terminology](#cold-wallet-terminology) + +## Description + +Setup multiple lightweights Electrum Bitcoin Wallets, one offline qube named +"electrum-cold" and one online qube based on Whonix-Workstation named +"electrum-hot". + +## Installation + +- Top +```sh +qubesctl top.enable electrum +qubesctl --targets=tpl-electrum,electrum-cold,electrum-hot state.apply +qubesctl top.disable electrum +qubesctl state.apply electrum.appmenus +``` + +- State + +```sh +qubesctl state.apply electrum.create +qubesctl --skip-dom0 --targets=tpl-electrum state.apply electrum.install +qubesctl --skip-dom0 --targets=electrum-cold,electrum-hot state.apply electrum.configure +qubesctl state.apply electrum.appmenus +``` + + +## Usage + +The qube `electrum-cold` serves as a cold wallet, while the `electrum-hot` is +networked via tor and you can use it as a watching-only (only pub key present) +or hot wallet (private key present). + +### Wallet cooperation + +If you plan to create private keys on any wallet, it is recommended to pause +or shutdown all qubes to reduce the side-channel attack surface. + +As you have both types of wallets, a networked and an offline one, with the +networked wallet you can broadcast transactions while with the offline one, +you sign them. Sharing data between the qubes can be done with `qvm-copy` and +the process of combining a watching-only and a cold wallet is explained in the +[Electrum wiki](https://electrum.readthedocs.io/en/latest/coldstorage.html). + +### Cold wallet terminology + +I can expect some comments complaining about the term `cold wallet` when +using Qubes OS with an online system. We use this term to refer to an isolated +environment (a qube) that has no internet connection. + +You are free to use a non-Qubes physically air-gapped system if you prefer, +you just have to remove the Audio stack (microphone, speakers), Video stack +(camera), USB stack (external ports, Bluetooth), Network stack (network +cards), External reference lights (blinking pattern). If you use a hardware +wallet, you are dependent on a specific hardware vendor and you will need to +choose at least an insecure transfer method, scanning QR code where you can +expose the camera to the data being read, connecting via NFC/USB/SD card +exposes to the USB stack, transfer via radio exposes all devices nearby to the +signal being passed, guard against supply-chain attacks. In the end, your +air-gapped system is not so secure as you thought it to be. + +Yes, a Xen exploit that reaches Dom0 or a CPU exploit that can infer +[the memory contents of other running VMs](https://www.qubes-os.org/news/2023/11/14/qsb-096/) +or [the contents of data from a different execution context on the +same CPU core](https://www.qubes-os.org/news/2023/09/27/qsb-094/) can +compromise private private keys, so it is up to you, the user, to choose your +strategy. + +Another possibility is a fully offline Qubes OS with this formula installed, +but then again, transferring the data safely to communicate with a networked +device for the transactions to be broadcasted is still a hard thing to fix for +physical air-gapped systems. + +There is no consensus on the best solution, choose the option that you can +have more security, not the one you "fell" more secure. diff --git a/salt/electrum/appmenus.sls b/salt/electrum/appmenus.sls new file mode 100644 index 0000000..5d36794 --- /dev/null +++ b/salt/electrum/appmenus.sls @@ -0,0 +1,8 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% from 'utils/macros/sync-appmenus.sls' import sync_appmenus -%} +{{ sync_appmenus('tpl-' ~ sls_path) }} diff --git a/salt/electrum/appmenus.top b/salt/electrum/appmenus.top new file mode 100644 index 0000000..a343753 --- /dev/null +++ b/salt/electrum/appmenus.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - electrum.appmenus diff --git a/salt/electrum/clone.sls b/salt/electrum/clone.sls new file mode 100644 index 0000000..9467186 --- /dev/null +++ b/salt/electrum/clone.sls @@ -0,0 +1,8 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% from 'utils/macros/clone-template.sls' import clone_template -%} +{{ clone_template('debian-minimal', sls_path) }} diff --git a/salt/electrum/clone.top b/salt/electrum/clone.top new file mode 100644 index 0000000..82d41f4 --- /dev/null +++ b/salt/electrum/clone.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - electrum.clone diff --git a/salt/electrum/configure.sls b/salt/electrum/configure.sls new file mode 100644 index 0000000..86d6853 --- /dev/null +++ b/salt/electrum/configure.sls @@ -0,0 +1,24 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - dev.home-cleanup + - dotfiles.copy-x11 + - dotfiles.copy-sh + +"{{ slsdotpath }}-setconfig-auto_connect": + cmd.run: + - name: electrum --offline setconfig auto_connect false + - runas: user + +"{{ slsdotpath }}-setconfig-check_updates": + cmd.run: + - name: electrum --offline setconfig check_updates false + - runas: user + +{% endif -%} diff --git a/salt/electrum/configure.top b/salt/electrum/configure.top new file mode 100644 index 0000000..9efeaf8 --- /dev/null +++ b/salt/electrum/configure.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'electrum': + - match: nodegroup + - electrum.configure diff --git a/salt/electrum/create.sls b/salt/electrum/create.sls new file mode 100644 index 0000000..5b3f310 --- /dev/null +++ b/salt/electrum/create.sls @@ -0,0 +1,82 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{%- from "qvm/template.jinja" import load -%} + +{%- import "whonix/template.jinja" as whonix -%} + +include: + - .clone + - whonix.create + +{% load_yaml as defaults -%} +name: tpl-{{ slsdotpath }} +force: True +require: +- sls: {{ slsdotpath }}.clone +prefs: +- audiovm: "" +features: +- set: + - default-menu-items: "qubes-run-terminal.desktop qubes-start.desktop electrum.desktop" +{%- endload %} +{{ load(defaults) }} + +{% load_yaml as defaults -%} +name: {{ slsdotpath }}-cold +force: True +require: +- sls: {{ slsdotpath }}.clone +present: +- template: tpl-{{ slsdotpath }} +- label: gray +prefs: +- template: tpl-{{ slsdotpath }} +- label: gray +- netvm: "" +- audiovm: "" +- vcpus: 1 +- memory: 400 +- maxmem: 600 +- autostart: False +- include_in_backups: True +features: +- disable: + - service.cups + - service.cups-browsed +- set: + - menu-items: "qubes-run-terminal.desktop qubes-start.desktop electrum.desktop" +{%- endload %} +{{ load(defaults) }} + +{% load_yaml as defaults -%} +name: {{ slsdotpath }}-hot +force: True +require: +- sls: {{ slsdotpath }}.clone +present: +- template: {{ whonix.whonix_workstation_template }} +- label: orange +prefs: +- template: {{ whonix.whonix_workstation_template }} +- label: orange +- audiovm: "" +- vcpus: 1 +- memory: 400 +- maxmem: 600 +- autostart: False +- include_in_backups: True +tags: +- add: + - anon-vm +features: +- disable: + - service.cups + - service.cups-browsed +- set: + - menu-items: "qubes-run-terminal.desktop qubes-start.desktop qubes-open-file-manager.desktop electrum.desktop" +{%- endload %} +{{ load(defaults) }} diff --git a/salt/electrum/create.top b/salt/electrum/create.top new file mode 100644 index 0000000..99261f9 --- /dev/null +++ b/salt/electrum/create.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - electrum.create diff --git a/salt/electrum/init.top b/salt/electrum/init.top new file mode 100644 index 0000000..44533f8 --- /dev/null +++ b/salt/electrum/init.top @@ -0,0 +1,14 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - electrum.create + 'tpl-electrum': + - electrum.install + 'electrum-cold,electrum-hot': + - electrum.configure diff --git a/salt/electrum/install.sls b/salt/electrum/install.sls new file mode 100644 index 0000000..ca8a2c9 --- /dev/null +++ b/salt/electrum/install.sls @@ -0,0 +1,27 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - dev.home-cleanup + - dotfiles.copy-sh + - dotfiles.copy-x11 + +"{{ slsdotpath }}-updated": + pkg.uptodate: + - refresh: True + +"{{ slsdotpath }}-installed": + pkg.installed: + - refresh: True + - install_recommends: False + - skip_suggestions: True + - pkgs: + - electrum + - python3-pyqt5 + +{% endif -%} diff --git a/salt/electrum/install.top b/salt/electrum/install.top new file mode 100644 index 0000000..e0631ab --- /dev/null +++ b/salt/electrum/install.top @@ -0,0 +1,9 @@ +{# +SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'tpl-electrum': + - electrum.install