.\" Copyright 2017 Google Inc. .\" .\" Licensed under the Apache License, Version 2.0 (the "License"); you may not .\" use this file except in compliance with the License. You may obtain a copy .\" of the License at: .\" .\" http://www.apache.org/licenses/LICENSE-2.0 .\" .\" Unless required by applicable law or agreed to in writing, software .\" distributed under the License is distributed on an "AS IS" BASIS, WITHOUT .\" WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the .\" License for the specific language governing permissions and limitations .\" under the License. .Dd March 11, 2020 .Dt SANDBOXFS 1 .Os .Sh NAME .Nm sandboxfs .Nd A virtual file system for sandboxing .Sh SYNOPSIS .Nm .Op Fl -allow Ar who .Op Fl -cpu_profile Ar path .Op Fl -input Ar path .Op Fl -help .Op Fl -mapping Ar type:mapping:target .Op Fl -node_cache .Op Fl -output Ar path .Op Fl -reconfig_threads Ar count .Op Fl -ttl Ar duration .Op Fl -version .Op Fl -xattrs .Ar mount_point .Sh DESCRIPTION .Nm is a FUSE file system that exposes a combination of multiple files and directories from the host's file system in the form of a virtual tree with an arbitrary layout. You can think of a sandbox as an arbitrary view into the host's file system with different access privileges per directory. .Pp .Nm is designed to allow running commands with limited access to the file system by using the virtual tree as their new root, and to do so consistently across a variety of platforms. .Pp The sandbox instance mounted at .Ar mount_point is initially configured with the mapping specifications provided via repeated instances of .Fl -mapping . Once running, the mount point can be reconfigured any number of times: .Nm listens for reconfiguration requests on the file specified by .Fl -input and reconfigures the mappings as requested, emiting a response on the file specified by .Fl -output . .Pp The following flags are recognized: .Bl -tag -width XXXX .It Fl -allow Ar who Specifies who should have access to the file system. .Ar who must be one of: .Sq other to indicate that everyone, including root, can access the file system; .Sq root to indicate that only the current user and root can access the file system; and .Sq self to indicate that only the current user can access the file system. .Pp The default value is .Sq self because the standard FUSE configuration does not allow more relaxed permissions, and it would be a pity if you couldn't run .Nm successfully without additional configuration. To change this behavior, add .Sq user_allow_other to .Pa /etc/fuse.conf on Linux or set the .Sq vfs.generic.osxfuse.tunables.allow_other .Xr sysctl 8 tunable to 1 on macOS. .Pp Note that, at least on macOS, you will need to consider granting .Sq other permissions if you intend to run signed binaries through the sandbox. This is because the .Xr amfid 8 daemon, which implements the signature validation, runs as a different user and must be able to access the executables. .It Fl -cpu_profile Ar path Enables CPU profiling and stores the pprof log to the given .Ar path . This feature is only available if .Nm was built with gperftools support (i.e. with the compile-time .Sq profiler feature). Passing this flag when support is not enabled results in an error. .It Fl -input Ar path Points to the file from which to read new configuration requests, or .Sq - (the default) for stdin. See the .Sx Reconfigurations subsection for details on the contents and behavior of the input file. .It Fl -help Prints global help details and exits. Specifying this flag causes all other valid flags and arguments to be ignored. .It Fl -mapping Ar type:mapping:target Registers a new mapping. This flag can be given an arbitrary number of times as long as the same .Ar mapping is not repeated. See the .Sx Mapping specifications subsection for details on how a mapping is specified. .It Fl -node_cache Enables the path-based node cache, which causes nodes to be reused across reconfigurations when they map to the same underlying paths. This should offer a performance boost when the same set of files are mapped over and over again across different reconfiguration operations. .Pp This cache used to be enabled by default but it turned out to be problematic. For example, on macOS, OSXFUSE seems to keep track of the path where a node first appeared, which causes trouble if the node disappears and then reappears in a different location (for example, .Xr dladdr 3 returns invalid paths). For this reason, the cache is now disabled by default but is still kept around in the code for experimentation. .Em The cache may be removed in a future release altogether if these problems .Em cannot be worked around. .It Fl -output Ar path Points to the file to which to write confirmations of reconfiguration, or .Sq - (the default) for stdout. See the .Sx Reconfigurations subsection for details on the contents and behavior of the output file. .It Fl -ttl Ar duration Specifies how long the kernel is allowed to cache file metadata for. The duration is currently specified as a number of seconds followed by the .Sq s suffix. .It Fl -reconfig_threads Ar count Sets the number of threads to use to process reconfiguration requests. Defaults to the number of logical CPUs in the system. .It Fl -version Prints version information and exits. Specifying this flag causes all other valid flags and arguments to be ignored except for .Fl -help , which has priority over this one. .Pp The first line printed to stdout is machine parseable and follows the following structure: .Bd -literal -offset indent sandboxfs MAJOR.MINOR[.ADDITIONAL OPTIONAL QUALIFIERS] .Ed .Pp Programs automating invocations of .Nm can use this information to determine the correct command-line syntax to use. .It Fl -xattrs Enables support for extended attributes, which causes all extended attribute operations to propagate to the underlying files. .Pp When disabled (the default), the behavior of extended attribute operations is platform-dependent: they may either fail as not supported or they may act as no-op stubs. The reason is that this feature is disabled by telling the kernel that none of these operations are implemented in .Nm , which then causes the kernel to never contact the daemon for them. Keeping extended attributes disabled can result in a performance boost. .El .Ss Mapping specifications The basic concept behind a .Nm instance is the .Em mapping specification . A mapping specification defines how an arbitrary location within the sandbox relates to another arbitrary location on the host file system. Mapping specifications have three components: the .Em target , which is a path to a file or directory into the host file system (i.e. outside of the mount point); the .Em mapping , which is an absolute path within the mount point in which the target is exposed; and the .Em type , which specifies the permissions of the mapping. .Pp The target must exist but the mapping may not: in particular, any path subcomponents of the mapping that have not been previously mapped within the sandbox will be created as virtual read-only nodes. This means that the ordering of the mappings matters. .Pp For example: consider a .Nm instance mounted onto .Pa /mnt that maps .Pa /my/custom/bin to the .Pa /usr/bin target. With this setup, .Pa /mnt/my/custom/bin will expose the contents of the real .Pa /usr/bin directory. .Pp The following types are currently supported: .Bl -tag -width XXXX .It ro A read-only mapping. The contents of the target are exposed verbatim at the mapping point and they cannot be modified through the mount point. Any write access will result in an .Dv EPERM . .It rw A read/write mapping. The contents of the target are exposed verbatim at the mapping point and they can be modified at will through the mount point. Writes through the moint point are applied immediately to the underlying target directory. .El .Ss Reconfigurations While a mount point is live, .Nm listens for reconfiguration requests on the file specified by .Fl -input and writes reconfiguration responses to the file specified by .Fl -output . These two files essentially implement a rudimentary RPC system subject to change. .Pp The mount point can be configured any number of times while mounted, which allows for processes to efficiently change the view of the sandbox at will without having to remount it. Closing the input file causes .Nm to freeze its configuration and not accept any more requests for reconfiguration, but the file system will continue to operate normally until it is either unmounted or signaled. .Pp Reconfiguration requests can be issued at any time. However, it is impossible for .Nm to properly deal with ongoing file system operations or with open file handles in a deterministic manner. Due to this, the behavior of ongoing file system activity while a reconfiguration happens is unspecified but can be assumed to be stable (i.e. .Nm will not deadlock nor crash). As a result, it is highly recommended that you only send reconfiguration requests when you know that the file system is quiescent. .Pp Configuration requests are provided as a stream of JSON objects. Each request is an object with just one of the following keys: .Sq CreateSandbox , which requests the creation of a new top-level directory with a given set of mappings; and .Sq DestroySandbox , which requests the deletion of the mappings at an existing top-level directory. .Pp .Pp A .Sq CreateSandbox operation contains an object with three keys: .Sq id specifies the name of the top-level directory to create, .Sq mappings contains an array of all mappings to apply within that directory, and .Sq prefixes contains a dictionary of all new prefixes to register for prefix-encoded paths. Each mapping entry is an object with the following keys: .Sq path and .Sq underlying_path , which define the mapping paths; .Sq path_prefix and .Sq underlying_path_prefix , which identify the prefixes for the provided paths, respectively; and .Sq writable , which if set to true indicates a read/write mapping. The mapping must not yet exist in the file system. Each entry in the prefixes dictionary is keyed by the numerical identifier of the prefix (supplied as a string due to JSON limitations), and the value is the absolute path for that prefix. .Pp Prefix-encoded paths serve to minimize the size of the requests but their use is optional. To specify a path in its prefix-encoded form, .Sq path and/or .Sq underlying_path must be relative and .Sq path_prefix and/or .Sq underlying_path_prefix must indicate a positive non-zero integer. The referenced prefix must be registered in the same request or a previous request, but if the prefix is repeated across requests, it must point to the same location. To avoid prefix-encoded form, paths must be given as absolute and they need to reference the special prefix 0. .Pp A .Sq DestroySandbox operation simply contains the .Sq id of a previously-created sandbox as a string. The whole tree hierarchy is unmapped. .Pp Each configuration request is paired with a response, which are also provided as a stream of JSON objects. Each response is a map with an optional .sq id field, which corresponds to the identifier given in the request, and an optional .Sq error field, which is empty if the request was successful and contains an error message otherwise. Responses with a missing identifier indicate fatal failures during the reconfiguration (e.g. due to a syntax error) and are not recoverable. .Pp To minimize the size of the requests, all fields support aliases and default values as follows: .Pp .Bl -tag -offset XXXXXX -width underlyingXpathXprefixXX -compact .It Sq CreateSandbox Alias: .Sq C . .It Sq DestroySandbox Alias: .Sq D . .It Sq id Alias: .Sq i . .It Sq mappings Alias: .Sq m . Default value: empty array. .It Sq prefixes Alias: .Sq q . Default value: empty object. .It Sq path Alias: .Sq p . .It Sq path_prefix Alias: .Sq x . Default value: .Sq 0 . .It Sq underlying_path Alias: .Sq u . .It Sq underlying_path_prefix Alias: .Sq y . Default value: .Sq 0 . .It Sq writable Alias: .Sq w . Default value: .Sq false . .El .Sh EXIT STATUS .Nm exits with 0 if the file system was both mounted and unmounted cleanly; 1 on a controlled error condition encountered during the execution of a command; or 2 on a usage error. .Pp Sending a termination signal to .Nm will cause the file system to exit in a controlled manner, ensuring that the mount point is released. If the file system is busy, the signal will be queued until all open file descriptors on the file system are released at which point the file system will try to exit cleanly again. Note that, due to limitations in signal handling in Rust (which is the language in which .Nm is implemented), the reception of a signal will cause .Nm to return 1 instead of terminating with a signal condition. .Sh ENVIRONMENT .Nm recognizes the following environment variables: .Bl -tag -width XXXX .It Va RUST_LOG Sets the maximum level of logging messages sent to stderr. Possible values include .Sq error , .Sq warn , .Sq info and .Sq debug , though many more syntaxes are supported. See the documentation for Rust's .Sq env_logger crate for more details. .El .Pp .Nm may recognize other standard Rust variables like .Va RUST_BACKTRACE but the list above attempts to describe the ones a user may find most useful. .Sh EXAMPLES .Ss Command-line invocation This example configures a sandbox that maps the whole host's file system but clears .Pa /tmp to point into a sandbox-specific writable directory: .Bd -literal -offset indent sandboxfs --mapping=ro:/:/ --mapping=rw:/tmp:/tmp/fresh-tmp /mnt .Ed .Ss Reconfiguration request This example creates a new sandbox under .Pa /first with a single mapping that will appear as .Pa /first/tmp and that is mapped to .Pa /tmp/abc ; then deletes said sandbox, and then creates a different sandbox under .Pa /second with a single mapping that will appear as .Pa /foo/bar and that is mapped to .Pa /tmp/x/y : .Bd -literal -offset indent [ {"CreateSandbox": { "id": "first", "mappings": [ {"path": "/tmp", "underlying_path": "/tmp/abc", "writable": true} ] }}, {"DestroySandbox": "first"} {"CreateSandbox": { "id": "second", "mappings": [ {"path": "/foo/bar", "underlying_path": "/tmp/x/y", "writable": false} ] }} ] .Ed .Pp When sending this request, we obtain the following response: .Bd -literal -offset indent {"id": "first", "error": null} {"id": "first", "error": null} {"id": "second", "error": null} .Ed .Ss Prefix-encoded reconfiguration request This example is the same as above, but this time using prefix-encoded paths: .Bd -literal -offset indent [ {"CreateSandbox": { "id": "first", "mappings": [ {"path": "", "path_prefix": 1, "underlying_path": "abc", "underlying_path_prefix": 1, "writable": true} ], "prefixes": { "1": "/tmp" } }}, {"DestroySandbox": "first"} {"CreateSandbox": { "id": "second", "mappings": [ {"path": "bar", "path_prefix": 2, "underlying_path": "x/y", "underlying_path_prefix": 1, "writable": false} ], "prefixes": { "2": "/foo" } }} ] .Ed .Ss Minimized reconfiguraton request This example is the same as above, but this time using field aliases and default values to minimize the message: .Bd -literal -offset indent [ {"C": { "i": "first", "m": [ {"p": "", "x": 1, "u": "abc", "y": 1, "w": true} ], "q": { "1": "/tmp" } }}, {"D": "first"} {"C": { "i": "second", "m": [ {"p": "bar", "x": 2, "u": "x/y", "y": 1} ], "q": { "2": "/foo" } }} ] .Ed .Sh AUTHORS .An -nosplit The .Nm was originally developed as a Go program by .An Pallav Agarwal .Aq pallavag@google.com with guidance from .An Julio Merino .Aq jmmv@google.com . The program was later reimplemented in Rust by the latter. .Sh BUGS The following are known limitations of .Nm : .Bl -bullet .It Hard links are not supported. .It Mapping the same external file or directory under two different locations within the mount point results in undefined behavior. Writes may not be reflected at both mapped locations at the same time, which can lead to data corruption. This is true even for read-only mappings because each separate view within the mount point may have cached different contents, returning different data than what's truly on disk. Using .Fl -node_cache may help mitigate this issue but it doesn't always do. .It The .Fl -allow Ar root setting does not work on Linux; use .Fl -allow Ar other as the alternative. See https://github.com/bazil/fuse/issues/144 for details. .It It is currently impossible to terminate .Nm cleanly while the file system is busy. Signals received while the file system is in use will be queued as described in .Sx EXIT STATUS and fatal signals will cause .Nm to leak the mount point (possibly irrecoverably without a reboot because of kernel bugs). .It Any explicitly-mapped directories and any scaffold directories (those directories that appear to represent intermediate path components that do not exist anywhere else in the file system) cannot be removed. Attempts to remove them will result in a .Dq permission denied error. While it could be possible to implement some different behavior, this is what .Nm currently exposes. You may or may not consider this to be a bug. .It If .Fl -node_cache is enabled, node data is cached in-memory for all files accessed through a .Nm instance in order to offer good performance across reconfigurations. However, this cache does not currently implement any expiration policy, which means that memory usage can grow unboundedly if many different files are mapped and accessed through the sandbox. .It If a FIFO is used for .Fl input , .Nm will block until a separate process opens the FIFO for writing. This happens even before the file system starts serving, which means the file system is not usable until the FIFO is opened. .It While it is possible to reconfigure the entries of the root directory of a running file system, it is not possible to reconfigure the root mapping itself to point to a different location or to change its writability properties. .It Unmapping entries doesn't fully work. The FUSE library that .Nm currently uses does not support sending cache invalidation requests to the kernel, which means unmapped entries will not vanish immediately from the file system. You may be able to mitigate this by setting a low node TTL with the .Fl -ttl flag, but this doesn't work on macOS either because OSXFUSE does not honor node TTLs. .It Handling of extended attributes on open-but-deleted-files does not work properly. Those files will appear as if they didn't have any extended attributes any longer, and attempts to modify them will fail. .El