secure-javascript-sandbox

module
v0.0.0-...-e14d18c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 5, 2021 License: MIT

README

Secure JavaScript Sandbox

"Build a simple CLI tool that deploys & runs arbitrary JavaScript files in a secure isolated process or thread."

We want to execute untrusted third party JavaScript code in a secure manner, managing the execution through a command-line interface (CLI).

We use Docker as our sandboxing technology (mechanism for running untrusted code). Code will be ran in isolated container processes, which we will start, clean up and monitor through Docker. Using Docker instead of virtual machines will give faster startup times, though at the cost of some extra security concerns.

We implemented the CLI and sandbox launcher in Go, a cross-platform compiled language similar to C.

We assume that most code will be ran using a Node.js runtime (invoked with node or npm). Nevertheless other JavaScript runtimes and engines are supported through Docker images.

Usage

Run a JavaScript file using Node.js and see the results after the sandbox completes running

sjs run test/javascript/hello-world.js

Pass custom command line arguments

sjs run test/javascript/mod-exp.js --args "65 17 3233"

Run a file that processes data using a third party API (requires Internet access)

sjs run test/javascript/derive-age.js --args "'{ \"name\": \"tomas\" }'"

Run an Express.js web server with npm dependencies that provides temporary access to a user's data
sjs run test/javascript/temporary-link \
--cmd="/bin/bash -c \"npm install . && npm run start -- '{'name':'ElonMusk', 'email': 'elon.at.boringcompany.com', 'dateOfBirth': '110001' }'\"" \
--ports "3000:3005" \
--env PORT=3005 \
--env HOST=0.0.0.0

Use curl localhost:3000/mydata to get your data. The web server shuts down after the first request. Note that a custom start command, ports to publish, and environment variables can be supplied.

Development

System Design
Command-line interface

We use the third party Kong command-line parser for parsing subcommands and flags. This module provides advantages over Go's flag module, such as making it easier to have a complex CLI similar to Docker without much effort, which is what we are after.

Sandboxing

We pull the specified image. We create a Docker container, overriding the start command to execute the scripts. We copy the untrusted user files to the Read-Write layer of the Docker container. We start the container, thus executing the user's files.

Alternatively we could create a new image, with the user's files as the top new layer. This way we could re-run the containers easily from an image.

Code layout

We follow https://github.com/golang-standards/project-layout for our Go project structure.

Testing

For small testable functions that do branching we write Go unit tests using the testing package.

For carrying out integration tests (CLI with Docker from the user's point of view) we use BATS, a bash testing tool. This allows us to run commands in bash and check that processes have a non-error exit status and have certain output in stdout.

Security

Greater security precautions must be made for docker than for virtual machines.

All containers without a --network specified, are attached to the default bridge network. This can be a risk, as unrelated stacks/services/containers are then able to communicate.

For example running the temporary-link web server in two sandboxes with the default network, it is possible to shell into one of the containers and send a request to another container's web server.

Thus for each container we create a random bridge network, so that no two containers should share the same network and be able to push data to one another.

Alternatively we can run the docker daemon with --icc=false (disable inter-container communication).

Containers shouldn't run as a root user.

We specify our user as "node". Extra permissions should be granted using "--cap-add".

The docker daemon needs root access and thus can be dangerous. The daemon can be run in experimental rootless mode.

Management of docker installation can be performed using Ansible, i.e. https://github.com/konstruktoid/ansible-docker-rootless can install rootless docker. If connecting to a remote docker daemon, configure the connection to use SSH or TLS. The underlying operating system can be further protected with Linux Security Modules (i.e. AppArmor).

Images can be verified before being pulled and run.

This becomes important once the user can specify which image they want, i.e. which Node version or which programming environment.

Future enhancements

  • Improve cmd and args shell character escaping. Right now it's not possible to pass valid JSON via command-line arguments (see the Express.js example). Double quotes must be escaped. The whole expression is hard to read.
  • Real time feed of stdout and stderr from sandboxes.
  • Sandbox management from the CLI - list, delete, stop, logs, clone, edit, environment.
  • More security precautions. Automated security tests. Docker daemon configuration

Directories

Path Synopsis
cmd
sjs
internal
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL