├── .gitignore
├── .project
├── .pydevproject
├── Jenkinsfile
├── Makefile
├── README.md
├── bin
├── git-local-qubes
└── git-remote-qubes
├── build.parameters
├── etc
├── qubes-rpc
│ └── ruddo.Git.in
└── qubes
│ └── policy.d
│ └── 80-git-remote-qubes.policy
├── git-remote-qubes.spec
└── src
└── gitremotequbes
├── __init__.py
├── client.py
├── copier.py
└── server.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *~
3 | *.tar.gz
4 | *.rpm
5 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | git-remote-qubes
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /${PROJECT_DIR_NAME}/src
5 |
6 | python 3.8
7 | Default
8 |
9 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | // https://github.com/Rudd-O/shared-jenkins-libraries
2 | @Library('shared-jenkins-libraries@master') _
3 |
4 | genericFedoraRPMPipeline()
5 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BINDIR=/usr/bin
2 | SYSCONFDIR=/etc
3 | LIBEXECDIR=/usr/libexec
4 | GITEXECPATH=`git --exec-path`
5 | DESTDIR=
6 | PROGNAME=git-remote-qubes
7 | SITELIBDIR=`python3 -c 'import distutils.sysconfig; print (distutils.sysconfig.get_python_lib())'`
8 |
9 | OBJLIST=$(shell find src/gitremotequbes -name '*.py' | sed 's/.py$$/.pyc/')
10 | SUBSTLIST=etc/qubes-rpc/ruddo.Git
11 |
12 | all: $(OBJLIST) $(SUBSTLIST) bin/git-*-qubes
13 |
14 | src/gitremotequbes/%.pyc: src/gitremotequbes/%.py
15 | @if [ -z "$(SITELIBDIR)" ] ; then echo Error: you need Python 3 on your system >&2 ; exit 1 ; fi
16 | python3 -m compileall src/gitremotequbes/
17 |
18 | etc/%: etc/%.in
19 | cat $< | sed 's|@BINDIR@|$(BINDIR)|g' | sed 's|@LIBEXECDIR@|$(LIBEXECDIR)|g' > $@
20 |
21 | clean:
22 | rm -rfv $(OBJLIST) $(SUBSTLIST)
23 | find -name '*~' -print0 | xargs -0 rm -fv
24 | rm -fv *.tar.gz *.rpm
25 |
26 | dist: clean
27 | excludefrom= ; test -f .gitignore && excludefrom=--exclude-from=.gitignore ; DIR=$(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec` && FILENAME=$$DIR.tar.gz && tar cvzf "$$FILENAME" --exclude="$$FILENAME" --exclude=.git --exclude=.gitignore $$excludefrom --transform="s|^|$$DIR/|" --show-transformed *
28 |
29 | rpm: dist
30 | T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ta $(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/RPMS/*/* "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T"
31 |
32 | srpm: dist
33 | T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ts $(PROGNAME)-`awk '/^Version:/ {print $$2}' $(PROGNAME).spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T"
34 |
35 | install-vm: all
36 | install -Dm 644 src/gitremotequbes/*.py src/gitremotequbes/__pycache__/*.pyc -t $(DESTDIR)/$(SITELIBDIR)/gitremotequbes/
37 | install -Dm 755 bin/git-local-qubes -t $(DESTDIR)/$(LIBEXECDIR)/
38 | install -Dm 755 bin/git-remote-qubes -t $(DESTDIR)/$(GITEXECPATH)/
39 | install -Dm 755 etc/qubes-rpc/ruddo.Git -t $(DESTDIR)/$(SYSCONFDIR)/qubes-rpc/
40 |
41 | install-dom0: all
42 | install -Dm 664 etc/qubes/policy.d/*.policy -t $(DESTDIR)/$(SYSCONFDIR)/qubes/policy.d/
43 | getent group qubes && chgrp qubes $(DESTDIR)/$(SYSCONFDIR)/qubes/policy.d/*.policy || true
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Inter-VM Git for Qubes OS
2 |
3 | This is a very simple Git protocol bridge between Qubes OS VMs. With it,
4 | you can `git pull` and `git push` between VMs without having to grant
5 | any of the VMs any special policy privileges other than access to Git.
6 |
7 | The advantages of this solution over Git-over-SSH or other networking
8 | protocols are clear:
9 |
10 | 1. Better security: you do not need to network the VM that stores your
11 | Git repos, or take care of firewall rules.
12 | 2. More resource efficiency: you do not need to run any network-facing
13 | daemons, effectively saving RAM for other uses.
14 |
15 | ## Using the software
16 |
17 | These instructions assume you have installed the software. See the
18 | *Installing the software* heading below for more information.
19 |
20 | ### Creating a repository
21 |
22 | We'll assume for illustration purposes that you want to access a repository
23 | stored in `/home/user/xyz` on your VM `servervm`.
24 |
25 | Run the following commands on `servervm`:
26 |
27 | ```
28 | cd /home/user
29 | mkdir -p xyz
30 | cd xyz
31 | git --bare init
32 | ```
33 |
34 | That's it. Your new and empty repository is ready to use.
35 |
36 | ### Adding a remote to a local repository
37 |
38 | For illustration purposes, you'll be pushing and pulling `servervm`'s `xyz`
39 | repo on your vm `clientvm`. Run the following commands on `clientvm`:
40 |
41 | ```
42 | cd /home/user
43 | git clone qubes://clientvm/home/user/xyz
44 | ```
45 |
46 | You will get a permission dialog from dom0 asking for `ruddo.Git` access.
47 | Accept it. Note that accepting the permission dialog implicitly gives
48 | Git access to all Git repos stored in `servervm`, but only for that one
49 | execution (unless you say *Yes to all*, in which case the permission
50 | is remembered within the policy file that you installed earlier with the
51 | `dom0` package).
52 |
53 | This should have cloned `xyz` from `servervm` into your `/home/user/xyz`
54 | directory in `clientvm`.
55 |
56 | From this point on, you can push and pull in `clientvm` from
57 | `servervm:/home/user/xyz` to your heart's content.
58 |
59 | If, instead of cloning, you have an existing repo, you can add a new remote
60 | just as easily:
61 |
62 | ```
63 | cd /home/user/existingxyz
64 | git remote add qubesremotevm qubes://servervm/home/user/xyz
65 | ```
66 |
67 | That addition will enable to push and pull from the remote `qubesremotevm`
68 | which represents the repo `/home/user/xyz` in the remote VM `servervm`.
69 |
70 | ## Installing the software
71 |
72 | There are two components for this software:
73 |
74 | * Component 1 is the VM side of things, which implements the Git protocol
75 | across VMs.
76 | * Component 2 is the dom0 side of things, which is a simple text file declaring
77 | the initial Git access policy for your VMs.
78 |
79 | First, build the software, After cloning this repository on a suitable VM,
80 | run the command:
81 |
82 | ```
83 | make rpm
84 | ```
85 |
86 | This will generate two installable packages on the local directory:
87 |
88 | * `git-remote-qubes-.noarch.rpm`, which contains the Git
89 | protocol implementation.
90 | * `git-remote-qubes-dom0-.noarch.rpm`, which contains the
91 | default policy.
92 |
93 | Copy the `git-remote-qubes-.noarch.rpm` file to the template VM
94 | or standalone VM where you plan to pull or push to / from a Git repo
95 | stored in another Qubes VM. Install the RPM with
96 | `dnf install `. At this point, this VM, as well as
97 | any VMs using this as a template, have gained the ability to push and pull
98 | from Git repos stored in other VMs, as well as the ability to listen on
99 | push / pull requests from different VMs in the same system.
100 |
101 | Now copy the `git-remote-qubes-dom0-.noarch.rpm` file to
102 | your dom0. At this point, the default policy (`deny`) is active on
103 | your Qubes OS system, and you can begin pushing and pulling.
104 |
105 | Those clever among you will have discovered that there is a `Makefile`
106 | included, and that you can use the `Makefile` to install the software on
107 | other non-RPM templates. I welcome pull requests to add support for
108 | other distro packages and Qubes OS templates.
109 |
110 | ## Troubleshooting and debugging
111 |
112 | If you are experiencing problems communicating with a Git repo in a VM,
113 | export the variable `QUBES_DEBUG` on the side of your client (where your
114 | local Git repo is), and look at the debugging output that appears.
115 |
116 | As always, you can file new issues on the repo of this project for help
117 | with fixing bugs that the programs may have. Pull requests also welcome.
118 |
--------------------------------------------------------------------------------
/bin/git-local-qubes:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3 -u
2 |
3 | import sys
4 |
5 | import gitremotequbes.server
6 |
7 |
8 | sys.exit(gitremotequbes.server.main())
9 |
--------------------------------------------------------------------------------
/bin/git-remote-qubes:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3 -u
2 |
3 | import sys
4 |
5 | import gitremotequbes.client
6 |
7 |
8 | sys.exit(gitremotequbes.client.main())
9 |
--------------------------------------------------------------------------------
/build.parameters:
--------------------------------------------------------------------------------
1 | ["RELEASE": "q4.2 38 39"]
2 |
--------------------------------------------------------------------------------
/etc/qubes-rpc/ruddo.Git.in:
--------------------------------------------------------------------------------
1 | @LIBEXECDIR@/git-local-qubes
2 |
--------------------------------------------------------------------------------
/etc/qubes/policy.d/80-git-remote-qubes.policy:
--------------------------------------------------------------------------------
1 | ## Default git-remote-qubes policy.
2 |
3 | ruddo.Git * @anyvm @anyvm ask
4 |
--------------------------------------------------------------------------------
/git-remote-qubes.spec:
--------------------------------------------------------------------------------
1 | %define debug_package %{nil}
2 |
3 | %define mybuildnumber %{?build_number}%{?!build_number:1}
4 |
5 | %{!?python3_sitelib: %define python3_sitelib %(python3 -c "from distutils.sysconfig import get_python_lib ; print(get_python_lib(1))")}
6 |
7 | Name: git-remote-qubes
8 | Version: 0.1.0
9 | Release: %{mybuildnumber}%{?dist}
10 | Summary: Inter-VM git push and pull for Qubes OS AppVMs and StandaloneVMs
11 | BuildArch: noarch
12 |
13 | License: GPLv3+
14 | URL: https://github.com/Rudd-O/git-remote-qubes
15 | Source0: https://github.com/Rudd-O/%{name}/archive/{%version}.tar.gz#/%{name}-%{version}.tar.gz
16 |
17 | BuildRequires: make
18 | BuildRequires: sed
19 | BuildRequires: python3
20 | BuildRequires: git
21 |
22 | Requires: python3
23 | Requires: git-core
24 | # systemd is required because of systemd-escape.
25 | Requires: systemd
26 |
27 | %package dom0
28 | Summary: Policy package for Qubes OS dom0s that arbitrates %{name}
29 | Requires: qubes-core-dom0 >= 4.1
30 |
31 | Requires: systemd qubes-core-dom0-linux
32 |
33 | %description
34 | This package lets you setup Git servers on your Qubes OS VMs.
35 | You are meant to install this package on TemplateVMs that are the templates
36 | for AppVMs where you want to either serve git repos from, or push/pull git
37 | repos from, as well as StandaloneVMs where you wish to do the same things.
38 |
39 | %description dom0
40 | This package contains the Qubes OS execution policy for the %{name} package.
41 | You are meant to install this package on the dom0 of the machine where you
42 | have VMs that have the %{name} package installed.
43 |
44 | %prep
45 | %setup -q
46 |
47 | %build
48 | # variables must be kept in sync with install
49 | make DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} SYSCONFDIR=%{_sysconfdir} SITELIBDIR=%{python3_sitelib} LIBEXECDIR=%{_libexecdir}
50 |
51 | %install
52 | rm -rf $RPM_BUILD_ROOT
53 | # variables must be kept in sync with build
54 | for target in install-vm install-dom0; do
55 | make $target DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} SYSCONFDIR=%{_sysconfdir} SITELIBDIR=%{python3_sitelib} LIBEXECDIR=%{_libexecdir}
56 | done
57 |
58 | %check
59 | if grep -r --exclude='*.pyc' --exclude='*.pyo' --exclude='*.policy' '@.*@' $RPM_BUILD_ROOT ; then
60 | echo "Check failed: files with AT identifiers appeared" >&2
61 | exit 1
62 | fi
63 |
64 | %files
65 | %attr(0755, root, root) %{_libexecdir}/git-local-qubes
66 | %attr(0755, root, root) %{_libexecdir}/git-core/git-remote-qubes
67 | %attr(0644, root, root) %{python3_sitelib}/gitremotequbes/*.py
68 | %attr(0644, root, root) %{python3_sitelib}/gitremotequbes/*.pyc
69 | %attr(0644, root, root) %{python3_sitelib}/gitremotequbes/__pycache__/*.pyc
70 | %attr(0755, root, root) %{_sysconfdir}/qubes-rpc/ruddo.Git
71 | %doc README.md
72 |
73 | %files dom0
74 | %config(noreplace) %attr(0664, root, qubes) %{_sysconfdir}/qubes/policy.d/80-git-remote-qubes.policy
75 | %doc README.md
76 |
77 | %changelog
78 | * Mon Oct 24 2016 Manuel Amador (Rudd-O)
79 | - Initial release.
80 |
--------------------------------------------------------------------------------
/src/gitremotequbes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rudd-O/git-remote-qubes/89bf26f3c56fe99899596a4c135bad43a66b3b6b/src/gitremotequbes/__init__.py
--------------------------------------------------------------------------------
/src/gitremotequbes/client.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import logging
3 | import os
4 | import pipes
5 | import subprocess
6 | import sys
7 | import urllib.parse
8 |
9 | import gitremotequbes.copier
10 |
11 |
12 | def get_main_parser():
13 | p = argparse.ArgumentParser()
14 | p.add_argument("name", metavar="NAME")
15 | p.add_argument("url", metavar="URL")
16 | return p
17 |
18 |
19 | def main():
20 | logging.basicConfig(
21 | format="local:" + logging.BASIC_FORMAT,
22 | level=logging.DEBUG if os.getenv("QUBES_DEBUG") else logging.INFO,
23 | )
24 |
25 | p = get_main_parser()
26 | args = p.parse_args()
27 | url = urllib.parse.urlparse(args.url)
28 | assert url.scheme == "qubes"
29 |
30 | l = logging.getLogger()
31 |
32 | rpcarg = subprocess.check_output([
33 | "systemd-escape", "--", url.path
34 | ], universal_newlines=True)[:-1]
35 | if len(rpcarg) > 64 or "\\" in rpcarg:
36 | # Path is too long! We must do without rpcarg.
37 | rpcarg = None
38 |
39 | vm = subprocess.Popen(
40 | ["/usr/lib/qubes/qrexec-client-vm",
41 | url.netloc,
42 | "ruddo.Git" + ("+%s" % rpcarg if rpcarg else "")],
43 | stdin=subprocess.PIPE,
44 | stdout=subprocess.PIPE,
45 | bufsize=0,
46 | )
47 |
48 | cmd = sys.stdin.readline()
49 | assert cmd == "capabilities\n"
50 | sys.stdout.write("connect\n\n")
51 | sys.stdout.flush()
52 |
53 | remoteargs = [args.name, url.path]
54 | if os.getenv("QUBES_DEBUG"):
55 | remoteargs = ["-d"] + remoteargs
56 | quotedargs = " ".join(pipes.quote(x) for x in remoteargs)
57 | quotedlen = len(quotedargs)
58 | vm.stdin.write(("%s\n" % quotedlen + quotedargs).encode("utf-8"))
59 | vm.stdin.flush()
60 |
61 | line = vm.stdout.readline()
62 | if line != b"confirmed\n":
63 | l.debug("the request appears to have been refused or it malfunctioned")
64 | return 128
65 |
66 | ret = 0
67 | while ret == 0:
68 | for f in sys.stdin.buffer, vm.stdin, sys.stdout.buffer, vm.stdout:
69 | gitremotequbes.copier.b(f)
70 | cmd = sys.stdin.readline()
71 |
72 | if not cmd:
73 | l.debug("no more commands, exiting")
74 | break
75 | elif cmd.startswith("connect "):
76 | l.debug("asked to run %s", cmd)
77 | vm.stdin.write(cmd.encode("utf-8"))
78 | reply = vm.stdout.readline().decode("utf-8")
79 | assert reply == "\n", "local: wrong reply %r" % reply
80 | sys.stdout.write(reply)
81 | sys.stdout.flush()
82 |
83 | ret = gitremotequbes.copier.call(
84 | vm,
85 | sys.stdin.buffer,
86 | sys.stdout.buffer,
87 | )
88 | if ret != 0:
89 | l.debug("remote side exited with %s", ret)
90 | else:
91 | l.debug("remote side exited normally")
92 | break
93 | elif cmd == "\n":
94 | l.debug("git sent us an empty line as command")
95 | else:
96 | l.error("invalid command %r", cmd)
97 | ret = 127
98 |
99 | return ret
100 |
--------------------------------------------------------------------------------
/src/gitremotequbes/copier.py:
--------------------------------------------------------------------------------
1 | import fcntl
2 | import logging
3 | import os
4 | import select
5 | import subprocess
6 | import threading
7 |
8 |
9 | def nb(f):
10 | fd = f.fileno()
11 | fl = fcntl.fcntl(fd, fcntl.F_GETFL)
12 | fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
13 |
14 |
15 | def b(f):
16 | fd = f.fileno()
17 | fl = fcntl.fcntl(fd, fcntl.F_GETFL)
18 | fcntl.fcntl(fd, fcntl.F_SETFL, fl & (~os.O_NONBLOCK))
19 |
20 |
21 | class Copy(threading.Thread):
22 | """Copy from the keys of the dictionary allfds to the values of the
23 | allfds dictionary.
24 |
25 | Side effects: fds passed will be set to nonblocking.
26 | """
27 |
28 | def __init__(self, allfds):
29 | threading.Thread.__init__(self)
30 | self.l = logging.getLogger("copy")
31 | self.setDaemon(True)
32 | self.allfds = allfds
33 | for readable in allfds:
34 | nb(readable)
35 | self.enders = {}
36 | for r in self.allfds:
37 | pr, pw = os.pipe()
38 | pr = os.fdopen(pr, "rb")
39 | pw = os.fdopen(pw, "ab")
40 | nb(pr)
41 | self.enders[r] = [pr, pw]
42 |
43 | def fdname(self, f):
44 | return "[%s %r]" % (f.name, f.mode)
45 |
46 | def run(self):
47 | fdname = self.fdname
48 | l = self.l
49 |
50 | def copier(readable, writable):
51 | l.debug("beginning to copy from %s to %s",
52 | fdname(readable), fdname(writable))
53 | stop = False
54 | readables = [readable, self.enders[readable][0]]
55 | while True:
56 | r, _, _ = select.select(
57 | readables,
58 | [],
59 | [],
60 | 0 if stop else None
61 | )
62 | if self.enders[readable][0] in r:
63 | l.debug("signaled to stop copying from %s to %s",
64 | fdname(readable), fdname(writable))
65 | self.enders[readable][0].close()
66 | readables.remove(self.enders[readable][0])
67 | stop = True
68 | continue
69 | elif stop and not r:
70 | break
71 | chunk = r[0].read()
72 | if type(chunk) is str:
73 | chunk = str.encode("utf-8")
74 | if chunk == '' or chunk == b"":
75 | l.debug("%s closed", fdname(readable))
76 | readable.close()
77 | l.debug("closing write end %s",
78 | fdname(self.allfds[readable]))
79 | self.allfds[readable].close()
80 | self.enders[readable][0].close()
81 | break
82 | l.debug("copying from %s to %s: %r",
83 | fdname(readable), fdname(writable), chunk)
84 | writable.write(chunk)
85 | writable.flush()
86 | l.debug("done copying data from %s to %s",
87 | fdname(readable), fdname(writable))
88 |
89 | self.l.debug("beginning to copy")
90 |
91 | threads = []
92 | for readable, writable in list(self.allfds.items()):
93 | threads.append(threading.Thread(target=copier,
94 | args=(readable, writable)))
95 | threads[-1].setDaemon(True)
96 | threads[-1].start()
97 |
98 | for t in threads:
99 | t.join()
100 |
101 | self.l.debug("done copying")
102 |
103 | def end(self):
104 | for readable, (_, wp) in list(self.enders.items()):
105 | self.l.debug("ending copy of data from %s",
106 | self.fdname(readable))
107 | wp.close()
108 |
109 |
110 | def call(cmd, stdin, stdout, env=None):
111 | """call() runs (or adopts) a subprocess, copying data from stdin into
112 | the process' stdin, and stdout from the process into stdout.
113 | """
114 | if env is None:
115 | env = os.environ
116 |
117 | l = logging.getLogger("call")
118 |
119 | if isinstance(cmd, subprocess.Popen):
120 | p = cmd
121 | l.debug("adopting command %s", cmd)
122 | else:
123 | l.debug("running command %s", cmd)
124 | p = subprocess.Popen(list(cmd),
125 | env=env,
126 | stdin=subprocess.PIPE,
127 | stdout=subprocess.PIPE)
128 |
129 | copier = Copy({p.stdout: stdout, stdin: p.stdin})
130 | copier.start()
131 |
132 | l.debug("waiting for %s to end", cmd)
133 | ret = p.wait()
134 | l.debug("%s ended, signaling copier to end", cmd)
135 | copier.end()
136 | l.debug("copier signaled, waiting for readers and writers")
137 | copier.join()
138 | l.debug("readers and writers done")
139 |
140 | return ret
141 |
--------------------------------------------------------------------------------
/src/gitremotequbes/server.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import shlex
4 | import signal
5 | import subprocess
6 | import sys
7 |
8 | import gitremotequbes.copier
9 |
10 |
11 | def main():
12 | quotedlen = sys.stdin.readline()
13 | if not quotedlen:
14 | logging.basicConfig(format="remote:" + logging.BASIC_FORMAT, level=logging.INFO)
15 | l = logging.getLogger()
16 | logging.error("Peer disconnected early.")
17 | return 8
18 | quotedlen = int(quotedlen, 10)
19 | if quotedlen > 65535 or quotedlen < 1:
20 | assert 0, "invalid len"
21 | args = sys.stdin.read(quotedlen)
22 | if len(args) != quotedlen:
23 | assert 0, "invalid argument list"
24 | try:
25 | args = shlex.split(args)
26 | except Exception as e:
27 | assert 0, "invalid argument list: %s" % e
28 | if args[0] == "-d":
29 | args = args[1:]
30 | level = logging.DEBUG
31 | else:
32 | level = logging.INFO
33 | git_dir = args[1]
34 |
35 | logging.basicConfig(format="remote:" + logging.BASIC_FORMAT, level=level)
36 | l = logging.getLogger()
37 |
38 | trustedarg = os.getenv("QREXEC_SERVICE_ARGUMENT")
39 | if trustedarg:
40 | # Qubes OS subsystem has sent us an argument, and that argument
41 | # is trusted, so trust that over whatever the remote process said.
42 | l.debug("trustworthy argument %r sent by Qubes OS", trustedarg)
43 | git_dir = subprocess.check_output([
44 | "systemd-escape", "--unescape", "--", trustedarg
45 | ], universal_newlines=True)[:-1]
46 |
47 | sys.stdout.write("confirmed\n")
48 |
49 | while True:
50 | for f in sys.stdin, sys.stdout:
51 | gitremotequbes.copier.b(f)
52 | cmd = sys.stdin.readline()
53 |
54 | if not cmd:
55 | l.debug("no more commands, exiting")
56 | break
57 | if cmd.startswith("connect "):
58 | cmd = cmd[8:-1]
59 | assert cmd in ("git-upload-pack", "git-receive-pack"), \
60 | "remote: bad command %r" % cmd
61 | sys.stdout.write("\n")
62 | # And here we go. We no longer are in control. Child is.
63 | os.execvp("git", ["git", cmd[4:], git_dir])
64 | else:
65 | assert 0, "invalid command %r" % cmd
66 |
--------------------------------------------------------------------------------