diff options
author | Tristan Maat <tristan.maat@codethink.co.uk> | 2019-11-04 13:22:55 +0000 |
---|---|---|
committer | Tristan Maat <tristan.maat@codethink.co.uk> | 2019-11-12 11:19:01 +0000 |
commit | bcad8f400bf4f20faef87e3e58d4602dd3310cab (patch) | |
tree | 6bae41bb7f71830ab47f87df0b86c6ef66649954 | |
parent | 288bd39c5ecd74a5817f86159f0a2153515d3bd3 (diff) | |
download | buildstream-bcad8f400bf4f20faef87e3e58d4602dd3310cab.tar.gz |
cascache.py: Set up socket path via a symlink
This is necessary to allow using buildbox-run with userchroot in the
near future, since currently only the owner of the BuildStream process
can access the CASD socket, but the buildbox-casd binary will need to
be setuid' to another user.
This gets around this limitation by allowing the group to access a
symlink, which in turn should point to a directory owned by the CASD
user.
-rw-r--r-- | src/buildstream/_cas/cascache.py | 64 |
1 files changed, 60 insertions, 4 deletions
diff --git a/src/buildstream/_cas/cascache.py b/src/buildstream/_cas/cascache.py index 11f15bdd3..bcd835e9a 100644 --- a/src/buildstream/_cas/cascache.py +++ b/src/buildstream/_cas/cascache.py @@ -24,6 +24,7 @@ import stat import contextlib import ctypes import multiprocessing +import random import shutil import signal import subprocess @@ -84,10 +85,7 @@ class CASCache(): self._cache_usage_monitor_forbidden = False if casd: - # Place socket in global/user temporary directory to avoid hitting - # the socket path length limit. - self._casd_socket_tempdir = tempfile.mkdtemp(prefix='buildstream') - self._casd_socket_path = os.path.join(self._casd_socket_tempdir, 'casd.sock') + self._casd_socket_path = self._make_socket_path(path) casd_args = [utils.get_host_tool('buildbox-casd')] casd_args.append('--bind=unix:' + self._casd_socket_path) @@ -116,6 +114,64 @@ class CASCache(): else: self._casd_process = None + # _make_socket_path() + # + # Create a path to the CASD socket, ensuring that we don't exceed + # the socket path limit. + # + # Note that we *may* exceed the path limit if the python-chosen + # tmpdir path is very long, though this should be /tmp. + # + # Args: + # path (str): The root directory for the CAS repository. + # + # Returns: + # (str) - The path to the CASD socket. + # + def _make_socket_path(self, path): + self._casd_socket_tempdir = tempfile.mkdtemp(prefix='buildstream') + # mkdtemp will create this directory in the "most secure" + # way. This translates to "u+rwx,go-rwx". + # + # This is a good thing, generally, since it prevents us + # from leaking sensitive information to other users, but + # it's a problem for the workflow for userchroot, since + # the setuid casd binary will not share a uid with the + # user creating the tempdir. + # + # Instead, we chmod the directory 750, and only place a + # symlink to the CAS directory in here, which will allow the + # CASD process RWX access to a directory without leaking build + # information. + os.chmod( + self._casd_socket_tempdir, + stat.S_IRUSR | + stat.S_IWUSR | + stat.S_IXUSR | + stat.S_IRGRP | + stat.S_IXGRP, + ) + + os.symlink(path, os.path.join(self._casd_socket_tempdir, "cas")) + # FIXME: There is a potential race condition here; if multiple + # instances happen to create the same socket path, at least + # one will try to talk to the same server as us. + # + # There's no real way to avoid this from our side; we'd need + # buildbox-casd to tell us that it could not create a fresh + # socket. + # + # We could probably make this even safer by including some + # thread/process-specific information, but we're not really + # supporting this use case anyway; it's mostly here fore + # testing, and to help more gracefully handle the situation. + # + # Note: this uses the same random string generation principle + # as cpython, so this is probably a safe file name. + socket_name = "casserver-{}.sock".format( + "".join(random.choices("abcdefghijklmnopqrstuvwxyz0123456789_", k=8))) + return os.path.join(self._casd_socket_tempdir, "cas", socket_name) + def __getstate__(self): state = self.__dict__.copy() |