About me: My name is Solène Rapenne, pronouns she/her. I like learning and sharing knowledge. Hobbies: '(BSD OpenBSD Qubes OS Lisp cmdline gaming security QubesOS internet-stuff). I love percent and lambda characters. OpenBSD developer solene@. No AI is involved in this blog.

Contact me: solene at dataswamp dot org or @solene@bsd.network (mastodon).

You can sponsor my work financially if you want to help me writing this blog and contributing to Free Software as my daily job.

Using git bundle to synchronize a repository between Qubes OS dom0 and an AppVM

Written by Solène, on 17 June 2023.
Tags: #security #qubesos #git

Comments on Fediverse/Mastodon

1. Introduction §

In a previous article, I explained how to use Fossil version control system to version the files you may write in dom0 and sync them against a remote repository.

I figured how to synchronize a git repository between an AppVM and dom0, then from the AppVM it can be synchronized remotely if you want. This can be done using the git feature named bundle, which bundle git artifacts into a single file.

Qubes OS project official website

Git bundle documentation

Using fossil to synchronize data from dom0 with a remote fossil repository

2. What you will learn §

In this setup, you will create a git repository (this could be a clone of a remote repository) in an AppVM called Dev, and you will clone it from there into dom0.

Then, you will learn how to send and receive changes between the AppVM repo and the one in dom0, using git bundle.

3. Setup §

The first step is to have git installed in your AppVM and in dom0.

For the sake of simplicity for the guide, the path /tmp/repo/ refers to the git repository location in both dom0 and the AppVM, don't forget to adapt to your setup.

In the AppVM Dev, create a git repository using cd /tmp/ && git init repo. We need a first commit for the setup to work because we can't bundle commits if there is nothing. So, commit at least one file in that repo, if you have no idea, you can write a short README.md file explaining what this repository is for.

In dom0, use the following command:

qvm-run -u user --pass-io Dev "cd /tmp/repo/ && git bundle create - master" > /tmp/git.bundle
cd /tmp/ && git clone -b master /tmp/git.bundle repo

Congratulations, you cloned the repository into dom0 using the bundle file, the path /tmp/git.bundle is important because it's automatically set as URL for the remote named "origin". If you want to manage multiple git repositories this way, you should use a different name for this exchange file for each repo.

[solene@dom0 repo]$ git remote -v
origin	/tmp/git.bundle (fetch)
origin	/tmp/git.bundle (push)

Back to the AppVM Dev, run the following command in the git repository, this will configure the bundle file to use for the remote dom0. Like previously, you can pick the name you prefer.

git remote add dom0 /tmp/dom0.bundle

4. Workflow §

Now, let's explain the workflow to exchange data between the AppVM and dom0. From here, we will only use dom0.

Create a file push.sh in your git repository with the content:

#!/bin/sh

REPO="/tmp/repo/"
BRANCH=master

# setup on the AppVM
# git remote add dom0 /tmp/dom0.bundle

git bundle create - origin/master..master | \
  qvm-run -u user --pass-io Dev "cat > /tmp/dom0.bundle"

qvm-run -u user --pass-io Dev "cd ${REPO} && git pull -r dom0 ${BRANCH}"

Create a file pull.sh in your git repository with the content:

#!/bin/sh

REPO="/tmp/repo/"
BRANCH=master

# init the repo on dom0
# git clone -b ${BRANCH} /tmp/git.bundle

qvm-run -u user --pass-io Dev "cd ${REPO} && git bundle create - dom0/master..${BRANCH}" > /tmp/git.bundle
git pull -r

Make the files push.sh and pull.sh executable.

If you don't want to have the files committed in your repository, add their names to the file .gitignore.

Now, you are able to send changes to the AppVM repo using ./push.sh, and receive changes using ./pull.sh.

If needed, those scripts could be made more generic and moved in a directory in your PATH instead of being used from within the git repository.

4.1. Explanations §

Here are some explanations about those two scripts.

4.1.1. Push.sh §

In the script push.sh, git bundle is used to send a bundle file over stdout containing artifacts from the remote AppVM last known commit up to the latest commit in the current repository, hence origin/master..master range. This data is piped into the file /tmp/dom0.bundle in the AppVm, and was configured earlier as a remote for the repository.

Then, the command git pull -r dom0 master is used to fetch the changes from the bundle, and rebase the current repository, exactly like you would do with a "real" remote over the network.

4.1.2. Pull.sh §

In the script pull.sh, we run the git bundle from within the AppVM Dev to generate on stdout the bundle from the last known state of dom0 up to the latest commit in the branch master, and pipe into the dom0 file /tmp/git.bundle, remember that this file is the remote origin in dom0's clone.

After the bundle creation, a regular git pull -r is used to fetch the changes, and rebase the repository.

4.1.3. Using branches §

If you use different branches, this could require adding an extra parameter to the script to make the variable BRANCH configurable.

5. Conclusion §

I find this setup really elegant, the safe qvm-run is used to exchange static data between dom0 and the AppVM, no network is involved in the process. Now there is no reason to have dom0 configuration file not properly tracked within a version control system :)