On this page, you will learn how to create your own sandboxed C/C++ library with Sandboxed API (SAPI). Use it as a guide alongside the examples and code documentation in the header files.
Build Dependencies
The following dependencies must be installed on the system:
- Linux kernel with support for UTS, IPC, user, PID, and network namespaces
- Linux userspace API headers
- To compile your code: GCC 6 (version 7 or higher preferred) or Clang 7 (or higher)
- For auto-generating header files: Clang Python Bindings
- Python 3.5 or later
- Bazel version 2.2.0 or CMake version 3.12 or higher.
Using Bazel
Bazel is the recommended build system and is the easiest to integrate with.
Our documentation uses the Clang compiler. If you need a specific toolchain (e.g. compiler, linker, etc.), refer to the Bazel documentation for information on how to change the default compiler toolchain.
Debian 10 (Buster)
To install build dependencies:
echo "deb http://storage.googleapis.com/bazel-apt stable jdk1.8" | \ sudo tee /etc/apt/sources.list.d/bazel.list
wget -qO - https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install -qy build-essential linux-libc-dev bazel python3 \ python3-pip libclang-dev
pip3 install clang
Gentoo
Kernel options required:
General setup --->
-*- Namespaces support
[*] UTS namespace
[*] IPC namespace
[*] User namespace (EXPERIMENTAL)
[*] PID Namespaces
[*] Network namespace
To install build dependencies:
emerge dev-util/bazel dev-python/typing dev-python/clang-python
Using CMake
CMake is a popular open-source meta build system that generates project files for build tools such as Ninja or Make.
Debian 10 (Buster)
To install build dependencies:
sudo apt-get install -qy build-essential linux-libc-dev cmake ninja-build \ python3 python3-pip libclang-dev libcap-dev
pip3 install absl-py clang
Gentoo
Kernel options required:
General setup --->
-*- Namespaces support
[*] UTS namespace
[*] IPC namespace
[*] User namespace (EXPERIMENTAL)
[*] PID Namespaces
[*] Network namespace
To install build dependencies:
emerge sys-kernel/linux-headers dev-util/cmake dev-util/ninja \
dev-python/clang-python
Development Process
To sandbox a C/C++ library, you will have to prepare two items for your project:
- The Sandboxed Library
- The Host Code which will make use of the functionality exposed by your Sandboxed Library. SAPI will generate the SAPI Object and RPC Stub automatically for you during the build process.
You may be familiar with the zlib from the Sandbox2 examples here a whole program (zpipe.c) was sandboxed. In the following steps, you will learn how to use SAPI to sandbox the zlib library and make use of the Sandboxed Library.
1. Decide which functions are needed
If you look at the zlib host code
(main_zlib.cc),
you can see that the tool's functionality is to read data from stdin and use
zlib's deflate()
function to compress the data until an EOF
marker is read.
In total, the program uses three functions from zlib:
deflateInit_()
: To initialize for compressiondeflate()
: To perform the compression operation on the data chunkdeflateEnd()
: To end the compression and free dynamically allocated data structures
In a real life example you would review the C/C++ library and decide which functions are needed. A possible strategy is to start with the Host Code and use the library unsandboxed. Then, in a second step, you could generate the Sandboxed Library and adapt the Host Code to use the sandboxed function calls.
2. Write the sapi_library Build Rule
After you have identified the three zlib functions that are needed from the
sandboxed zlib library, you can define the build rule in the
BUILD
file. The documentation for the sapi_library
build rule can be found on the
Build Rules page.
The code snippet below shows the sapi_library
definition for the zlib SAPI
example. Using the lib
attribute, Bazel is instructed to look in the
WORKSPACE file
for the zlib library.
sapi_library(
name = "zlib-sapi",
srcs = [],
hdrs = [],
functions = [
"deflateInit_",
"deflate",
"deflateEnd",
],
lib = "@net_zlib//:zlib",
lib_name = "Zlib",
namespace = "sapi::zlib",
)
The result is that the sandboxed zlib library is generated. The output is the SAPI Object which can be included into the Host Code and be used to communicate with the sandboxed library via RPC calls. The Sandbox Policy used in this example is the default policy.
3. Write or Change the Host Code
It's now time to incorporate the generated SAPI Library into the Host Code.
Create the Sandbox
Use sapi::Sandbox sandbox(sapi::zlib::zlib_sapi_embed_create());
to create a
sandbox object.
Use sapi::zlib::ZlibApi api(&sandbox);
to instantiate the SAPI Object and thus
make the sandboxed functions available for use.
Use SAPI Types
SAPI Types are special types in the form of C++ classes that SAPI provides because sometimes regular C-types will not work.
The first use of a SAPI type can be observed in the declaration of strm
, where
a SAPI Struct is used: sapi::v::Struct<sapi::zlib::z_stream> strm;
The template type (sapi::zlib::z_stream
) is a good example of code
automatically generated by the build rule.
Take a look at the Variables page for more details.
Make API Calls
To make calls to either defalteInit_
, deflate
, or deflateEnd
, use the SAPI
Object. If you decide to use the ‘change' approach, you have to make sure that
the function parameters match the expected values.
An example of each of the calls in the zlib example:
api.deflateInit_(strm.PtrBoth(), Z_DEFAULT_COMPRESSION, version.PtrBefore(), sizeof(sapi::zlib::z_stream));
api.deflate(strm.PtrBoth(), flush);
api.deflateEnd(strm.PtrBoth()).IgnoreError();
Using SAPI Transactions
SAPI isolates the Host Code from the Sandboxed Library and gives the caller the ability to restart or abort problematic data processing requests. SAPI Transaction goes one step further and automatically repeats failed processes.
Take a look at the SAPI Transactions Page for more details.
Examples
Under Examples you can find a few libraries, already prepared by the SAPI team.