diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 8023d7d..54583bc 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -77,3 +77,5 @@ pre-commit run ## Where to start See open issues and search for the word `TODO` in the repository files. + +If you want to understand how Qusal uses Salt features, read our [Salt guide](SALT.md). diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 7d5a458..053aea0 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -18,8 +18,8 @@ Qusal design document. ## Goal Provide a minimal modular isolated environment for users to complete daily -tasks in a secure manner. We should not focus on a specific Qubes OS user base -as it would narrow our reach. We scope to have a diverse user base, with +tasks in a secure manner. We should not focus on a specific Qubes OS user +base as it would narrow our reach. We scope to have a diverse user base, with different needs and use case that could shape our project for the better. We must not aim to be a one solution fits all by adding every new project @@ -36,7 +36,7 @@ requirements are low, it is capped to a low limit, thus avoiding exacerbated memory consumption on systems with low specs. No extraneous features should be included by default besides the basic for -functionality. Extra functionalities that could weak the system can be +functionality. Extra functionalities that could weaken the system can be provided via extra states that needs to be installed per the user discretion. ## Format diff --git a/docs/SALT.md b/docs/SALT.md new file mode 100644 index 0000000..86c5faf --- /dev/null +++ b/docs/SALT.md @@ -0,0 +1,239 @@ +# Salt + +Qusal SaltStack development guide. + +## Table of Contents + +* [What this guide is](#what-this-guide-is) +* [Resources](#resources) +* [Minion Configuration](#minion-configuration) +* [Jinja](#jinja) +* [Targetting Minions](#targetting-minions) +* [Idempotence](#idempotence) +* [Examples](#examples) + +## What this guide is + +This guide is an introduction to the use of SaltStack in Qusal. It is in no +way anything other than that, it is not a beginner's instructional guide to +the use of Salt in Qubes OS or any other project, although it may share some +similarities. It is not meant to substitute our other guidelines, +[contribution](CONTRIBUTING.md) and [design](DESIGN.md) rules still apply. + +If you just want to understand SaltStack, upstream provides an +[excellent tutorial](https://docs.saltproject.io/en/getstarted/system/index.html), +don't worry if you don't understand everything, Qubes OS SaltStack integration +abstracts the client to server communication, only remaining to you to [learn +how to write states](https://docs.saltproject.io/en/getstarted/config/index.html) +and [how to correctly apply them](https://docs.saltproject.io/en/latest/topics/targeting/index.html), +in Qubes OS case, install of running salt-call directly, use +[qubesctl](https://www.qubes-os.org/doc/salt/#salt-configuration-qubesos-layout). + +## Resources + +We follow all of the rules dictated by [Salt Best Practices](https://docs.saltproject.io/en/latest/topics/best_practices.html) +documentation. We must not write the code to only be read by the code authors, +but to anyone that understands Salt. Bugs are common in code that is obscure +and takes effort to read, aim for clarity and modularity, use a `files` +directory to place files that are going to be applied to the minions. All +other rules also apply. + +A list of Salt's [Execution Modules](https://docs.saltproject.io/en/latest/ref/modules/all/) +and [State Modules](https://docs.saltproject.io/en/latest/ref/states/all/) are +good indexes to find the desired module, but you may not recognize the module +that you need this way, you may need to find examples in our code or available +in the internet. + +Qubes OS provides [qvm State Modules](https://github.com/QubesOS/qubes-mgmt-salt-dom0-qvm/blob/main/README.rst), +we are using it to manage qubes properties. + +## Minion Configuration + +We chose to set [some minion configuration](../minion.d/), such as +[file_roots](https://docs.saltproject.io/en/latest/ref/configuration/minion.html#file-roots) +for us to own the directory, in other words, we expect only our project files +to be deployed in the configured minion root directory, we can clean it on an +update without worrying of deleting user settings. + +Note that on Qubes OS, every qube is a minion, including dom0. + +## Jinja + +[Jinja](https://jinja.palletsprojects.com/) is the [default templating language](https://docs.saltproject.io/en/latest/topics/jinja/index.html#include-and-import) +in SLS files. + +We use [Jinja includes and imports](../salt/debian-minimal/template.jinja) and +[Jinja macros](../salt/utils/macros) to share reusable state +configuration, thus avoiding code duplication, allowing us to apply a state +always the same way between files in the same or different projects. + +## Targetting Minions + +You can target minions in two ways, with a [top file](https://docs.saltproject.io/en/latest/ref/states/top.html) +or specifying the states on the command-line. + +We use [top files](../salt/sys-git/init.top) to be able to execute a state in +multiple qubes in a single call, with the powers of advanced minion +targetting, we can [match properties of a qube](../salt/debian/install.top) to +apply the state depending on its name, its type and many other settings, by +specifying the minion minion IDs in a list, globbing per name, PCRE matching +a minion ID and many other match types. + +## Idempotence + +We always use Salt modules when possible, even for simple tasks such as +[creating a directory](../salt/sys-git/install.sls). Not all systems supports +the same `mkdir` options, we delegate this task to the Salt module +[file.directory](https://docs.saltproject.io/en/latest/ref/states/all/salt.states.file.html#salt.states.file.directory), +it them becomes responsible to find out how to apply the desired state to the +wanted directory. + +Specific modules are preferred over the [cmd +module](https://docs.saltproject.io/en/latest/ref/states/all/salt.states.cmd.html) +as they only apply the changes that are necessary, skipping what is already in +the desired state and provide a human and machine-readable output of what has +been done, changed or not. + +The `cmd` state might still be needed in some circumstances: + +- When Qubes OS does not provide a module; +- When SaltStack does provide a module; and +- When SaltStack module does not meet all requirements. + +## Examples + +If you have followed along until now, you are ready to start experimenting. + +Let's create a qube to hold our private keys and passwords. Create the +following SLS files. The contents can be copied from the below example. Please +make sure to install Qusal before, it is required to create the base +templates, do Jinja imports and run Jinja macros. + +`create-keys.sls`: +```salt +{# Use Qubes OS Jinja Template to create qubes using 'qvm.vm' #} +{% from "qvm/template.jinja" import load %} + +{# From our Jinja template clone-template, import 'clone_template' macro #} +{% from 'utils/macros/clone-template.sls' import clone_template -%} +{# Run the 'clone_template' macro to clone 'debian-minimal' to 'tpl-keys' #} +{{ clone_template('debian-minimal', 'keys') }} + +{# Load the following block as an YAML to the 'defaults' variable #} +{% load_yaml as defaults -%} +name: keys +{# Enforce qube settings #} +force: True +{# Only run this state if the requirements are executed succesfully #} +require: + {# Ensure succesfull 'qvm.clone' run to create 'tpl-keys-clone' + This module was executed in the 'clone_template' macro #} + - qvm: tpl-keys-clone +{# If qube does not exist, create it with the specified settings #} +present: + {# Set it to an updated Debian version #} + - template: tpl-keys + {# We must assign a high trust to this qube, it holds important data #} + - label: black +{# Set qube preferences after it was created with the 'present' state #} +prefs: + {# Enforce qube template after it was created #} + - template: tpl-keys + {# Enforce qube label after it was created #} + - label: black + {# Audio is not necessary for a keys qube, remove it #} + - audiovm: "" + {# Networking is not necessary for a keys qube, remove it #} + - netvm: "" + {# We want it backed up by default #} + - include_in_backups: True +{# Set qube features after it was created with the 'present' state #} +features: + {# Disable unwanted features #} + - disable: + {# Printing is not necessary for a keys qube, remove it #} + - service.cups + {# Set feature values, useful for string values #} + - set: + {# Help GUI users find useful applications for this qube #} + - menu-items: "org.keepassxc.KeepPassXC.desktop qubes-open-file-manager.desktop qubes-run-terminal.desktop qubes-start.desktop" +{# Stop loading to the 'defaults' variable #} +{% endload %} +{# Run the 'load' macro of 'qvm/template.jinja' with the value 'defaults' #} +{{ load(defaults) }} +``` + +`install-keys.sls`: +```salt +{# Avoid applying the state by mistake to dom0 #} +{% if grains['nodename'] != 'dom0' %} + +{# Always update the package list before trying to install any package #} +keys-updated: + pkg.uptodate: + - refresh: True + +{# Install packages using Salt's pkg.installed module #} +keys-installed: + pkg.installed: + - refresh: True + {# Enforce that we don't want to install recommended packages #} + - install_recommends: False + {# Enforce that we don't want to install suggested packages #} + - skip_suggestions: True + {# List of packages to be installed #} + - pkgs: + {# Wait, some package names do not match on different distributions #} + - keepassxc + - gnupg2 + +{# The package name can be specified for different OSes depending on grains #} +{% set pkg = { + 'Debian': { + 'pkg': ['sq', 'openssh-client'], + }, + 'RedHat': { + 'pkg': ['sequoia-sq', 'openssh-clients'], + }, +}.get(grains.os_family) %} + +{# Install the packages specific to the OS that this state is being applied #} +keys-installed-os-specific: + pkg.installed: + - refresh: True + - install_recommends: False + - skip_suggestions: True + {# Get the Jinja variable 'pkg.pkg' and convert it to an YAML list #} + - pkgs: {{ pkg.pkg|sequence|yaml }} + +{# End our 'if' statement created above #} +{% endif %} +``` + +`appmenus-keys.sls`: +```salt +{# From our Jinja template sync-appmenus, import 'sync_appmenus' macro #} +{% from 'utils/macros/sync-appmenus.sls' import sync_appmenus %} +{# Run the 'sync_appmenus' macro to synchronize the application list #} +{{ sync_appmenus('tpl-keys') }} +``` + +After you have created the states above, copy them to Dom0 in `/srv/salt`. + +Create the qube: +```sh +qubesctl state.apply create-keys +``` + +Install packages in the qube template: +```sh +qubesctl --skip-dom0 --targets=tpl-keys state.apply install-keys +``` + +Make the application menus appear after the requirements are installed: +```sh +qubesctl state.apply appmenus-keys +``` + +Congratulations, you have applied you first desired state with the benefit of +Qusal macros. The above examples are based on our [vault formula](../salt/vault).