summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Maat <tristan.maat@codethink.co.uk>2019-11-04 13:22:55 +0000
committerTristan Maat <tristan.maat@codethink.co.uk>2019-11-12 11:19:01 +0000
commitbcad8f400bf4f20faef87e3e58d4602dd3310cab (patch)
tree6bae41bb7f71830ab47f87df0b86c6ef66649954
parent288bd39c5ecd74a5817f86159f0a2153515d3bd3 (diff)
downloadbuildstream-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.py64
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()