From e69c981342f4de452168f69eba84a9fc39f9fd8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:07:25 +0000 Subject: [PATCH 1/7] Bump tracing from 0.1.37 to 0.1.38 Bumps [tracing](https://github.com/tokio-rs/tracing) from 0.1.37 to 0.1.38. - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-0.1.37...tracing-0.1.38) --- updated-dependencies: - dependency-name: tracing dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 11 +++++------ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56852db..a66958c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1764,11 +1764,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1776,13 +1775,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fe3b70a..89c9b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ serde = { version = "1.0.160", default-features = false, features = ["derive"] } sha2 = { version = "0.10.6", default-features = false } thiserror = { version = "1.0.40", default-features = false } tokio = { version = "1.27.0", default-features = false, features = ["rt-multi-thread", "macros", "process", "time"] } -tracing = { version = "0.1.37", default-features = false } +tracing = { version = "0.1.38", default-features = false } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt", "ansi"] } uuid = { version = "1.3.1", default-features = false, features = ["v4", "fast-rng", "serde"] } From a6a7b64f8d0c941a124ca7941814ea674987b81e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:07:40 +0000 Subject: [PATCH 2/7] Bump tokio from 1.27.0 to 1.28.0 Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.27.0 to 1.28.0. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.27.0...tokio-1.28.0) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56852db..f39bede 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1678,9 +1678,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", @@ -1691,14 +1691,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index fe3b70a..a691bab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ regex = "1.8.0" serde = { version = "1.0.160", default-features = false, features = ["derive"] } sha2 = { version = "0.10.6", default-features = false } thiserror = { version = "1.0.40", default-features = false } -tokio = { version = "1.27.0", default-features = false, features = ["rt-multi-thread", "macros", "process", "time"] } +tokio = { version = "1.28.0", default-features = false, features = ["rt-multi-thread", "macros", "process", "time"] } tracing = { version = "0.1.37", default-features = false } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt", "ansi"] } uuid = { version = "1.3.1", default-features = false, features = ["v4", "fast-rng", "serde"] } From b65c3f55413575f3a0e3d1b71d850cf28fe4fa17 Mon Sep 17 00:00:00 2001 From: Defelo Date: Wed, 26 Apr 2023 22:10:42 +0200 Subject: [PATCH 3/7] Add readme --- README.md | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 624a421..df19938 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,168 @@ -# sandkasten +[![check](https://github.com/Defelo/sandkasten/actions/workflows/check.yml/badge.svg)](https://github.com/Defelo/sandkasten/actions/workflows/check.yml) +[![test](https://github.com/Defelo/sandkasten/actions/workflows/test.yml/badge.svg)](https://github.com/Defelo/sandkasten/actions/workflows/test.yml) +[![docker](https://github.com/Defelo/sandkasten/actions/workflows/docker.yml/badge.svg)](https://github.com/Defelo/sandkasten/actions/workflows/docker.yml) +![Version](https://img.shields.io/github/v/tag/Defelo/sandkasten?include_prereleases&label=version) +[![dependency status](https://deps.rs/repo/github/Defelo/sandkasten/status.svg)](https://deps.rs/repo/github/Defelo/sandkasten) + +# Sandkasten Run untrusted code in an isolated environment + +## What is this? +Sandkasten is a code execution engine for running arbitrary untrusted/harmful code in a sandbox, +isolating it from both the host system and other Sandkasten jobs. A simple REST API allows uploading +and executing arbitrary programs, while also enabling the user to specify resource limits and +providing feedback on the actual resources used. This project was partly inspired by +[Piston](https://github.com/engineer-man/piston) and aims to solve some problems with it. + +## How does it work? +Sandkasten uses [nsjail](https://github.com/google/nsjail) to run programs in restricted +environments and to enforce the specified resource limits. Additionally +[GNU Time](https://www.gnu.org/software/time/) is used for reporting the resources used by the +program. Programs are always run in a chroot environment using nsjail, which contains only the +following directories: + +- `/nix/store` (ro mount from host) +- `/program` (rw in compile steps, ro in run steps) contains the compiled program +- `/box` (ro) current working directory which contains the specified files for compile/run steps +- `/tmp` (rw, tmpfs) +- some files in `/dev` and `/etc` which are needed for some packages to work properly + +Programs are uniquely identified using the hash value of their source files and selected +environments. If a program has been uploaded and compiled before and is then uploaded again, the +same program id is used and the existing compilation results can be used without having to recompile +the program. + +## Features +- [x] Compile and execute arbitrary programs. +- [x] Cache compilation results to avoid having to recompile the same programs for every time they + are run. +- [x] Set resource limits for both compile and run steps. +- [x] Report resource usage for both compile and run steps. +- [x] Packages are defined via Nix. +- [x] Programs are deleted automatically if they are not executed anymore. +- [x] Specify stdin, command line arguments and files in the working directory for run steps. + +### Planned/Ideas +- [ ] Specify environment variables for both compile and run steps. +- [ ] Communicate with running programs via websockets. +- [ ] Spawn multiple processes that can communicate with each other. +- [ ] JWTs for individual limits (+rate limits). +- [ ] Install packages at runtime via nix. +- [ ] Add more packages. + +## API Documentation +On a running Sandkasten instance, the API documentation is available on `/docs` and +`/redoc`. There is also an OpenAPI specification available on `/openapi.json`. + +## Public Instance +Not available (yet). + +## Setup instructions + +### NixOS Module + +1. Add this repository to your flake inputs: + ```nix + { + inputs.sandkasten.url = "github:Defelo/sandkasten"; + } + ``` +2. Add the module to your NixOS configuration: + ```nix + { + imports = [sandkasten.nixosModules.sandkasten]; + } + ``` +3. Configure the module: + ```nix + { + services.sandkasten = { + enable = true; + # example config: + host = "0.0.0.0"; + port = 8080; + max_concurrent_jobs = 16; + run_limits.time = 10; + # for a full list of configuration options, see `config.toml` + }; + } + ``` + +### Nix flake +```bash +CONFIG_PATH=config.toml nix run github:Defelo/sandkasten +``` + +### Docker +```bash +docker compose up -d +``` + +## Development + +### Setup instructions +The following components are needed for a working development environment: + +- [Rust](https://www.rust-lang.org/) (stable) toolchain +- [Nix](https://nixos.org/) with [flakes](https://nixos.wiki/wiki/Flakes) enabled + +If you also have [direnv](https://github.com/direnv/direnv) installed, you can just use +`direnv allow` to setup your shell for development. Otherwise you can also use `nix develop` +to enter a development shell. This will add some tools to your `PATH` and set a few environment +variables that are needed by Sandkasten and some of the integration tests. In the development shell +you can just use `cargo run` to start the application. + +### Unit tests +To run the unit tests, you can just use `cargo test`. This only requires you to have a working rust +toolchain, but you should not need to setup nix for this. + +### Integration tests +To run the integration tests, you can use `cargo test -F nix -- --ignored`. For this to work you +need to have a Sandkasten instance running on `127.0.0.1:8000`. You can also specify a different +instance via the `TARGET` environment variable. If you only want to run the integration tests that +do not require a nix development shell, you can omit the `-F nix`. In the development shell you can +also run the `integration-tests` command to automatically start a temporary sandkasten instance and +run the integration tests against it. + +### Packages +All packages are defined using nix expressions in +[nix/packages](https://github.com/Defelo/sandkasten/tree/develop/nix/packages). Each package has a +unique id, a human-readable name, a version, optionally a script to compile a program, a script to +run a program and a test program that is executed as part of the integration tests to ensure that +the package is working. When creating a new package, don't forget to add it to +[nix/packages/default.nix](https://github.com/Defelo/sandkasten/blob/develop/nix/packages/default.nix). + +#### Compile scripts +The compile script of a package is executed whenever a new program has been uploaded. When this +script is run, the current working directory (`/box`) contains all the source files and the command +line arguments contain the names of the source files in the same order as they were specified by +the client. It is assumed that the first file always provides the entrypoint into the program. The +purpose of the compile script is to compile the provided program and store the result (plus any +files that may be needed to run the program) in `/program`. + +If a package does not have a compile script, the source files are instead copied directly into the +program directory. + +#### Run scripts +The run script of a package is executed whenever a program is executed. When this script is run, the +current working directory (`/box`) contains the files that have been specified in the run step (if +any) and `/program` contains the files that have been produced by the corresponding compile script +previously (or the source files if the packages does not have a compile script). The first command +line argument is always the name of the first source file (which is assumed to be the entrypoint +into the program). In most cases, this is only relevant for interpreted languages (like Python) and +can be ignored for most compiled languages. All other command line arguments are the ones specified +by the client and should be forwarded to the actual program. + +#### Test program +Every package should provide a test program that checks the following: + +- Multiple source files are working (e.g. `first_file.py` can import `second_file.py`) +- Reading from stdin is working. The program should assert that the string `stdin` is read from + stdin. +- Command line arguments are working. The program should assert that the only three command line + arguments are `foo`, `bar` and `baz`. +- File system is working. The program should assert that the file `test.txt` in the current working + directory contains the string `hello world`. + +If any of these checks fails, the program should exit with a non-zero exit code. Otherwise, if all +checks passed, it should exit with exit code zero and print `OK` to stdout. From c95b472e1b9605b3fbb319fe541e2e4d8039591d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 09:15:12 +0000 Subject: [PATCH 4/7] Bump reqwest from 0.11.16 to 0.11.17 Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.16 to 0.11.17. - [Release notes](https://github.com/seanmonstar/reqwest/releases) - [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md) - [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.16...v0.11.17) --- updated-dependencies: - dependency-name: reqwest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56852db..7653a29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1296,9 +1296,9 @@ checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" dependencies = [ "base64 0.21.0", "bytes", diff --git a/Cargo.toml b/Cargo.toml index fe3b70a..bf70768 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ uuid = { version = "1.3.1", default-features = false, features = ["v4", "fast-rn [dev-dependencies] indoc = { version = "2.0.1", default-features = false } proptest = "1.1.0" -reqwest = { version = "0.11.16", default-features = false, features = ["json", "blocking"] } +reqwest = { version = "0.11.17", default-features = false, features = ["json", "blocking"] } [features] nix = [] From 36215d8d424c61ca9119bc6b813aa02a00a6bde4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 09:15:30 +0000 Subject: [PATCH 5/7] Bump anyhow from 1.0.70 to 1.0.71 Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.70 to 1.0.71. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.70...1.0.71) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56852db..8b7758a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,9 +57,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "async-trait" diff --git a/Cargo.toml b/Cargo.toml index fe3b70a..00b0fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ lto = true codegen-units = 1 [dependencies] -anyhow = { version = "1.0.70", default-features = false, features = ["std"] } +anyhow = { version = "1.0.71", default-features = false, features = ["std"] } config = { version = "0.13.3", default-features = false, features = ["toml", "json"] } key-rwlock = { version = "0.1.0", default-features = false } once_cell = { version = "1.17.1", default-features = false } From d5d850c104836ab1b2040db15c7d9aba357f3185 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 09:15:45 +0000 Subject: [PATCH 6/7] Bump uuid from 1.3.1 to 1.3.2 Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/1.3.1...1.3.2) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56852db..8fa44a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1889,9 +1889,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" dependencies = [ "getrandom", "rand", diff --git a/Cargo.toml b/Cargo.toml index fe3b70a..ce48beb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ thiserror = { version = "1.0.40", default-features = false } tokio = { version = "1.27.0", default-features = false, features = ["rt-multi-thread", "macros", "process", "time"] } tracing = { version = "0.1.37", default-features = false } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt", "ansi"] } -uuid = { version = "1.3.1", default-features = false, features = ["v4", "fast-rng", "serde"] } +uuid = { version = "1.3.2", default-features = false, features = ["v4", "fast-rng", "serde"] } [dev-dependencies] indoc = { version = "2.0.1", default-features = false } From 7f103db15aeb15c66e5992499eb489459e6b1fe3 Mon Sep 17 00:00:00 2001 From: Defelo Date: Tue, 2 May 2023 10:09:14 +0200 Subject: [PATCH 7/7] Update dependencies --- Cargo.lock | 5 +++-- Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1053064..28314cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1764,10 +1764,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index c828683..4e15aa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,12 +17,12 @@ poem = { version = "1.3.55", default-features = false, features = ["server"] } poem-ext = { version = "0.5.2", default-features = false } poem-openapi = { version = "2.0.26", default-features = false, features = ["swagger-ui", "redoc", "uuid"] } postcard = { version = "1.0.4", default-features = false, features = ["use-std"] } -regex = "1.8.0" +regex = "1.8.1" serde = { version = "1.0.160", default-features = false, features = ["derive"] } sha2 = { version = "0.10.6", default-features = false } thiserror = { version = "1.0.40", default-features = false } tokio = { version = "1.28.0", default-features = false, features = ["rt-multi-thread", "macros", "process", "time"] } -tracing = { version = "0.1.38", default-features = false } +tracing = { version = "0.1.37", default-features = false } tracing-subscriber = { version = "0.3.17", default-features = false, features = ["fmt", "ansi"] } uuid = { version = "1.3.2", default-features = false, features = ["v4", "fast-rng", "serde"] }