How can write to rw/config/rc.local file using Salt?

Hello,

I’ve been working on a salt script to configure my development environment. Thanks to some great posts here in the community and good docs I managed to get pretty far already.

I followed mostly this:

And I got to the point where I can already create my template from a cloned fedora template, install the apps that I need and change my default shell.

So far I’ve been only working on a single file I created in /srv/user_salt/developer.sls

And I’ve been using the jinja syntax to decide where to run things.

I trigger salt with: sudo qubesctl --targets=fedora-44-dev state.sls developer

What I wasn’t able to figure out, is how can I apply some changes in the actual AppVM? More precisely I am configuring split ssh, so I know how I can append to the rpc file policy in dom0 with:

/etc/qubes/policy.d/50-ssh.policy:
  file.append:
    - text: |
        qubes.SshAgent * dev vault ask default_target=vault

But how do I get to auto write to the /rw/config/rc.local in my AppVm this part?:

/rw/config/rc.local:
  file.append:
    - text: |
        SSH_VAULT_VM="vault"
        if [ "$SSH_VAULT_VM" != "" ]; then
          export SSH_SOCK="/home/user/.SSH_AGENT_$SSH_VAULT_VM"
          rm -f "$SSH_SOCK"
          sudo -u user /bin/sh -c "umask 177 && exec socat 'UNIX-LISTEN:$SSH_SOCK,fork' 'EXEC:qrexec-client-vm $SSH_VAULT_VM qubes.SshAgent'" &
        fi

The command runs all states I created, but it never gets to run the one for rw/config/rc.local

I think is because is gated after this else if block:
{% elif grains ['id'] == 'dev' %}

So it never gets to run the rw command block.

Thanks in advance

Well, you used elif - so either the prior if or following else statement got executed.

Apart from that … you could improve some aspects.

Instead of appending to the rc.local file (which will break if executed multiple times …) you could create a .rc file like /rw/config/rc.local.d/ssh-vault.rc

/rw/config/rc.local.d/ssh-vault.rc:
   file.managed:
      - makedirs: True
      - contents: |
         #!/bin/bash
         SSH_VAULT=...
      - mode: '0751'

These .rc files get executed just like the default rc.local file, but in an organized manner.

2 Likes

For debugging purposes, if your state /rw/config/rc.local: wouldn’t be encapsulated by jinja, you could simply only run the single state instead of the whole file.

sudo qubesctl --targets=fedora-44-dev state.sls_id /rw/config/rc.local developer

1 Like

Hey,

The only other block would be dom0, my understanding is that during this block I should be creating the template only?

So what I had was:

{% if grains['id'] == 'dom0' %}

… Create template here …

{% elif grains['id'] == 'dev' %}

… create the rc config here …

But yeah, it never gets to the dev block, so I am not sure if dev is the right block id. I am assuming is the name I used when creating the AppVM qube.

Btw, thanks for the /rw/config/rc.local.d/ssh-vault.rc tip, I didn’t know I could do that! Will definitely apply

But I am still not sure how do I trigger salt to run in the AppVM, if I don’t put it behind the jinja block and just run the standalone /rw/config/rc.local: like you suggested will qubes/salt already figure out that it should be triggered in the AppVM?

Thanks again

Please provide the full command line output: are you using --targets dev?

Off-topic note: with file.managed (and file.append) you can use a real file as a source instead of putting it into contents. I think it helps keeping things simple and debugging even if I don’t think it is relevant here.

1 Like

Good point - he went with sudo qubesctl --targets=fedora-44-dev state.sls developer. so the jinja should be {% elif grains ['id'] == 'fedora-44-dev' %}

1 Like

Hey,

So this is the one I am using it right now:

{% if grains['id'] == 'dom0' %}

developer--create-template:
  qvm.clone:
    - name: fedora-44-dev
    - source: fedora-44-xfce

developer--create-qube:
  qvm.vm:
    - name: dev
    - present:
        - template: fedora-44-dev
        - label: purple
        - mem: 400
        - maxmem: 4096
        - vcpus: 4
    - prefs:
        - label: purple
        - netvm: sys-vpn
        - class: AppVM
        - include-in-backups: true
    - require:
        - qvm: developer--create-template

developer--enable-services:
  qvm.service:
    - name: dev
    - enable:
        - split-gpg2-client
    - require:
        - qvm: developer--create-qube

developer--expand-storage:
  cmd.run:
    - name: qvm-volume extend dev:private 50G
    - require:
        - qvm: developer--create-qube

/etc/qubes/policy.d/50-ssh.policy:
  file.append:
    - text: |
        qubes.SshAgent * dev vault ask default_target=vault

{% elif grains['id'] == 'fedora-44-dev' %}

developer--install-apps-in-template:
    pkg.installed:
        - pkgs:
            - fish
            ....

developer--set-fish-shell:
  user.present:
    - name: user
    - shell: /usr/bin/fish

{% endif %}

/rw/config/rc.local.d/ssh-vault.rc:
  file.managed:
    - makedirs: True
    - mode: '0751'
    - content: |
        SSH_VAULT_VM="vault"
        if [ "$SSH_VAULT_VM" != "" ]; then
          export SSH_SOCK="/home/user/.SSH_AGENT_$SSH_VAULT_VM"
          rm -f "$SSH_SOCK"
          sudo -u user /bin/sh -c "umask 177 && exec socat 'UNIX-LISTEN:$SSH_SOCK,fork' 'EXEC:qrexec-client-vm $SSH_VAULT_VM qubes.SshAgent'" &
        fi

But the /rw/rc... part don’t get to run in the actual AppVM I am creating. I tried having it inside a dev jinja block but it didn’t run, and now is triggering on dom0. I haven’t tried inside the fedora-44-dev block because I though the rw/config/rc.local file should live in the actual AppVM, right?

I was reading about some other things like top.sls, pillar and running the highstate, but since I am not familiar with salt, I am just trying the more naive approach with a single file, so it must be something really stupid on my side, or something obvious that I am missing, but can’t really sort it out

The command I am running is this:
sudo qubesctl --targets=fedora-44-dev --show-output state.sls developer saltenv=user

Thanks a lot for the help, much appreciated

1 Like

That would be much better indeed, so I can just have the file with the .sls file and point to it?

Right. Leaving it outside the conditional block will make it executed in every target (dom0 and the template), so I would avoid this.

You can ignore this right now. But I like to split things into multiple files, most of the time, one file for dom0, one for the templates and one for the app qubes. You run them one by one with the required targets. I still use conditions to avoid mistakes.

So your state is executed:

  1. in dom0
  2. in each target, so here, only fedora-44-dev.

Add dev to the targets to solve your issue.

Yes, you can also use a /files/ directory to separate state files from the others.

Another tip: take a look at the “configuration management” links of qubes.taxi and see how things are clearly organized (at least in the repos of unman and ben-grande).

1 Like

Hey,

It was as simple as that and it worked fine now. I put the /rw/config.... behind the dev jinja block and added dev to the targets in the command and it worked as expected.

Didn’t know about the qubes.taxi, this is really useful!

Thank you so much

1 Like