diff --git a/README.md b/README.md index e403e02..c5b2e14 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ User Salt States for Qubes OS ============================= -A collection of user Salt states for Qubes OS. +A collection of user Salt formulas for Qubes OS. - **Split-SSH** ([sources][split-ssh-src], [packaging][split-ssh-pkg]) - [split-ssh-src]: ./states/split-ssh/src - [split-ssh-pkg]: ./states/split-ssh + [split-ssh-src]: ./packages/split-ssh/src + [split-ssh-pkg]: ./packages/split-ssh Usage ----- ### Prerequisites -Enable the Salt [user directories][qubes-user-dirs] if you haven't already. This allows to install Salt states without mixing them with the `base` states that are maintained by the Qubes OS team. +Enable the Salt [user directories][qubes-user-dirs] if you haven't already. This allows to install Salt formulas without mixing them with the `base` formulas that are maintained by the Qubes OS team. ```sh sudo qubesctl top.enable qubes.user-dirs @@ -29,11 +29,11 @@ sudo qubesctl state.apply ### Step-by-step -This repository contains multiple states and it is not required to use them all. For each of them, the same enabling steps apply, and `split-ssh` will be used as an example. +This repository contains multiple formulas and it is not required to use them all. For each of them, the same enabling steps apply, and `split-ssh` will be used as an example. -#### Install the state +#### Install the formula -In order to be able to use a state, its definition should be present in the `/srv/user_salt/` directory of _dom0_. +In order to be able to use a formula, the state definition should be present in the `/srv/user_salt/` directory of _dom0_, and the pillar definition in `/srv/user_pillar`. > ⚠ **Security warning**: Since any domain is _less trusted_ than _dom0_ (by definition), copying anything into _dom0_ requires extreme caution. See [References](#references) for details, and use own judgement. @@ -43,11 +43,15 @@ In order to be able to use a state, its definition should be present in the `/sr [secure-update]: https://www.qubes-os.org/doc/dom0-secure-updates -No matter how you decide to perform this step, the end result should be a directory containing one or more `.top` files (and the corresponding `.sls` and other files): +No matter how you decide to perform this step, the end result should be a directory containing one or more `.top` files (and the corresponding `.sls` and other files), as well as a directory containing a `config.yaml` file: ```sh -/srv/user_salt/split-ssh +/srv/user_pillar/split-ssh # contains the configuration +/srv/user_salt/split-ssh # contains the .top files ``` +#### Adjust the configuration to fit your needs + +The state will be defined by the configuration stored in the pillar. In our example, you can find the default configuration in `/srv/user_pillar/split-ssh/config.yaml` and modify it to fit your needs. #### Enable the state @@ -91,7 +95,7 @@ sudo qubesctl --all state.apply ``` Each state targets one or more qubes, and if you know which qubes you're modifying you can save some time by targetting them specifically. -For example, the `split-ssh` state targets `dom0` and the `fedora-32` template, as well as two qubes called `ssh-vault` and `ssh-client`: +For example, the `split-ssh` state targets `dom0` by default, as well as the `fedora-32` template, and two qubes called `ssh-vault` and `ssh-client`: ```sh sudo qubesctl --targets=fedora-32,ssh-client,ssh-vault state.apply @@ -100,7 +104,7 @@ sudo qubesctl --targets=fedora-32,ssh-client,ssh-vault state.apply Note that _dom0_ is always implicitly targetted by `qubesctl` (and appears in the output as `local`). If you know it doesn't need to be updated, you can skip _dom0_: ```sh -sudo qubesctl --skip-dom0 --targets=fedora-32,ssh-client,ssh-vault state.apply # in this example, that would be enough if the qubes already exist +sudo qubesctl --skip-dom0 --targets=fedora-32,ssh-client,ssh-vault state.apply # in this example, that would be enough if the client and vault qubes already exist ``` References diff --git a/packages/split-ssh/src/split-ssh-formula/Makefile b/packages/split-ssh/src/split-ssh-formula/Makefile index b31a604..8c00af5 100644 --- a/packages/split-ssh/src/split-ssh-formula/Makefile +++ b/packages/split-ssh/src/split-ssh-formula/Makefile @@ -1,14 +1,35 @@ PREFIX ?= /srv/user_salt +# Assumes layout but allows modification of PREFIX +PILLAR_PREFIX ?= ${PREFIX}/../user_pillar + # The path of the state files SALT_STATE_PATH = state/ +# The path of the pillar files +SALT_PILLAR_PATH = pillar/ + .PHONY: all all: # intentionally left blank .PHONY: install -install: +install: pillar state + +.PHONY: pillar +pillar: + mkdir -p ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}client-template.sls ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}client.sls ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}clients.sls ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}config.yaml ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}init.jinja ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}vault-template.sls ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}vault.sls ${DESTDIR}${PILLAR_PREFIX}/split-ssh + cp ${SALT_PILLAR_PATH}vaults.sls ${DESTDIR}${PILLAR_PREFIX}/split-ssh + +.PHONY: state +state: mkdir -p ${DESTDIR}${PREFIX}/split-ssh cp -r ${SALT_STATE_PATH}client ${DESTDIR}${PREFIX}/split-ssh cp -r ${SALT_STATE_PATH}packages ${DESTDIR}${PREFIX}/split-ssh diff --git a/packages/split-ssh/src/split-ssh-formula/README.md b/packages/split-ssh/src/split-ssh-formula/README.md index 072b7a3..9379d09 100644 --- a/packages/split-ssh/src/split-ssh-formula/README.md +++ b/packages/split-ssh/src/split-ssh-formula/README.md @@ -1,48 +1,53 @@ Split-SSH ========= -A Salt state that enables [split-SSH][split-ssh] in Qubes OS. +A Salt formula that enables [split-SSH][split-ssh] in Qubes OS. Overview -------- -- Ensures the existence of a `ssh-vault` qube that starts automatically on system startup. This qube has no network access and holds SSH keys. -- Ensures the presence of a Qubes RPC policy in `dom0`. That policy ensures that the user is asked before any qube accesses the SSH keys contained in `ssh-vault`. -- Ensures a `ssh-client` qube exists. That qube is configured to allow usage of SSH but relies on the SSH keys contained in `ssh-vault`. +- Ensures the existence of a **vault** qube that starts automatically on system startup. This qube has no network access and holds SSH keys. +- Ensures the presence of a Qubes RPC policy in `dom0`. That policy ensures that the user is asked before any qube accesses the SSH keys contained in the **vault**. +- Ensures a **client** qube exists. That qube is configured to allow usage of SSH but relies on the SSH keys contained in the **vault**. + +By default, the **vault** is a _qube_ called `ssh-vault` and the **client** is a _qube_ called `ssh-client`, but that configuration can be changed if needeed. Installation ------------ ### Recommended installation -Build and sign an RPM package in order to install this Salt state in _dom0_. See [qubes-mgmt-salt-user-split-ssh][rpm]. +Build and sign an RPM package in order to install this Salt formula in _dom0_. See [qubes-mgmt-salt-user-split-ssh][rpm]. - [rpm]: https://github.com/gonzalo-bulnes/qubes-mgmt-salt-user/tree/main/states/split-ssh + [rpm]: https://github.com/gonzalo-bulnes/qubes-mgmt-salt-user/tree/main/packages/split-ssh ### Manual installation - Enable the Salt _user directories_ ([how-to][user-dirs-how-to]) - Copy, or type the contents of the `state/` directory into `/srv/user_salt/split-ssh/`. -- Inspect the surounding files and apply similar permissions and ownership to the `/srv/user_salt/split-ssh` directory. +- Copy, or type the contents of the `pillar/` directory into `/srv/user_pillar/split-ssh/`. +- Inspect the surounding files and apply similar permissions and ownership to the `/srv/user_salt/split-ssh` and `/srv/user_pillar/split-ssh` directories. Usage ----- +Adapt the configuration to your needs if necessary by modifying `/srv/user_pillar/split-ssh/config.yaml`. + Enable the top files: ```sh sudo qubesctl top.enable split-ssh.client split-ssh.policy split-ssh.vault ``` -Apply the state: +Apply the state (if you modified the configuration, adjust the targets accordingly): ```sh sudo qubesctl --targets=fedora-32,ssh-client,ssh-vault state.apply ``` -**Note**: the `ssh-client` and `ssh-vault` machines will be created if they don't exist. That is the point of having a Salt state! They are part of the targets because because part of the configuration applies to them. +**Note**: the configured **client** and **vault** machines will be created if they don't exist. That is the point of using a Salt formula! They are part of the targets because because part of the configuration applies to them. -Once the state is enforced, create new SSH keys in `ssh-vault` (or copy existing keys if you prefer). +Once the state is enforced, create new SSH keys in the **vault** (or copy existing keys if you prefer). References ---------- diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/client-template.sls b/packages/split-ssh/src/split-ssh-formula/pillar/client-template.sls new file mode 100644 index 0000000..14e69d6 --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/client-template.sls @@ -0,0 +1,2 @@ +split-ssh-role: + client-template: True diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/client.sls b/packages/split-ssh/src/split-ssh-formula/pillar/client.sls new file mode 100644 index 0000000..d90093d --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/client.sls @@ -0,0 +1,2 @@ +split-ssh-role: + client: True diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/clients.sls b/packages/split-ssh/src/split-ssh-formula/pillar/clients.sls new file mode 100644 index 0000000..f3e3e42 --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/clients.sls @@ -0,0 +1,7 @@ +## Load config +#{% load_yaml as config %} +#{% include 'split-ssh/config.yaml' %} +#{% endload %} +--- +split-ssh-clients: + {{ config.clients }} diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/config.yaml b/packages/split-ssh/src/split-ssh-formula/pillar/config.yaml new file mode 100644 index 0000000..e0c888d --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/config.yaml @@ -0,0 +1,17 @@ +--- +vaults: + - name: ssh-vault + template: fedora-32 + label: black + mem: 400 + vcpus: 2 + autostart: True + +clients: + - name: ssh-client + template: fedora-32 + label: blue + mem: 400 + vcpus: 2 + autostart: False + diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/init.jinja b/packages/split-ssh/src/split-ssh-formula/pillar/init.jinja new file mode 100644 index 0000000..27c93ce --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/init.jinja @@ -0,0 +1,60 @@ +## Load config +#{% load_yaml as config %} +#{% include 'split-ssh/config.yaml' %} +#{% endload %} +# +## Collect information about the client(s) +#{% set client_names = [] %} +#{% set client_templates = [] %} +# +#{% for client in config.clients %} +#{{ client_names.append(client.name) }} +#{{ client_templates.append(client.template) }} +#{% endfor %} +# +## Collect information about the vault(s) +#{% set vault_names = [] %} +#{% set vault_templates = [] %} +# +#{% for vault in config.vaults %} +#{{ vault_names.append(vault.name) }} +#{{ vault_templates.append(vault.template) }} +#{% endfor %} +# +## Deduplicate and sort the lists to allow comparisons +## and ensure consistency +#{% set client_names = client_names|unique|sort %} +#{% set client_templates = client_templates|unique|sort %} +#{% set vault_names = vault_names|unique|sort %} +#{% set vault_templates = vault_templates|unique|sort %} +--- +user: + dom0: + - split-ssh.clients + - split-ssh.vaults + + {# Prevent duplicate keys error when client and vault templates are the same #} + {% if client_templates == vault_templates %} + {{ client_templates|join(',') }}: + - match: list + - split-ssh.client-template + - split-ssh.vault-template + {% else %} + {{ client_templates|join(',') }}: + - match: list + - split-ssh.client-template + + {{ vault_templates|join(',') }}: + - match: list + - split-ssh.vault-template + {% endif %} + + {# The clients and vaults themselves won't be the same qubes (split-SSH) #} + + {{ client_names|join(',') }}: + - match: list + - split-ssh.client + + {{ vault_names|join(',') }}: + - match: list + - split-ssh.vault diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/vault-template.sls b/packages/split-ssh/src/split-ssh-formula/pillar/vault-template.sls new file mode 100644 index 0000000..5bccbb3 --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/vault-template.sls @@ -0,0 +1,2 @@ +split-ssh-role: + vault-template: True diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/vault.sls b/packages/split-ssh/src/split-ssh-formula/pillar/vault.sls new file mode 100644 index 0000000..3e80fcc --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/vault.sls @@ -0,0 +1,2 @@ +split-ssh-role: + vault: True diff --git a/packages/split-ssh/src/split-ssh-formula/pillar/vaults.sls b/packages/split-ssh/src/split-ssh-formula/pillar/vaults.sls new file mode 100644 index 0000000..216167b --- /dev/null +++ b/packages/split-ssh/src/split-ssh-formula/pillar/vaults.sls @@ -0,0 +1,7 @@ +## Load config +#{% load_yaml as config %} +#{% include 'split-ssh/config.yaml' %} +#{% endload %} +--- +split-ssh-vaults: + {{ config.vaults }} diff --git a/packages/split-ssh/src/split-ssh-formula/qubes-mgmt-salt-user-split-ssh.spec b/packages/split-ssh/src/split-ssh-formula/qubes-mgmt-salt-user-split-ssh.spec index feb6c3f..e0c51d4 100644 --- a/packages/split-ssh/src/split-ssh-formula/qubes-mgmt-salt-user-split-ssh.spec +++ b/packages/split-ssh/src/split-ssh-formula/qubes-mgmt-salt-user-split-ssh.spec @@ -25,6 +25,8 @@ make install DESTDIR=%{buildroot} %files %license LICENSE %doc README.md +%config /srv/user_pillar/config.yaml +/srv/user_pillar/split-ssh /srv/user_salt/split-ssh %changelog diff --git a/packages/split-ssh/src/split-ssh-formula/state/client.top b/packages/split-ssh/src/split-ssh-formula/state/client.top index be86c98..10bae9c 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/client.top +++ b/packages/split-ssh/src/split-ssh-formula/state/client.top @@ -2,8 +2,10 @@ user: dom0: - split-ssh.client.vm - fedora-32: + 'split-ssh-role:client-template': + - match: pillar - split-ssh.client.packages - ssh-client: + 'split-ssh-role:client': + - match: pillar - split-ssh.client.sock diff --git a/packages/split-ssh/src/split-ssh-formula/state/client/files/bashrc.d/sock b/packages/split-ssh/src/split-ssh-formula/state/client/files/bashrc.d/sock.jinja similarity index 78% rename from packages/split-ssh/src/split-ssh-formula/state/client/files/bashrc.d/sock rename to packages/split-ssh/src/split-ssh-formula/state/client/files/bashrc.d/sock.jinja index 3f7f609..0d9fdbf 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/client/files/bashrc.d/sock +++ b/packages/split-ssh/src/split-ssh-formula/state/client/files/bashrc.d/sock.jinja @@ -2,7 +2,7 @@ # Ensure the SSH socket discoverability # # See /srv/salt/split-ssh in dom0 for details. -SSH_VAULT_VM="ssh-vault" +SSH_VAULT_VM="{{ pillar.split-ssh-vaults|first }}" if [[ "$SSH_VAULT_VM" != "" ]]; then export SSH_AUTH_SOCK=~user/.SSH_AGENT_$SSH_VAULT_VM diff --git a/packages/split-ssh/src/split-ssh-formula/state/client/files/rc.local.d/sock b/packages/split-ssh/src/split-ssh-formula/state/client/files/rc.local.d/sock.jinja similarity index 85% rename from packages/split-ssh/src/split-ssh-formula/state/client/files/rc.local.d/sock rename to packages/split-ssh/src/split-ssh-formula/state/client/files/rc.local.d/sock.jinja index 283a017..384f856 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/client/files/rc.local.d/sock +++ b/packages/split-ssh/src/split-ssh-formula/state/client/files/rc.local.d/sock.jinja @@ -2,7 +2,7 @@ # Create split-SSH socket # # See /srv/salt/split-ssh in dom0 for details. -SSH_VAULT_VM="ssh-vault" +SSH_VAULT_VM="{{ pillar.split-ssh-vaults|first }}" if [[ "$SSH_VAULT_VM" != "" ]]; then export SSH_SOCK=~user/.SSH_AGENT_$SSH_VAULT_VM diff --git a/packages/split-ssh/src/split-ssh-formula/state/client/sock.sls b/packages/split-ssh/src/split-ssh-formula/state/client/sock.sls index 6bf73d0..f4e6873 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/client/sock.sls +++ b/packages/split-ssh/src/split-ssh-formula/state/client/sock.sls @@ -1,9 +1,11 @@ ssh-socket-present: file.append: - name: /rw/config/rc.local - - source: salt://split-ssh/client/files/rc.local.d/sock + - source: salt://split-ssh/client/files/rc.local.d/sock.jinja + - template: jinja ssh-socket-discoverable: file.append: - name: ~user/.bashrc - - source: salt://split-ssh/client/files/bashrc.d/sock + - source: salt://split-ssh/client/files/bashrc.d/sock.jinja + - template: jinja diff --git a/packages/split-ssh/src/split-ssh-formula/state/client/vm.sls b/packages/split-ssh/src/split-ssh-formula/state/client/vm.sls index c75ee5e..518293d 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/client/vm.sls +++ b/packages/split-ssh/src/split-ssh-formula/state/client/vm.sls @@ -1,8 +1,15 @@ -ssh-client-present: +{% for client in pillar.split-ssh-clients %} +{{ client.name }}-present: qvm.present: - - name: ssh-client - - template: fedora-32 - - label: blue - - mem: 400 - - vcpus: 2 + - name: {{ client.name }} + - template: {{ client.template }} + - label: {{ client.label }} + - mem: {{ client.mem }} + - vcpus: {{ client.vcpus }} + +{{ client.name }}-autostarts: + qvm.prefs: + - name: {{ client.name }} + - autostart: {{ client.autostart }} +{% endfor %} diff --git a/packages/split-ssh/src/split-ssh-formula/state/policy/files/qubes.SSHAgent b/packages/split-ssh/src/split-ssh-formula/state/policy/files/qubes.SSHAgent index a483bb2..11d3a4f 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/policy/files/qubes.SSHAgent +++ b/packages/split-ssh/src/split-ssh-formula/state/policy/files/qubes.SSHAgent @@ -1 +1,5 @@ -ssh-client ssh-vault ask +{% for vault in pillar.split-ssh-vault %} +{% for client in pillar.split-ssh-client %} +{{ client.name }} {{ vault.name }} ask +{% endfor %} +{% endfor %} diff --git a/packages/split-ssh/src/split-ssh-formula/state/policy/init.sls b/packages/split-ssh/src/split-ssh-formula/state/policy/init.sls index 02807eb..2ae1720 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/policy/init.sls +++ b/packages/split-ssh/src/split-ssh-formula/state/policy/init.sls @@ -5,4 +5,5 @@ - mode: '0755' - makedirs: True - source: salt://split-ssh/policy/files/qubes.SSHAgent + - template: jinja diff --git a/packages/split-ssh/src/split-ssh-formula/state/vault.top b/packages/split-ssh/src/split-ssh-formula/state/vault.top index b600905..77b8787 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/vault.top +++ b/packages/split-ssh/src/split-ssh-formula/state/vault.top @@ -2,9 +2,11 @@ user: dom0: - split-ssh.vault.vm - fedora-32: + 'split-ssh-role:vault-template': + - match: pillar - split-ssh.vault.packages - ssh-vault: + 'split-ssh-role:vault': + - match: pillar - split-ssh.vault.config - split-ssh.vault.rpc diff --git a/packages/split-ssh/src/split-ssh-formula/state/vault/vm.sls b/packages/split-ssh/src/split-ssh-formula/state/vault/vm.sls index 55abd1c..1334ef2 100644 --- a/packages/split-ssh/src/split-ssh-formula/state/vault/vm.sls +++ b/packages/split-ssh/src/split-ssh-formula/state/vault/vm.sls @@ -1,19 +1,21 @@ -ssh-vault-present: +{% for vault in pillar.split-ssh-vaults %} +{{ vault.name }}-present: qvm.present: - - name: ssh-vault - - template: fedora-32 - - label: black - - mem: 400 - - vcpus: 2 + - name: {{ vault.name }} + - template: {{ vault.template }} + - label: {{ vault.label }} + - mem: {{ vault.mem }} + - vcpus: {{ vault.vcpus }} -ssh-vault-has-no-network-access: +{{ vault.name }}-has-no-network-access: qvm.prefs: - - name: ssh-vault + - name: {{ vault.name }} - netvm: none - default_dispvm: -ssh-vault-autostarts: +{{ vault.name }}-autostarts: qvm.prefs: - - name: ssh-vault - - autostart: True + - name: {{ vault.name }} + - autostart: {{ vault.autostart }} +{% endfor %}