summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Maat <tristan.maat@codethink.co.uk>2019-11-04 13:22:55 +0000
committerJürg Billeter <j@bitron.ch>2019-12-03 12:06:31 +0000
commitf553fc8c626d06f5993ecef55a027a28123f0cf9 (patch)
tree96bbb985d64112387943ec04175f8f461978bd7b
parent476a53975286364088576df270a3be13fe1bec4a (diff)
downloadbuildstream-f553fc8c626d06f5993ecef55a027a28123f0cf9.tar.gz
casdprocessmanager.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/casdprocessmanager.py61
1 files changed, 57 insertions, 4 deletions
diff --git a/src/buildstream/_cas/casdprocessmanager.py b/src/buildstream/_cas/casdprocessmanager.py
index 68bb88ef0..4c9d80209 100644
--- a/src/buildstream/_cas/casdprocessmanager.py
+++ b/src/buildstream/_cas/casdprocessmanager.py
@@ -18,8 +18,10 @@
import contextlib
import os
+import random
import shutil
import signal
+import stat
import subprocess
import tempfile
import time
@@ -52,10 +54,7 @@ class CASDProcessManager:
def __init__(self, path, log_dir, log_level, cache_quota, protect_session_blobs):
self._log_dir = log_dir
- # Place socket in global/user temporary directory to avoid hitting
- # the socket path length limit.
- self._socket_tempdir = tempfile.mkdtemp(prefix="buildstream")
- self._socket_path = os.path.join(self._socket_tempdir, "casd.sock")
+ self._socket_path = self._make_socket_path(path)
self._connection_string = "unix:" + self._socket_path
casd_args = [utils.get_host_tool("buildbox-casd")]
@@ -80,6 +79,60 @@ class CASDProcessManager:
with _signals.blocked([signal.SIGINT], ignore=False):
self.process = subprocess.Popen(casd_args, cwd=path, stdout=logfile_fp, stderr=subprocess.STDOUT)
+ # _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._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 755, 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._socket_tempdir,
+ stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH,
+ )
+
+ os.symlink(path, os.path.join(self._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.
+ random_name = "".join([random.choice("abcdefghijklmnopqrstuvwxyz0123456789_") for _ in range(8)])
+ socket_name = "casserver-{}.sock".format(random_name)
+ return os.path.join(self._socket_tempdir, "cas", socket_name)
+
# _rotate_and_get_next_logfile()
#
# Get the logfile to use for casd