ANN: git-remote-qubes: Inter-VM Git for Qubes OS

50 views
Skip to first unread message

Manuel Amador (Rudd-O)

unread,
Oct 27, 2016, 7:47:14 AM10/27/16
to qubes-users, qubes-devel
It gives me great pleasure to announce the inter-VM Git bridge for Qubes
OS, which allows you to git push and git pull from VMs stored in other
repos, with no networking involved whatsoever, and observing full
compliance with Qubes OS qrexec policy.

This should usher in a new era of software development that allows
people to segregate their secure Git repos from insecure build VMs and
other engineering constructs I can't even think of (after doing
low-level socket programming for a week, which has left my brain utterly
fried).

Find the software at https://github.com/Rudd-O/git-remote-qubes

-----------------------------------

# Inter-VM Git for Qubes OS

This is a very simple Git protocol bridge between Qubes OS VMs. With it,
you can `git pull` and `git push` between VMs without having to grant
any of the VMs any special policy privileges other than access to Git.

## Using the software

These instructions assume you have installed the software. See the
*Installing the software* heading below for more information.

### Creating a repository

We'll assume for illustration purposes that you want to access a repository
stored in `/home/user/xyz` on your VM `servervm`.

Run the following commands on `servervm`:

```
cd /home/user
mkdir -p xyz
cd xyz
git --bare init
```

That's it. Your new and empty repository is ready to use.

### Adding a remote to a local repository

For illustration purposes, you'll be pushing and pulling `servervm`'s `xyz`
repo on your vm `clientvm`. Run the following commands on `clientvm`:

```
cd /home/user
git clone qubes://clientvm/home/user/xyz
```

You will get a permission dialog from dom0 asking for `ruddo.Git` access.
Accept it. Note that accepting the permission dialog implicitly gives
Git access to all Git repos stored in `servervm`, but only for that one
execution (unless you say *Yes to all*, in which case the permission
is remembered within the policy file that you installed earlier with the
`dom0` package).

This should have cloned `xyz` from `servervm` into your `/home/user/xyz`
directory in `clientvm`.

From this point on, you can push and pull in `clientvm` from
`servervm:/home/user/xyz` to your heart's content.

If, instead of cloning, you have an existing repo, you can add a new remote
just as easily:

```
cd /home/user/existingxyz
git remote add qubesremotevm qubes://servervm/home/user/xyz
```

That addition will enable to push and pull from the remote `qubesremotevm`
which represents the repo `/home/user/xyz` in the remote VM `servervm`.

## Installing the software

There are two components for this software:

* Component 1 is the VM side of things, which implements the Git protocol
across VMs.
* Component 2 is the dom0 side of things, which is a simple text file
declaring
the initial Git access policy for your VMs.

First, build the software, After cloning this repository on a suitable VM,
run the command:

```
make rpm
```

This will generate two installable packages on the local directory:

* `git-remote-qubes-<version>.noarch.rpm`, which contains the Git
protocol implementation.
* `git-remote-qubes-dom0-<version>.noarch.rpm`, which contains the
default policy.

Copy the `git-remote-qubes-<version>.noarch.rpm` file to the template VM
or standalone VM where you plan to pull or push to / from a Git repo
stored in another Qubes VM. Install the RPM with
`dnf install <name of the RPM>`. At this point, this VM, as well as
any VMs using this as a template, have gained the ability to push and pull
from Git repos stored in other VMs, as well as the ability to listen on
push / pull requests from different VMs in the same system.

Now copy the `git-remote-qubes-dom0-<version>.noarch.rpm` file to
your dom0. At this point, the default policy (`deny`) is active on
your Qubes OS system, and you can begin pushing and pulling.

Those clever among you will have discovered that there is a `Makefile`
included, and that you can use the `Makefile` to install the software on
other non-RPM templates. I welcome pull requests to add support for
other distro packages and Qubes OS templates.

## Troubleshooting and debugging

If you are experiencing problems communicating with a Git repo in a VM,
export the variable `QUBES_DEBUG` on the side of your client (where your
local Git repo is), and look at the debugging output that appears.

As always, you can file new issues on the repo of this project for help
with fixing bugs that the programs may have. Pull requests also welcome.

--
Rudd-O
http://rudd-o.com/

Marek Marczykowski-Górecki

unread,
Oct 27, 2016, 9:13:41 AM10/27/16
to Manuel Amador (Rudd-O), qubes-devel
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On Thu, Oct 27, 2016 at 11:47:04AM +0000, Manuel Amador (Rudd-O) wrote:
> It gives me great pleasure to announce the inter-VM Git bridge for Qubes
> OS, which allows you to git push and git pull from VMs stored in other
> repos, with no networking involved whatsoever, and observing full
> compliance with Qubes OS qrexec policy.
>
> This should usher in a new era of software development that allows
> people to segregate their secure Git repos from insecure build VMs and
> other engineering constructs I can't even think of (after doing
> low-level socket programming for a week, which has left my brain utterly
> fried).

Hmm, have you seen this?
https://www.qubes-os.org/doc/development-workflow/#git-connection-between-vms

- --
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJYEf2AAAoJENuP0xzK19csttsIAJQJJ4kvbMceU7A3QZz9/ON9
uvn34Pl600Y7kmUvsiJOS1IvyoDy2ul4+weKX+5mCS/oneq8nJlBYDz/s4rD/wCt
TdItLfWbkFbfi+cy7JiLWdizDgGfJaC1GlkHa8tpCB7J90fniN9YHwHUB2dYFiwg
dN8AL0idvogDoQ5ZqqFcBq7d8VRH97A9C3OPCvI+FnWDU2OM7VCWBidbBZCzYmKD
KKgjRg4RWT87pMf3XJ3CCW9BcwxY/SYM2+FaWFo32j0m3fUmB0mBUM5X4ZjD+78+
dOXsUNYeqkcSHuuHe4vLgOwZAVX5p9wzvk47L4XxMgjO2QqAisXCi4JFAk7tdSU=
=7y9l
-----END PGP SIGNATURE-----

Drew White

unread,
Oct 27, 2016, 7:37:25 PM10/27/16
to qubes-users, qubes...@googlegroups.com, rud...@rudd-o.com
You can either do that and use something that will absorb RAM, OR you can just add the iptables rules, they work easier and faster than adding ANOTHER layer.

Just look for the post I put up months ago regarding the rules for it.

Manuel Amador (Rudd-O)

unread,
Oct 28, 2016, 12:56:44 AM10/28/16
to qubes...@googlegroups.com
On 10/27/2016 01:13 PM, Marek Marczykowski-Górecki wrote:
> On Thu, Oct 27, 2016 at 11:47:04AM +0000, Manuel Amador (Rudd-O) wrote:
> > It gives me great pleasure to announce the inter-VM Git bridge for Qubes
> > OS, which allows you to git push and git pull from VMs stored in other
> > repos, with no networking involved whatsoever, and observing full
> > compliance with Qubes OS qrexec policy.
>
> > This should usher in a new era of software development that allows
> > people to segregate their secure Git repos from insecure build VMs and
> > other engineering constructs I can't even think of (after doing
> > low-level socket programming for a week, which has left my brain utterly
> > fried).
>
> Hmm, have you seen this?
> https://www.qubes-os.org/doc/development-workflow/#git-connection-between-vms
>

I had. That inspired me to package up the solution in a nice-to-use and
easy-to-install way, that is more general and is not limited to Qubes OS
development. As you know, instructions are cool, but ready-to-go
software is cooler. I quite like that my solution actually has a
distinct protocol qubes:/// too.

I'm sure you've noticed that I tend to improve things in this manner :-)

I need some help here: what aspects of the documented solution should I
incorporate to my program?


--
Rudd-O
http://rudd-o.com/

Marek Marczykowski-Górecki

unread,
Oct 28, 2016, 5:17:37 AM10/28/16
to Manuel Amador (Rudd-O), qubes...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On Fri, Oct 28, 2016 at 04:56:36AM +0000, Manuel Amador (Rudd-O) wrote:
> On 10/27/2016 01:13 PM, Marek Marczykowski-Górecki wrote:
> > On Thu, Oct 27, 2016 at 11:47:04AM +0000, Manuel Amador (Rudd-O) wrote:
> > > It gives me great pleasure to announce the inter-VM Git bridge for Qubes
> > > OS, which allows you to git push and git pull from VMs stored in other
> > > repos, with no networking involved whatsoever, and observing full
> > > compliance with Qubes OS qrexec policy.
> >
> > > This should usher in a new era of software development that allows
> > > people to segregate their secure Git repos from insecure build VMs and
> > > other engineering constructs I can't even think of (after doing
> > > low-level socket programming for a week, which has left my brain utterly
> > > fried).
> >
> > Hmm, have you seen this?
> > https://www.qubes-os.org/doc/development-workflow/#git-connection-between-vms
> >
>
> I had. That inspired me to package up the solution in a nice-to-use and
> easy-to-install way, that is more general and is not limited to Qubes OS
> development. As you know, instructions are cool, but ready-to-go
> software is cooler. I quite like that my solution actually has a
> distinct protocol qubes:/// too.

I think it should be possible without manually copying the data back and
forth, too. In other words: connect git directly to stdin/out and wait
it for finish, then handle next command (if any). The amount of code
scares me - over 10x more over something that works just fine...

One possible problem is that it gives access to all the repositories
(there is an info about that in doc). It may not always be desired
effect. The instruction above have a place to limit the access, I see
two easy ways how to do it in your solution:

1. Add some configuration in target VM - like
.config/qubes-git/allow-from-MY_SOURCE_VM_NAME and put allowed paths there.

2. Use qrexec service argument[1]. This way it can be specified in policy (and
single "yes" on ask would allow only specific repository access). Using
the same way you could also add access level: pull/push only.

The second one is surely more flexible and more user friendly (no new
config, the same confirmation, etc). But probably require few more
changes. Also note that service argument cannot contain "/", so you'll
need to encode the path somehow. And also service name + argument is
limited to 64 characters, but this shouldn't be a big problem in
practice.

[1] https://www.qubes-os.org/doc/qrexec3/#service-argument-in-policy

- --
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJYExeqAAoJENuP0xzK19csehYH/jXDRQSxGw785j2JuahUrvoS
hMcmderGo3yQ8f9iA9V9H0w67bdBLq6UO00xjxBxBr/varI2j9+PWRmeYQVWvqFa
ZhDh1LubxLjvMKqzb4AyHTZhcEXJEXUnYZBG6uBtHyu4w5F5UZMGIJZjigxWklEL
cDl0NsMlZeKBDbrDoZOUR6pagpoFdqvwQkZuJ6GGL5/LA+1qV7zgVwuwfudUE5tm
+fISEU9XsO5GcKrGLo5Pq2GuCb5r5bsptwYz6m6xPrybVpLA5YUXBtzhOH5ppW9a
efULp8Ip7mTVkvuamWVB061rwkK8BM1qENQkCYvyqmq4p2xoNomuZjn6H/dmy54=
=JfJ6
-----END PGP SIGNATURE-----

Manuel Amador (Rudd-O)

unread,
Oct 28, 2016, 6:27:29 AM10/28/16
to Marek Marczykowski-Górecki, qubes...@googlegroups.com
I appreciate your security instincts.

I tried that first, but, you see, there was a huge problem.

I explain:

When the slave spawns / execs git-blah-borg-flurb, and that thing
happens to die (say, because the remote repo directory does not exist),
then the master (git-remote-qubes invoked by git itself) hangs.

Why? Well, I don't ******* know, but I spent literally four days
battling with that, until I decided that the sensible thing to do would
be to just copy the data back and forth.

Now, something must have changed lately, because now if I make the slave
execvp() the git receive pack program, it works fine. So I suppose the
problem was all along in the master.

Thus, I have changed the slave to execvp(). This reduces the amount of
code being executed to a minimum.

Master (src/gitremotequbes/client.py) still needs to copy to and from
the VM, because the connection to the slave is spawned *before*
beginning to process what Git wants us to do. It would be the only way
in which I could (for later iterations of the program) detect whether
Git wants to push or to pull.

>
> One possible problem is that it gives access to all the repositories
> (there is an info about that in doc). It may not always be desired
> effect. The instruction above have a place to limit the access, I see
> two easy ways how to do it in your solution:
>
> 1. Add some configuration in target VM - like
> .config/qubes-git/allow-from-MY_SOURCE_VM_NAME and put allowed paths
> there.
>
> 2. Use qrexec service argument[1]. This way it can be specified in
> policy (and
> single "yes" on ask would allow only specific repository access). Using
> the same way you could also add access level: pull/push only.
>
> The second one is surely more flexible and more user friendly (no new
> config, the same confirmation, etc). But probably require few more
> changes. Also note that service argument cannot contain "/", so you'll
> need to encode the path somehow. And also service name + argument is
> limited to 64 characters, but this shouldn't be a big problem in
> practice.
>
> [1] https://www.qubes-os.org/doc/qrexec3/#service-argument-in-policy
>

Honestly, UNIX permissions should suffice for that purpose. A VM that
wants access to pull but not push can contact the Git server using a
user that is not the "user" user, and then the files will appear
read-only to that VM. Or even completely barred, if the repo in
question is mode 0700 owned by user:user.

BUT, I can add the argument thing. That is not hard to do.

Edit: BIG FAT PROBLEM: if I say "yes to all" when there is an argument
(it's already in the code I just pushed to Github) then nothing actually
gets saved as "yes to all" anywhere. In other words: there's a bug as
"Yes to all" does nothing when an argument is specified.

Please fix!!!

--
Rudd-O
http://rudd-o.com/

cyrinux

unread,
Oct 28, 2016, 6:51:38 AM10/28/16
to qubes-users, qubes...@googlegroups.com, rud...@rudd-o.com
I installed it yestarday, no problem, documentation is clear, well done yet :)

Manuel Amador (Rudd-O)

unread,
Oct 28, 2016, 7:00:18 AM10/28/16
to cyrinux, qubes-users, qubes...@googlegroups.com
Thank you very much.

A few minutes ago I made a new release with a version bump and the
capability to use RPC arguments, such that you can selectively give
permission to certain repos and not to others. This edition also
massively reduces the amount of code that executes on the server (slave)
side, which should make it even more secure than it already is.

There's a small Qubes OS bug which prevents the "Yes to all" button from
working, but it's super easy to work around by just adding the necessary
policy to /etc/qubes-rpc/policy/ruddo.Git — I hope you enjoy it!

--
Rudd-O
http://rudd-o.com/

Marek Marczykowski-Górecki

unread,
Oct 28, 2016, 7:19:14 AM10/28/16
to Manuel Amador (Rudd-O), qubes...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Much better :)
I think now setting stdin/stdout to non-blocking mode is not needed
anymore (if git want that, it should do it itself).

> Master (src/gitremotequbes/client.py) still needs to copy to and from
> the VM, because the connection to the slave is spawned *before*
> beginning to process what Git wants us to do. It would be the only way
> in which I could (for later iterations of the program) detect whether
> Git wants to push or to pull.

I see. The server part is much more critical, so it's ok to have as it
is now. Actually my solution also pass the data manually on the client side
- - but it uses "cat" for this:

(echo $GIT_EXT_SERVICE $2 $3; exec cat) | qrexec-client-vm $VMNAME
local.Git

;)
Thanks for the report
https://github.com/QubesOS/qubes-issues/issues/2403
Fix on the way.

- --
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJYEzQuAAoJENuP0xzK19csLHsH/jGSFZ3mm2EjjjFynleijA5c
w1oWbW0EMPhZdV15rgrf0kryPzlWDeS8rk6bl/gx37J3lgP80MuqAMBgQOWsAJl+
IkgfBVXsLXNKs0aGcO+QywUaWmZbwQbvEOZ5BBp0pHjEAf6fYlaAyT47nLqDslOs
Xv72+PIJKDEi4FzqCUgp5/MBB9+PbgCU1dGuEpEsG6SFobWL9KVEPUlz7ukkBaZx
S2NBrAY8Ec0omef47oNQjbPUOP/GuoqLlVpN6PjLC/2R1xcSZVR/FNgJ2TAsQ7Kk
P7ubfZOCPyvGZn+MuEmRyOtvAWxyMm4xPdwjdhNm0yRIif/p78rLMQBSyYplfpU=
=dQdE
-----END PGP SIGNATURE-----

HW42

unread,
Oct 28, 2016, 9:49:38 AM10/28/16
to Marek Marczykowski-Górecki, Manuel Amador (Rudd-O), qubes...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Marek Marczykowski-Górecki:
[...]
> I see. The server part is much more critical, so it's ok to have as it
> is now. Actually my solution also pass the data manually on the client side
> - but it uses "cat" for this:
>
> (echo $GIT_EXT_SERVICE $2 $3; exec cat) | qrexec-client-vm $VMNAME local.Git

This unfortunately does not work if the remote simply exits. The problem
ist that cat does not detect that it's output fd is closed and therefore
still waits for input on stdin (You can observe this for example if you
try to fetch from an non existing repo).

That's the same reason why

cat | /bin/true

blocks. While you can work around it I'm not aware of a _simple_ trick
to do it.

What do you think of providing some way to pass a first line in
qrexec-client-vm or some wrapper. Then not everybody who implements such
simple qrexec services needs to implement their own "copier" and think
of all the corner cases.
-----BEGIN PGP SIGNATURE-----

iQIsBAEBCgAWBQJYE1c8DxxodzQyQGlwc3Vtai5kZQAKCRDkrMknimRoFjGgEACk
GtXLxxZC2fjgjrDG6u5ybOaMrc7aD8vhC+A9uKnHVTa2QR9NCnfk5ufaDuCYIMxw
ngMRkd4PTda2UlL2OYsrMB4wrgwC14+CpnCc0z8/15bOK1Mg0K0WK5cTnFX7/Xkh
8IcY63TWtsnWk333VOY1C8nfgD8D0u9GxvnWfY+69FdMC1GL8TAJJnynmPMXPupi
Jcf0AXjOD0DEDCbwNyGtkvQKRj/Aq+eBVs+6dozAlAIaIaQliGaFJE/FkmjQVDHI
2HoVP6ADzaOI/bopcv8jR30HrKczTLUTAPzlZ9j4gmI0QZ6hKQK5W8QROleU2Yim
iUMkPtyOrCUC7TN+Vf5MNDji5l/IUdeqDhrqLIcSNLHDoZX/4k8QzuZCr923wCuM
rEFuBLKq2ATQxTGTjFcmOkE/TdsskWUZvPFoqVyBVGeP5vlJk5KxR0kUHcemzdZK
10vVji5nqNsTW9zEwjAmh0tfXVzjLHRpB1bws3UM7A1X66KmNYHNKGesPuOQNhKR
pdyuqUIdK5t1l5OIfh93CsWBHAnB22HScQPcUcJzgxJDQa9I9qCZg12qJ9Gkz7Bf
i7hM38Qp8c/MmgG88lBDleR1R8QeqBOa6bbpYXc+vK4LMpuZxuo3fuqFxAFZ2t4f
9r3wgQvMbi4RZxvzFjRH4PYsM/jc/N5KMgto0zpluA==
=+ghY
-----END PGP SIGNATURE-----

Manuel Amador (Rudd-O)

unread,
Oct 28, 2016, 12:27:26 PM10/28/16
to qubes...@googlegroups.com
On 10/28/2016 01:48 PM, HW42 wrote:
> Marek Marczykowski-Górecki:
> [...]
> > I see. The server part is much more critical, so it's ok to have as it
> > is now. Actually my solution also pass the data manually on the
> client side
> > - but it uses "cat" for this:
>
> > (echo $GIT_EXT_SERVICE $2 $3; exec cat) | qrexec-client-vm
> $VMNAME local.Git
>
> This unfortunately does not work if the remote simply exits. The problem
> ist that cat does not detect that it's output fd is closed and therefore
> still waits for input on stdin (You can observe this for example if you
> try to fetch from an non existing repo).
>
> That's the same reason why
>
> cat | /bin/true
>
> blocks. While you can work around it I'm not aware of a _simple_ trick
> to do it.
>
> What do you think of providing some way to pass a first line in
> qrexec-client-vm or some wrapper. Then not everybody who implements such
> simple qrexec services needs to implement their own "copier" and think
> of all the corner cases.

FWIW: This is exactly the issue that motivated me to create a solution
based on data copying — not being able to detect whether the remote
process had exited.

--
Rudd-O
http://rudd-o.com/

Manuel Amador (Rudd-O)

unread,
Oct 28, 2016, 12:28:16 PM10/28/16
to HW42, Marek Marczykowski-Górecki, qubes...@googlegroups.com
On 10/28/2016 01:48 PM, HW42 wrote:
> Marek Marczykowski-Górecki:
> [...]
> > I see. The server part is much more critical, so it's ok to have as it
> > is now. Actually my solution also pass the data manually on the
> client side
> > - but it uses "cat" for this:
>
> > (echo $GIT_EXT_SERVICE $2 $3; exec cat) | qrexec-client-vm
> $VMNAME local.Git
>
> This unfortunately does not work if the remote simply exits. The problem
> ist that cat does not detect that it's output fd is closed and therefore
> still waits for input on stdin (You can observe this for example if you
> try to fetch from an non existing repo).
>
> That's the same reason why
>
> cat | /bin/true
>
> blocks. While you can work around it I'm not aware of a _simple_ trick
> to do it.
>
> What do you think of providing some way to pass a first line in
> qrexec-client-vm or some wrapper. Then not everybody who implements such
> simple qrexec services needs to implement their own "copier" and think
> of all the corner cases.

BTW the code is written as-is aand can be used as a library, no need to
write anything new anyone else anymore.

--
Rudd-O
http://rudd-o.com/

Reply all
Reply to author
Forward
0 new messages