diff --git a/.qubesbuilder b/.qubesbuilder index f4c708d..85100d6 100644 --- a/.qubesbuilder +++ b/.qubesbuilder @@ -28,6 +28,7 @@ host: - rpm_spec/qusal-mirage-builder.spec - rpm_spec/qusal-opentofu.spec - rpm_spec/qusal-qubes-builder.spec + - rpm_spec/qusal-qubes-dev.spec - rpm_spec/qusal-reader.spec - rpm_spec/qusal-remmina.spec - rpm_spec/qusal-signal.spec diff --git a/rpm_spec/qusal-qubes-dev.spec b/rpm_spec/qusal-qubes-dev.spec new file mode 100644 index 0000000..c17d70d --- /dev/null +++ b/rpm_spec/qusal-qubes-dev.spec @@ -0,0 +1,118 @@ +# SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +%define project qubes-dev +%define license_csv AGPL-3.0-or-later +## Reproducibility. +%define source_date_epoch_from_changelog 1 +%define use_source_date_epoch_as_buildtime 1 +%define clamp_mtime_to_source_date_epoch 1 +## Changelog is trimmed according to current date, not last date from changelog. +%define _changelog_trimtime 0 +%define _changelog_trimage 0 +%global _buildhost %{name} +## Python bytecode interferes when updates occur and restart is not done. +%undefine __brp_python_bytecompile + +Name: qusal-qubes-dev +Version: 0.0.1 +Release: 1%{?dist} +Summary: Development environment for Qubes OS +Group: qusal +Packager: %{?_packager}%{!?_packager:Ben Grande } +Vendor: Ben Grande +License: AGPL-3.0-or-later +URL: https://github.com/ben-grande/qusal +BugURL: https://github.com/ben-grande/qusal/issues +Source0: %{name}-%{version}.tar.gz +BuildArch: noarch + +Requires: qubes-mgmt-salt +Requires: qubes-mgmt-salt-dom0 +Requires: qusal-dev +Requires: qusal-dotfiles +Requires: qusal-sys-net +Requires: qusal-utils + + +%description +Setup a development qube named "qubes-dev", dedicated to contributing to Qubes +OS repositories. As there there is a very broad set of repositories, only +common packages will be installed. The qube has netvm but can reach remote +servers if the policy allows. + +%prep +%setup -q + +%build + +%check + +%pre + +%install +rm -rf -- %{buildroot} +install -m 755 -d -- \ + %{buildroot}/srv/salt/qusal \ + %{buildroot}%{_docdir}/%{name} \ + %{buildroot}%{_defaultlicensedir}/%{name} + +for license in $(printf '%s\n' "%{license_csv}" | tr "," " "); do + license_dir="LICENSES" + if test -d "salt/%{project}/LICENSES"; then + license_dir="salt/%{project}/LICENSES" + fi + install -m 644 -- \ + "${license_dir}/${license}.txt" %{buildroot}%{_defaultlicensedir}/%{name}/ +done + +install -m 644 -- salt/%{project}/README.md %{buildroot}%{_docdir}/%{name}/ +rm -rf -- \ + salt/%{project}/LICENSES \ + salt/%{project}/README.md \ + salt/%{project}/.* +cp -rv -- salt/%{project} %{buildroot}/srv/salt/qusal/%{name} + +%post +if test "$1" = "1"; then + ## Install + qubesctl state.apply qubes-dev.create + qubesctl --skip-dom0 --targets=tpl-qubes-dev state.apply qubes-dev.install + qubesctl --skip-dom0 --targets=dvm-qubes-dev state.apply qubes-dev.configure-dvm + qubesctl --skip-dom0 --targets=qubes-dev state.apply qubes-dev.configure + if test -n "${proxy_target}"; then + sudo qubesctl --skip-dom0 --targets="${proxy_target}" state.apply sys-net.install-proxy +elif test "$1" = "2"; then + ## Upgrade + true +fi + +%preun +if test "$1" = "0"; then + ## Uninstall + true +elif test "$1" = "1"; then + ## Upgrade + true +fi + +%postun +if test "$1" = "0"; then + ## Uninstall + true +elif test "$1" = "1"; then + ## Upgrade + true +fi + +%files +%defattr(-,root,root,-) +%license %{_defaultlicensedir}/%{name}/* +%doc %{_docdir}/%{name}/README.md +%dir /srv/salt/qusal/%{name} +/srv/salt/qusal/%{name}/* +%dnl TODO: missing '%ghost', files generated during %post, such as Qrexec policies. + +%changelog + diff --git a/salt/dev/install-common.sls b/salt/dev/install-common.sls new file mode 100644 index 0000000..295bccf --- /dev/null +++ b/salt/dev/install-common.sls @@ -0,0 +1,64 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - utils.tools.common.update + - {{ slsdotpath }}.home-cleanup + - dotfiles.copy-all + - utils.tools.zsh + - sys-pgp.install-client + - sys-git.install-client + - sys-ssh-agent.install-client + +"{{ slsdotpath }}-installed-common": + pkg.installed: + - require: + - sls: utils.tools.common.update + - install_recommends: False + - skip_suggestions: True + - setopt: "install_weak_deps=False" + - pkgs: + ## Necessary + - qubes-core-agent-passwordless-root + - ca-certificates + ## Usability + - tmux + - xclip + - bash-completion + ## Reading documentation + - man-db + - info + - texinfo + ## Searching files + - file + - tree + - ripgrep + - fzf + ## Lint + - gitlint + +## Fedora doesn't have: ruby-mdl (markdownlint, mdl) +{% set pkg = { + 'Debian': { + 'pkg': ['shellcheck', 'vim-nox', 'fd-find', 'ruby-mdl'], + }, + 'RedHat': { + 'pkg': ['ShellCheck', 'vim-enhanced', 'fd-find', 'passwd'], + }, +}.get(grains.os_family) -%} + +"{{ slsdotpath }}-installed-os-specific-common": + pkg.installed: + - require: + - sls: utils.tools.common.update + - install_recommends: False + - skip_suggestions: True + - setopt: "install_weak_deps=False" + - pkgs: {{ pkg.pkg|sequence|yaml }} + +{% endif -%} diff --git a/salt/dev/install-common.top b/salt/dev/install-common.top new file mode 100644 index 0000000..0123493 --- /dev/null +++ b/salt/dev/install-common.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'tpl-dev': + - match: list + - dev.install-common diff --git a/salt/dev/install-qusal.sls b/salt/dev/install-qusal.sls new file mode 100644 index 0000000..ac14dda --- /dev/null +++ b/salt/dev/install-qusal.sls @@ -0,0 +1,45 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - dev.install-common + - dev.install-python + +"{{ slsdotpath }}-installed-qusal": + pkg.installed: + - require: + - sls: utils.tools.common.update + - install_recommends: False + - skip_suggestions: True + - setopt: "install_weak_deps=False" + - pkgs: + - yamllint + - codespell + - pre-commit + - reuse + +## Debian doesn't have: salt-lint +{% set pkg = { + 'Debian': { + 'pkg': [], + }, + 'RedHat': { + 'pkg': ['salt-lint'], + }, +}.get(grains.os_family) -%} + +"{{ slsdotpath }}-installed-os-specific-qusal": + pkg.installed: + - require: + - sls: utils.tools.common.update + - install_recommends: False + - skip_suggestions: True + - setopt: "install_weak_deps=False" + - pkgs: {{ pkg.pkg|sequence|yaml }} + +{% endif -%} diff --git a/salt/dev/install-qusal.top b/salt/dev/install-qusal.top new file mode 100644 index 0000000..962c8cc --- /dev/null +++ b/salt/dev/install-qusal.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'tpl-dev': + - match: list + - dev.install-qusal diff --git a/salt/dev/install.sls b/salt/dev/install.sls index 5392541..472c63d 100644 --- a/salt/dev/install.sls +++ b/salt/dev/install.sls @@ -1,5 +1,5 @@ {# -SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. SPDX-License-Identifier: AGPL-3.0-or-later #} @@ -7,66 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later {% if grains['nodename'] != 'dom0' -%} include: - - utils.tools.common.update - - {{ slsdotpath }}.home-cleanup - - dotfiles.copy-all - - utils.tools.zsh - - sys-pgp.install-client - - sys-git.install-client - - sys-ssh-agent.install-client - -"{{ slsdotpath }}-installed": - pkg.installed: - - require: - - sls: utils.tools.common.update - - install_recommends: False - - skip_suggestions: True - - setopt: "install_weak_deps=False" - - pkgs: - ## Necessary - - qubes-core-agent-passwordless-root - - ca-certificates - ## Usability - - tmux - - xclip - - bash-completion - ## Reading documentation - - man-db - - info - - texinfo - ## Searching files - - file - - tree - - ripgrep - - fzf - ## Lint - - pre-commit - - precious - - reuse - - gitlint - - pylint - - yamllint - - ruby-mdl - - codespell - -## Fedora doesn't have: ruby-mdl (markdownlint, mdl) -## Debian doesn't have: salt-lint -{% set pkg = { - 'Debian': { - 'pkg': ['shellcheck', 'vim-nox', 'fd-find'], - }, - 'RedHat': { - 'pkg': ['ShellCheck', 'vim-enhanced', 'fd-find', 'salt-lint', 'passwd'], - }, -}.get(grains.os_family) -%} - -"{{ slsdotpath }}-installed-os-specific": - pkg.installed: - - require: - - sls: utils.tools.common.update - - install_recommends: False - - skip_suggestions: True - - setopt: "install_weak_deps=False" - - pkgs: {{ pkg.pkg|sequence|yaml }} + - dev.install-qusal {% endif -%} diff --git a/salt/qubes-dev/README.md b/salt/qubes-dev/README.md new file mode 100644 index 0000000..d7ebfd1 --- /dev/null +++ b/salt/qubes-dev/README.md @@ -0,0 +1,83 @@ +# qubes-dev + +Development environment for Qubes OS. + +## Table of Contents + +* [Description](#description) +* [Installation](#installation) +* [Access Control](#access-control) +* [Usage](#usage) + +## Description + +Setup a development qube named "qubes-dev", dedicated to contributing to Qubes +OS repositories. As there there is a very broad set of repositories, only +common packages will be installed. The qube has netvm but can reach remote +servers if the policy allows. + +## Installation + +* Top: + +```sh +sudo qubesctl top.enable qubes-dev +sudo qubesctl --targets=tpl-qubes-dev,dvm-qubes-dev,qubes-dev state.apply +sudo qubesctl top.disable qubes-dev +proxy_target="$(qusal-report-updatevm-origin)" +if test -n "${proxy_target}"; then + sudo qubesctl --skip-dom0 --targets="${proxy_target}" state.apply sys-net.install-proxy +fi +``` + +* State: + + + +```sh +sudo qubesctl state.apply qubes-dev.create +sudo qubesctl --skip-dom0 --targets=tpl-qubes-dev state.apply qubes-dev.install +sudo qubesctl --skip-dom0 --targets=dvm-qubes-dev state.apply qubes-dev.configure-dvm +sudo qubesctl --skip-dom0 --targets=qubes-dev state.apply qubes-dev.configure +proxy_target="$(qusal-report-updatevm-origin)" +if test -n "${proxy_target}"; then + sudo qubesctl --skip-dom0 --targets="${proxy_target}" state.apply sys-net.install-proxy +fi +``` + + + +The installation will make the Qusal TCP Proxy available in the `updatevm` +(after it is restarted in case it is template based). If you want to have the +proxy available on a `netvm` that is not deployed by Qusal, install the Qusal +TCP proxy on the templates of your `netvm`: + +```sh +sudo qubesctl --skip-dom0 --targets=TEMPLATE state.apply sys-net.install-proxy +``` + +Remember to restart the `netvms` after the proxy installation for the changes +to take effect. + +## Access Control + +_Default policy_: `denies` `all` qubes from calling `qusal.ConnectTCP` + +Allow qube `qubes-dev` to `connect` to `github.com:22` via `disp-sys-net` but +not to any other host or via any other qube: + +```qrexecpolicy +qusal.ConnectTCP +github.com+22 qubes-dev @default allow target=disp-sys-net +qusal.ConnectTCP * qubes-dev @anyvm deny +``` + +## Usage + +The development qube `qubes-dev` can be used for: + +* everything the [dev](../dev/README.md) qube can do; +* contributing to Qubes OS + +As the `qubes-dev` qube has no netvm, configure the Qrexec policy to allow or +ask calls to the `qusal.ConnectTCP` RPC service, so the qube can communicate +with a remote repository for example. diff --git a/salt/qubes-dev/clone.sls b/salt/qubes-dev/clone.sls new file mode 100644 index 0000000..5c7bd65 --- /dev/null +++ b/salt/qubes-dev/clone.sls @@ -0,0 +1,8 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% from 'utils/macros/clone-template.sls' import clone_template -%} +{{ clone_template('fedora-minimal', sls_path) }} diff --git a/salt/qubes-dev/clone.top b/salt/qubes-dev/clone.top new file mode 100644 index 0000000..e97d26a --- /dev/null +++ b/salt/qubes-dev/clone.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - qubes-dev.clone diff --git a/salt/qubes-dev/configure-dvm.sls b/salt/qubes-dev/configure-dvm.sls new file mode 100644 index 0000000..ac947b9 --- /dev/null +++ b/salt/qubes-dev/configure-dvm.sls @@ -0,0 +1,12 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - utils.tools.zsh.touch-zshrc + +{% endif -%} diff --git a/salt/qubes-dev/configure-dvm.top b/salt/qubes-dev/configure-dvm.top new file mode 100644 index 0000000..182897c --- /dev/null +++ b/salt/qubes-dev/configure-dvm.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + '*': + - match: nodegroup + - qubes-dev.configure-dvm diff --git a/salt/qubes-dev/configure.sls b/salt/qubes-dev/configure.sls new file mode 100644 index 0000000..c5c1b57 --- /dev/null +++ b/salt/qubes-dev/configure.sls @@ -0,0 +1,13 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - {{ slsdotpath }}.home-cleanup + - dotfiles.copy-all + +{% endif -%} diff --git a/salt/qubes-dev/configure.top b/salt/qubes-dev/configure.top new file mode 100644 index 0000000..b0b4cbf --- /dev/null +++ b/salt/qubes-dev/configure.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + '*': + - match: nodegroup + - qubes-dev.configure diff --git a/salt/qubes-dev/create.sls b/salt/qubes-dev/create.sls new file mode 100644 index 0000000..51ce958 --- /dev/null +++ b/salt/qubes-dev/create.sls @@ -0,0 +1,103 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{%- from "qvm/template.jinja" import load -%} + +include: + - {{ slsdotpath }}.clone + - sys-net.show-updatevm-origin + +{% load_yaml as defaults -%} +name: tpl-{{ slsdotpath }} +force: True +require: +- sls: {{ slsdotpath }}.clone +prefs: +- audiovm: "" +{%- endload %} +{{ load(defaults) }} + +{% load_yaml as defaults -%} +name: {{ slsdotpath }} +force: True +require: +- sls: {{ slsdotpath }}.clone +present: +- template: tpl-{{ slsdotpath }} +- label: purple +prefs: +- template: tpl-{{ slsdotpath }} +- label: purple +- netvm: "" +- audiovm: "" +- vcpus: 1 +- memory: 400 +- maxmem: 600 +- autostart: False +- include_in_backups: True +features: +- enable: + - service.split-gpg2-client + - service.qusal-proxy-client + - service.crond +- disable: + - service.cups + - service.cups-browsed +{%- endload %} +{{ load(defaults) }} + +{% load_yaml as defaults -%} +name: dvm-{{ slsdotpath }} +force: True +require: +- sls: {{ slsdotpath }}.clone +present: +- template: tpl-{{ slsdotpath }} +- label: red +prefs: +- template: tpl-{{ slsdotpath }} +- label: red +- audiovm: "" +- vcpus: 1 +- memory: 400 +- maxmem: 600 +- autostart: False +- template_for_dispvms: True +- include_in_backups: False +features: +- enable: + - appmenus-dispvm +- disable: + - service.cups + - service.cups-browsed +{%- endload %} +{{ load(defaults) }} + +{% load_yaml as defaults -%} +name: disp-{{ slsdotpath }} +force: True +require: +- qvm: dvm-{{ slsdotpath }} +present: +- template: dvm-{{ slsdotpath }} +- label: red +- class: DispVM +prefs: +- template: dvm-{{ slsdotpath }} +- label: red +- audiovm: "" +- vcpus: 1 +- memory: 400 +- maxmem: 600 +- autostart: False +- include_in_backups: False +features: +- disable: + - appmenus-dispvm + - service.cups + - service.cups-browsed +{%- endload %} +{{ load(defaults) }} diff --git a/salt/qubes-dev/create.top b/salt/qubes-dev/create.top new file mode 100644 index 0000000..c7a3c85 --- /dev/null +++ b/salt/qubes-dev/create.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - qubes-dev.create diff --git a/salt/qubes-dev/init.top b/salt/qubes-dev/init.top new file mode 100644 index 0000000..746fdf0 --- /dev/null +++ b/salt/qubes-dev/init.top @@ -0,0 +1,19 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'dom0': + - match: nodegroup + - qubes-dev.create + 'tpl-qubes-dev': + - qubes-dev.install + 'dvm-qubes-dev': + - qubes-dev.configure-dvm + 'qubes-dev': + - qubes-dev.configure + '(I@qubes:type:template or I@qubes:type:standalone) and (G@kernel:Linux or G@kernel:*BSD)': + - match: compound + - sys-net.install-proxy diff --git a/salt/qubes-dev/install.sls b/salt/qubes-dev/install.sls new file mode 100644 index 0000000..0b9cf1e --- /dev/null +++ b/salt/qubes-dev/install.sls @@ -0,0 +1,25 @@ +{# +SPDX-FileCopyrightText: 2023 - 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +{% if grains['nodename'] != 'dom0' -%} + +include: + - dev.install-common + - dev.install-python + +"{{ slsdotpath }}-installed": + pkg.installed: + - require: + - sls: utils.tools.common.update + - install_recommends: False + - skip_suggestions: True + - setopt: "install_weak_deps=False" + - pkgs: + - glade + - qt6-designer + # TODO: reboot vm and test missing packages on tests + +{% endif -%} diff --git a/salt/qubes-dev/install.top b/salt/qubes-dev/install.top new file mode 100644 index 0000000..7157580 --- /dev/null +++ b/salt/qubes-dev/install.top @@ -0,0 +1,10 @@ +{# +SPDX-FileCopyrightText: 2025 Benjamin Grande M. S. + +SPDX-License-Identifier: AGPL-3.0-or-later +#} + +base: + 'tpl-qubes-dev': + - match: list + - qubes-dev.install diff --git a/salt/qubes-dev/version b/salt/qubes-dev/version new file mode 100644 index 0000000..8acdd82 --- /dev/null +++ b/salt/qubes-dev/version @@ -0,0 +1 @@ +0.0.1