Link On Boot: A better way to configure software with Salt Template States?

Hi everyone,

I am monscodex, creator of saltqubes.

I have been working intermittently on the implementation of an idea for Salt Template configurations. I believe that the idea has reached a certain level of maturity, and am seeking to collaborate with other creators of Salt configurations (for Qubes Templates) to finish polishing it. I am unaware whether what I am describing has been already been developed, if so, please refer to me.

Idea

Enable a Salt Template State to change configuration files for the AppVM qubes that use those Templates. Example: in a Template template-coding that installs the neovim package, provide a way to change the configuration files located at ~/.config/nvim in the AppVMs that use that Template.

This should be highly practicality as it restores a conceptual model we usually have for the distribution of software (that has been somewhat disrupted by the separation of Templates and AppVMs in Qubes): a Linux distribution, in simple terms, provides a way to install packages, a default set of installed packages and a default configuration for those installed packages.

In practical terms, there is a problem with our Qubes Salt configurations: they often split into two states: one applied to the Template (that installs the software) and one applied to the AppVMs (that configures the software), providing extra overhead and complexity by breaking the previously described model.

Another way I have tried to unify the installation and configuration is by changing the location of the configuration (for example, setting an environment variable to tell neovim to find its configuration at a certain path). The problem with such methods is that not every software supports it.

In my opinion, the following idea should only be carried forward if it provides a safe implementation (defined below).

Main Implementation (in Salt Template configuration)

  1. Copy dotfiles into a Template directory (e.g. /usr/local/saltqubes/link_on_boot/nvim)
  2. On every boot, link the usual configuration path to its new location (e.g. /home/user/.config/nvim/usr/local/saltqubes/link_on_boot/nvim)

Voilà

Safety considerations

  1. If the source exists, create a backup of the existing content (e.g. if, in the AppVM, ~/.config/nvim already has a configuration, it should be moved to ~/.config/nvim.saltqubes.link_on_boot.previous)
  2. If the Template state is removed (e.g. I had run a Salt configuration that did the link on boot of the neovim config, and I no longer want to have a linked ~/.config/nvim), the link should be restored to its original content
  3. If the AppVM’s template is changed, it should remove the previous links. (e.g. moving the AppVM from template-coding (that links on boot a neovim configuration) to template-browser (that does not contain said configurations) should remove the

Implementation after safety considerations

The implementation is divided in the following parts (numbered in order of execution).

3. Linking

This is very similar to the principal implementation (1. the Salt state copies the files to a path in the root Template directory - 2. create a systemd system unit that links on boot), except for additional aspects:

  • Every time a link is made, the source path is added to a certain path (e.g. when linking from ~/.config/nvim to /usr/local/..., ‘/home/user/.config/nvim’ should be added to the file /home/user/.local/share/saltqubes/link_on_boot/links_from_previous_boot. It is important that this file is in the home directory as it needs to be stored in the AppVM)
  • All the linking services should depend on the unlinking service

2. Unlinking

There should be a systemd service that unlinks all the links created during the previous boot. These links are found in the /home/user/.local/share/saltqubes/link_on_boot/links_from_previous_boot file. When unlinking a path (e.g. ~/.config/nvim), if a backup file is found (e.g. ~/.config/nvim.saltqubes.link_on_boot.previous), it should be moved back to the corresponding path (e.g. ~/.config/nvim).

It is important that the systemd service is a user service stored in ~/.config/systemd, because if the Template is changed, it still needs to run on the next boot.

1. Creating the unlinking service

Because the unlinking service needs to be located in the AppVM’s home directory, it cannot be managed directly by the Salt state applied to the Template directly.

To solve this issue we can create a systemd system unit that will create and enable the unlinking service (yes, a system service that creates and enables a user service).

Case analysis

In theory, we could leave everything as is, so that on every boot:

  1. If the unlinking user service does not exist the unlinking-service-creator system service will create it
  2. The unlinking service runs and unlinks every link from the previous boot
  3. Linking for the new boot takes place

If for an AppVM (e.g. coding based on template-coding) the Template is changed to a new Template that does not contain any linking (e.g. template-browser), step 1 will not happen, step 3 will not happen either: there will only be a persistent systemd user service that will run removing the links from the previous boot (on the first boot after the Template change it will unlink previously linked files, on the following boots after that there will be nothing to unlink). No dead links, just a user service that effectively does nothing.

One could even make a final adjustment: at the end of the unlinking service script, it checks if the unlinking-service-creator exists. If it does not exist, (Template change), the service will destroy itself by disabling and removing its file, thus ensuring that there are absolutely no side effects after changing the Template.

Help

I see this as a pretty useful feature (I hope it is useful not just for me).

The proposed solution might seem over-engineered, and maybe it is, if a simpler way of solving this problem exists.

If you are interested in this feature, could you help me answer these questions:

  1. Am I missing any safety considerations?
  2. Am I missing anything in addressing those safety considerations?
  3. Is there a simpler way to solve this problem altogether

Thanks,
Monscodex