[qubes-users] Automatic updating of extra RPMs from add-on repos in Fedora template-based VMs?

Hi qubes-users,

I have a bunch of VMs based on one Fedora TemplateVM. In most cases,
I'm willing to install any Fedora package needed by any of the VMs in
the TemplateVM. However, due to security concerns, I have one VM that
runs Zoom, one that runs Skype, one that runs Google Chrome, and one
that runs Visual Studio Code... you get the idea. Each of those
applications offers its own dnf repository, but I don't want to add
those repositories to the TemplateVM. And I don't want to use
StandaloneVMs because that will multiply my management work; even if
there are management tools that could handle most of my needs, I'd
still have to learn and configure them.

So far, I've been manually downloading the RPMs and extracting them
into the user home directory. (Fortunately, none of them have had
dependencies on absolute paths that would break this approach.) This
is enough of a pain that I rarely update the applications, which may be
bad for security.

Does anyone know of a better but still convenient solution?

I was considering writing a tool to automatically update specified
packages in a TemplateBasedVM every time it boots. There are various
ways this could be implemented. My latest idea is to actually add the
repository files to /etc/yum.repos.d in the volatile layer of the root
filesystem and do a normal "dnf install" but find a way to cache the
RPMs under /rw to avoid re-downloading them when they haven't changed.
Compared to installing under /rw in some fashion, this approach does
extra installation work on every boot but produces a true systemwide
installation, avoiding any problems with absolute paths and the like
(though granted, I haven't experienced any such problems under my
current approach with the particular applications I use).

Proposed detailed design (untested): The TemplateBasedVM keeps a
directory of repository files, a list of package names to install, and
a cache directory of RPMs. On boot:

1. "dnf clean packages"

2. "createrepo" on the VM's cache directory.

3. Copy the repository files to /etc/yum.repos.d and generate one for
the VM's cache directory too.

4. "dnf --setopt=keepcache=1 install" the requested package names.
This may pull in additional dependencies, and all packages that were
installed will be left in the system dnf cache, including any from
repositories that were already in the TemplateVM. (I don't have any
packages of the latter type, but other users with scenarios different
than mine might. For that matter, other users could use the tool
purely to install extra packages from repositories already in the
TemplateVM (so the directory of repository files to add would be empty)
and/or for reasons unrelated to security.)

5. Sync the packages from the system dnf cache to the VM's cache
directory, deleting any obsolete packages from the VM's cache
directory.

6. "dnf clean packages" again.

I would welcome feedback of any kind. After working out the design, I
realized that though I've suffered from the problem for years, it
happens that starting tomorrow I will rarely have a need to use any of
the proprietary applications, so actually developing the tool may fall
down my priority list unless others express significant interest.

As an aside, I'm aware that some people in this community might be
inclined to tell me off for adopting an approach to security that they
believe is poor in some way. I haven't researched whether this is
actually a common problem; if it isn't, please do not take my statement
as an insult to the community. FWIW, I think it's great that the Qubes
OS technology allows users to achieve a range of security levels higher
than that of mainstream OS distributions depending on how much trouble
users are willing to go to, and I'd like to see the community
accommodate all of them. So I'll ignore any derision but consider any
reasonable suggestions to improve my own security or make my tools
suitable for users who want higher security.

Thanks for your attention!

Matt

My way of dealing with it is to just clone your pristine fedora-32 template and add the required packages to that template clone, then create an AppVM that uses that template. This way you limit any potential data loss or damage to just that one AppVM which you then use whenever you need one of those proprietary apps. The question now is what data would they share in that AppVM and is it reasonable for them to share the same AppVM? If the answer is yes then there is no problem. If no, then create another AppVM based on the same template for the other app.

The downside is you now have to update two templates instead of one, but that of course can be automated.

How many specialized AppVMs you create is then based on your own risk/benefit analysis. I would think it’s reasonable for instance to have Zoom and Skype share the same memory space unless the topics discussed in each app are highly confidential. If so, you could also just launch a Disposable VM based on that one template, but for each and every instance of conversation, and then nothing is ever shared since each instance starts up with no user data. You just need to move any presentations between AppVMs to support those conversations.

Steve

Hi Steve,

Thanks for your thoughtful response!

My way of dealing with it is to just clone your pristine fedora-32 template and add the required packages to that template clone, then create an AppVM that uses that template. This way you limit any potential data loss or damage to just that one AppVM which you then use whenever you need one of those proprietary apps. The question now is what data would they share in that AppVM and is it reasonable for them to share the same AppVM? If the answer is yes then there is no problem. If no, then create another AppVM based on the same template for the other app.

For proprietary apps packaged by their vendors, I don’t trust the package installation scripts any more than the apps themselves. Thus, if I wouldn’t be willing to run two apps in the same VM, I wouldn’t be willing to install both apps in the same template either. This being so, the approach you suggest degenerates to the StandaloneVM approach I mentioned. (At the other extreme, if the apps were packaged by an entity that I trust to ensure that no proprietary code runs without user consent, then I could just install the packages in my main template and the whole problem would go away. Is there an intermediate scenario in which having a second template shared by multiple AppVMs is useful?)

The downside is you now have to update two templates instead of one, but that of course can be automated.

While I could probably get used to kicking off the dnf upgrade in all templates and letting it run unattended (it’s often slow), my bigger concern is the custom tools and configuration changes in my main template that aren’t currently packaged for dnf. I could probably package them and/or do without some of them in some proprietary-app VMs, but I think that would end up being a bigger hassle than developing and using my proposed tool. Also, I’m low on disk space and making many templates would make it worse, though maybe it’s time that I just bought a bigger disk.

How many specialized AppVMs you create is then based on your own risk/benefit analysis. I would think it’s reasonable for instance to have Zoom and Skype share the same memory space unless the topics discussed in each app are highly confidential.

You’re probably right that the additional risk of sharing a VM between Zoom and Skype (for example) is small compared to the other unsolved security problems I currently have. However, inasmuch as I continue to use the proprietary apps, I’d be more inclined to just develop the tool to automate the use of separate VMs (anticipating that other people might reuse it) than to address this question.

Matt

Hi Steve,

Thanks for your thoughtful response!

My way of dealing with it is to just clone your pristine fedora-32
template and add the required packages to that template clone, then
create an AppVM that uses that template. This way you limit any
potential data loss or damage to just that one AppVM which you then
use whenever you need one of those proprietary apps.

Same here.

The question now
is what data would they share in that AppVM and is it reasonable for
them to share the same AppVM? If the answer is yes then there is no
problem. If no, then create another AppVM based on the same template
for the other app.

For proprietary apps packaged by their vendors, I don't trust the
package installation scripts any more than the apps themselves. Thus,
if I wouldn't be willing to run two apps in the same VM, I wouldn't be
willing to install both apps in the same template either. This being
so, the approach you suggest degenerates to the StandaloneVM approach I
mentioned. (At the other extreme, if the apps were packaged by an
entity that I trust to ensure that no proprietary code runs without
user consent, then I could just install the packages in my main
template and the whole problem would go away. Is there an intermediate
scenario in which having a second template shared by multiple AppVMs is
useful?)

For me, the advantage of TemplateVMs over StandaloneVMs (even if there's only one TemplateBasedVM based on the TemplateVM) is that it's easier to update the TemplateVM and back up the TemplateBasedVM.

The downside is you now have to update two templates instead of one,
but that of course can be automated.

While I could probably get used to kicking off the dnf upgrade in all
templates and letting it run unattended (it's often slow),

I just let it run overnight.

my bigger
concern is the custom tools and configuration changes in my main
template that aren't currently packaged for dnf. I could probably
package them and/or do without some of them in some proprietary-app
VMs, but I think that would end up being a bigger hassle than
developing and using my proposed tool.

No need. Just make your changes in one template, then clone that template as needed. That way, you only have to make the changes once.

Also, I'm low on disk space and
making many templates would make it worse, though maybe it's time that
I just bought a bigger disk.

If you use minimal templates, even having a lot of them doesn't take up much space.

I have the honor of a response from Andrew! :slight_smile:

For me, the advantage of TemplateVMs over StandaloneVMs (even if there's
only one TemplateBasedVM based on the TemplateVM) is that it's easier to
update the TemplateVM and back up the TemplateBasedVM.

I assumed the update process was the same for a TemplateVM or a
StandaloneVM (though I've never tried the latter), and for backups, I
can select any set of VMs in the Qube Manager. Perhaps you're pointing
out that if the system volume of the desired AppVM is easy enough to
recreate that it's not worth backing up, then using a TemplateVM +
TemplateBasedVM rather than a StandaloneVM makes it possible to skip
the backup? Interesting point. Though I suppose the more general
observation underlying my original proposal was that if the process to
generate the system volume from that of the main TemplateVM is
automated and reasonably fast, then there's the option to run it on
every boot of the TemplateBasedVM rather than persisting a separate
system volume at all.

> my bigger
> concern is the custom tools and configuration changes in my main
> template that aren't currently packaged for dnf. I could probably
> package them and/or do without some of them in some proprietary-app
> VMs, but I think that would end up being a bigger hassle than
> developing and using my proposed tool.

No need. Just make your changes in one template, then clone that
template as needed. That way, you only have to make the changes once.

The problem is when I change the main TemplateVM and want to apply the
change to all existing system volumes. For example, recently I've
migrated some really useful configuration settings (e.g., enabling
undo-tree by default in Emacs and setting "merge.conflictstyle = diff3"
in Git) from the user volume of my "main" AppVM to my TemplateVM so I
would enjoy their benefits in all AppVMs. If I had multiple
TemplateVMs, I would have to somehow copy those changes to all of them.
If I changed a single file, maybe I could write a script that does a
bunch of qvm-copy commands. But if I want to make sure things do not
get out of sync over time due to mistakes, it would help to have a
management tool of some kind.

However, on second thought, I realize that the only VM with a
proprietary app in which most of these customizations are valuable is
the one with Visual Studio Code, in which I do some of my software
development. In the others, I pretty much run the proprietary app and
nothing else because I want to minimize the data exposed to the
proprietary app. So as long as there are only two TemplateVMs in which
the customizations are needed, it may be manageable to copy them
manually.

> Also, I'm low on disk space and
> making many templates would make it worse, though maybe it's time that
> I just bought a bigger disk.
>

If you use minimal templates, even having a lot of them doesn't take up
much space.

Good point. This would likely be appropriate for my VMs that run a
proprietary app and nothing else, although the video conferencing ones
will need at least pavucontrol, for example. For the Visual Studio
Code VM, I'd need a lot more, but probably still a lot less than the
~13G of software in my main TemplateVM that is the union of everything
needed for all the projects in its TemplateBasedVMs.

Matt

I have the honor of a response from Andrew! :slight_smile:

> For me, the advantage of TemplateVMs over StandaloneVMs (even if there's
> only one TemplateBasedVM based on the TemplateVM) is that it's easier to
> update the TemplateVM and back up the TemplateBasedVM.

I assumed the update process was the same for a TemplateVM or a
StandaloneVM (though I've never tried the latter), and for backups, I
can select any set of VMs in the Qube Manager. Perhaps you're pointing
out that if the system volume of the desired AppVM is easy enough to
recreate that it's not worth backing up, then using a TemplateVM +
TemplateBasedVM rather than a StandaloneVM makes it possible to skip
the backup? Interesting point. Though I suppose the more general
observation underlying my original proposal was that if the process to
generate the system volume from that of the main TemplateVM is
automated and reasonably fast, then there's the option to run it on
every boot of the TemplateBasedVM rather than persisting a separate
system volume at all.

> > my bigger
> > concern is the custom tools and configuration changes in my main
> > template that aren't currently packaged for dnf. I could probably
> > package them and/or do without some of them in some proprietary-app
> > VMs, but I think that would end up being a bigger hassle than
> > developing and using my proposed tool.
>
> No need. Just make your changes in one template, then clone that
> template as needed. That way, you only have to make the changes once.

The problem is when I change the main TemplateVM and want to apply the
change to all existing system volumes. For example, recently I've
migrated some really useful configuration settings (e.g., enabling
undo-tree by default in Emacs and setting "merge.conflictstyle = diff3"
in Git) from the user volume of my "main" AppVM to my TemplateVM so I
would enjoy their benefits in all AppVMs. If I had multiple
TemplateVMs, I would have to somehow copy those changes to all of them.
If I changed a single file, maybe I could write a script that does a
bunch of qvm-copy commands. But if I want to make sure things do not
get out of sync over time due to mistakes, it would help to have a
management tool of some kind.

Just one word - salt.
Salt your templates, and you can straightforwardly clone, and configure,
those templates with one command.
Using salt also has the benefit that you can sit down at a new machine,
pull down the salt config and recreate your system. No more wondering if
you kept track of what was installed, or whether your scripts contain
all the nice config changes you laboured over.

I have the honor of a response from Andrew! :slight_smile:

:smiley:

For me, the advantage of TemplateVMs over StandaloneVMs (even if there's
only one TemplateBasedVM based on the TemplateVM) is that it's easier to
update the TemplateVM and back up the TemplateBasedVM.

I assumed the update process was the same for a TemplateVM or a
StandaloneVM (though I've never tried the latter),

It mostly is, but I personally find it easier to be able to update and install packages in the TemplateVM separately from the TemplateBasedVM. There's also the minor fact that I can update all of my templates with a single qubesctl command, whereas StandaloneVMs would be left out.

Oh, and there's also a bit of a security benefit, which I forgot to mention:

and for backups, I
can select any set of VMs in the Qube Manager. Perhaps you're pointing
out that if the system volume of the desired AppVM is easy enough to
recreate that it's not worth backing up, then using a TemplateVM +
TemplateBasedVM rather than a StandaloneVM makes it possible to skip
the backup? Interesting point.

Yes, but even if you don't skip backing up templates, just being able to include them in different backup sets and being able to back them up at different frequencies is handy. There was a forum discussion about this recently:

Though I suppose the more general
observation underlying my original proposal was that if the process to
generate the system volume from that of the main TemplateVM is
automated and reasonably fast, then there's the option to run it on
every boot of the TemplateBasedVM rather than persisting a separate
system volume at all.

I can't speak to that. My experience has led me to keep things simple and in line with intended functionality, since I've found that erecting elaborate custom processes that aren't necessarily supported by the underlying system results in too high of a maintenance burden for me in the future.

1 Like

I thought someone might mention Salt. :slight_smile: If you feel like it, to
satisfy my curiosity, can you point me to documentation on how I'd use
Salt to copy a file to all templates? I'm still more likely to use my
original proposed solution though.

In the long term, I'd indeed like to have a concise script of some kind
to reproduce all my templates. It will be a significant amount of
work to find all my customizations and get them into such a script, so
I'm seeking a nearer-term solution for these proprietary apps. But
while we're on the topic: while mutating templates may be a decent
solution for rapid iteration, how fast could I make the clean
regeneration run? Is there a reasonable way to generate one template
and then generate the others from it? Might OSTree be helpful?

Thanks for any insights!

Matt

> I assumed the update process was the same for a TemplateVM or a
> StandaloneVM (though I've never tried the latter),

It mostly is, but I personally find it easier to be able to update and
install packages in the TemplateVM separately from the TemplateBasedVM.

Why? One advantage I see to the StandaloneVM is that package changes
are immediately persistent and usable in combination with the private
volume. When using a TemplateVM and TemplateBasedVM, I generally make
package changes first in the TemplateBasedVM for rapid iteration (where
they will be lost on shutdown) and later make them to the TemplateVM
once I am sure what changes I want.

There's also the minor fact that I can update all of my templates with a
single qubesctl command, whereas StandaloneVMs would be left out.

That's strange. If qubesctl has an option to target all TemplateVMs,
I'd think the case for an option to target all updatable VMs
(TemplateVMs and StandaloneVMs) would be equally strong.

Oh, and there's also a bit of a security benefit, which I forgot to
mention:

https://www.qubes-os.org/doc/templates/#note-on-treating-templatebasedvms-root-filesystem-non-persistence-as-a-security-feature

I'm of the firm opinion that auditing a home directory for user-level
rootkits is impractical, as suggested by that page. IIRC, I came to
this conclusion long before I migrated to Qubes OS in 2014.

Yes, but even if you don't skip backing up templates, just being able to
include them in different backup sets and being able to back them up at
different frequencies is handy.

Another interesting point. Currently, I just back up all my VMs
weekly. If I were to try to improve that, rather than set different
frequencies for different VMs, I'd be more likely to try to find a
solution to back up each VM incrementally so I can afford to back up
all of them more frequently. In the past, I've seen some discussions
of how to do this without significantly increasing the attack surface,
but I don't have the links on hand.

> Though I suppose the more general
> observation underlying my original proposal was that if the process to
> generate the system volume from that of the main TemplateVM is
> automated and reasonably fast, then there's the option to run it on
> every boot of the TemplateBasedVM rather than persisting a separate
> system volume at all.
>

I can't speak to that. My experience has led me to keep things simple
and in line with intended functionality, since I've found that erecting
elaborate custom processes that aren't necessarily supported by the
underlying system results in too high of a maintenance burden for me in
the future.

I personally am not worried about this. While I was waiting for
https://github.com/QubesOS/qubes-gui-agent-linux/pull/107 to be merged,
rather than build a custom RPM and install it in my template, I elected
to set up a script that ran on every boot of the TemplateBasedVM in
which I wanted the functionality and overwrote module-vchan-sink.so
with my custom-built one. Maybe modifying the template would have been
better, but modifying the TemplateBasedVM on every boot did work.
Installing RPMs on boot differs only in degree.

Matt

> > The problem is when I change the main TemplateVM and want to apply the
> > change to all existing system volumes. For example, recently I've
> > migrated some really useful configuration settings (e.g., enabling
> > undo-tree by default in Emacs and setting "merge.conflictstyle = diff3"
> > in Git) from the user volume of my "main" AppVM to my TemplateVM so I
> > would enjoy their benefits in all AppVMs. If I had multiple
> > TemplateVMs, I would have to somehow copy those changes to all of them.
> > If I changed a single file, maybe I could write a script that does a
> > bunch of qvm-copy commands. But if I want to make sure things do not
> > get out of sync over time due to mistakes, it would help to have a
> > management tool of some kind.
>
> Just one word - salt.
> Salt your templates, and you can straightforwardly clone, and configure,
> those templates with one command.
> Using salt also has the benefit that you can sit down at a new machine,
> pull down the salt config and recreate your system. No more wondering if
> you kept track of what was installed, or whether your scripts contain
> all the nice config changes you laboured over.

I thought someone might mention Salt. :slight_smile: If you feel like it, to
satisfy my curiosity, can you point me to documentation on how I'd use
Salt to copy a file to all templates? I'm still more likely to use my
original proposed solution though.

The state file, copyit.sls, could look like this:

/home/user/target:
  file.managed:
    - source:
      - salt://source
    - user: user
    - group: group

And the command would be:
qubesctl --skip-dom0 --templates state.apply copyit

The best source is, of course, the docs, which explain all the
different things you can do with files/directories:

I have some introductory notes at https://github.com/unman/notes/tree/master/salt

In the long term, I'd indeed like to have a concise script of some kind
to reproduce all my templates. It will be a significant amount of
work to find all my customizations and get them into such a script, so
I'm seeking a nearer-term solution for these proprietary apps. But
while we're on the topic: while mutating templates may be a decent
solution for rapid iteration, how fast could I make the clean
regeneration run? Is there a reasonable way to generate one template
and then generate the others from it? Might OSTree be helpful?

Thanks for any insights!

Matt

As the Irishman said, "I wouldn't start from here".
It's undoubtedly *much* easier to start by installing and customising
using salt, than to try to remember what one has done and fit that in to
salt states.

As to "how long", that depends on exactly what you're doing. Creating and
cloning templates is, I'd say, pretty much as fast as
qvm-create/qvm-clone.
Installing and configuring slower than opening console and doing it
manually, I think, although I haven't done that for some time, except for
minimal templates that cant be salted out of the box. (This is a
constant source of frustration for me and a huge error imo)
The first thing I do is salt a caching proxy in place of tinyproxy.
Then I clone the vanilla template, build the largest template (which
caches almost all the packages), and start on the rest.
Most of my templates don't share new packages, so I cant install, clone and
install in to the clone. If your model is different, then you'd be able to do
this quite simply, using cascading template installs.

I assumed the update process was the same for a TemplateVM or a
StandaloneVM (though I've never tried the latter),

It mostly is, but I personally find it easier to be able to update and
install packages in the TemplateVM separately from the TemplateBasedVM.

Why?

Just a lot of little things. For example, if I screw up the TemplateBasedVM, and I don't have any data in it, I can just destroy it and recreate it without having to reinstall any programs. Conversely, if I screw up the TemplateVM, I can keep the TemplateBasedVM and just recreate the TemplateVM.

One advantage I see to the StandaloneVM is that package changes
are immediately persistent and usable in combination with the private
volume. When using a TemplateVM and TemplateBasedVM, I generally make
package changes first in the TemplateBasedVM for rapid iteration (where
they will be lost on shutdown) and later make them to the TemplateVM
once I am sure what changes I want.
  
I suppose I'm now at the point where I already know which packages I need, so that problem seldom arises for me now.

There's also the minor fact that I can update all of my templates with a
single qubesctl command, whereas StandaloneVMs would be left out.

That's strange. If qubesctl has an option to target all TemplateVMs,
I'd think the case for an option to target all updatable VMs
(TemplateVMs and StandaloneVMs) would be equally strong.

Oh, and there's also a bit of a security benefit, which I forgot to
mention:

https://www.qubes-os.org/doc/templates/#note-on-treating-templatebasedvms-root-filesystem-non-persistence-as-a-security-feature

I'm of the firm opinion that auditing a home directory for user-level
rootkits is impractical, as suggested by that page. IIRC, I came to
this conclusion long before I migrated to Qubes OS in 2014.

Yes, but even if you don't skip backing up templates, just being able to
include them in different backup sets and being able to back them up at
different frequencies is handy.

Another interesting point. Currently, I just back up all my VMs
weekly. If I were to try to improve that, rather than set different
frequencies for different VMs, I'd be more likely to try to find a
solution to back up each VM incrementally so I can afford to back up
all of them more frequently. In the past, I've seen some discussions
of how to do this without significantly increasing the attack surface,
but I don't have the links on hand.

There's an open issue for this: