diff options
author | Tristan Maat <tristan.maat@codethink.co.uk> | 2019-11-04 13:22:55 +0000 |
---|---|---|
committer | Jürg Billeter <j@bitron.ch> | 2019-12-03 12:06:31 +0000 |
commit | f553fc8c626d06f5993ecef55a027a28123f0cf9 (patch) | |
tree | 96bbb985d64112387943ec04175f8f461978bd7b | |
parent | 476a53975286364088576df270a3be13fe1bec4a (diff) | |
download | buildstream-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.py | 61 |
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 |