/safeexec

Primary LanguageCMIT LicenseMIT

safeexec(safe execution environment)

A general-purpose lightweight sandbox for safely executing user programs. safeexec provides a sandbox environment which you can set limits on system resources like memory, cpu time effectively preventing intended or unintented breach of system. However, running untrusted code on your server should be taken at your own risk.

How it works

  • Safeexec forks child process.
  • Sets various limitations on the child process via setrlimit. So safeexec should have setuid bit on, and uid root.
  • Parent process polls memory usage looking into /proc filesystem on linux or via kvm on freebsd.
  • Wall clock limit is enforced with alarm() & signal().
  • Reports usage stats to stdout or file specified by --usage argument.

Resource usage report format

Resource usage is in 4 line plain text.

For memory limit:

MLE
ELAPSED_TIME: 3 s
MEMORY_USED: 40470 kB
CPU_TIME: 0.002 s

For time limit:

TLE
ELAPSED_TIME: 3 s
MEMORY_USED: 2070 kB
CPU_TIME: 1.002 s

File write attempt:

Command exited with non-zero status (1)
ELAPSED_TIME: 2 s
MEMORY_USED: 7342 kB
CPU_TIME: 1.052 s

Usage notes

Running it with no argument gives:

usage: ./safeexec <options> --exec <command>
Available options:
	--cpu     <seconds>           Default: 1 second(s)
	--mem     <kbytes>            Default: 32768 kbyte(s)
	--space   <kbytes>            Default: 0 kbyte(s)
	--uids    <minuid> <maxuid>   Default: 5000-65535
	--minuid  <uid>               Default: 5000
	--maxuid  <uid>               Default: 65535
	--core    <kbytes>            Default: 0 kbyte(s)
	--nproc   <number>            Default: 0 proccess(es)
	--fsize   <kbytes>            Default: 8192 kbyte(s)
	--stack   <kbytes>            Default: 8192 kbyte(s)
	--clock   <seconds>           Wall clock timeout (default: 10)
	--usage   <filename>          Report statistics to ... (default: stderr)
	--chroot  <path>              Directory to chrooted (default: /tmp)
	--error   <path>              Print stderr to file (default: /dev/null)

Keep in mind that --exec must be last option.

Memory limits

For compiled languages like C or C++ you can set strict memory limitation. But for interpreted languages(python, ruby, java etc..) you can't set strict memory limits. Instead you have to give big enough memory that your language can load.

Unpriviliged uid range

Safeexec runs users program as a random uid. Default gid/uid range is: 5000-65535. So please check if any uid in this range is in use. If then you can chage the range with arguments. --minuid and --maxuid. Whole user range is treated as "unpriviliged" ("other" for all files).

Network access

On Freebsd network access blocked by setting limit on socket buffer size. But on Linux it doesn't block network access. Use other tools(iptables,..) for network access blockage.

Number of open files

Maximum number of open files is not limited because vm(java) or interpreted(ruby, python) languages open a lots of files.

Chroot

From ubuntu's BasicChroot page:

A chroot is basically a special directory on your computer which prevents applications, if run from inside that directory, from accessing files outside the directory. In many ways, a chroot is like installing another operating system inside your existing operating system. Technically-speaking, chroot temporarily changes the root directory (which is normally /) to the chroot directory (for example, /var/chroot). As the root directory is the top of the filesystem hierarchy, applications are unable to access directories higher up than the root directory, and so are isolated from the rest of the system. This prevents applications inside the chroot from interfering with files elsewhere on your computer.

You can use safeexec without chroot if your OS doesn't allow arbitrary users to write filesystems.

Build

Clone this repository and run:

% cmake .
% make

Install

You'll need root to install binary to set root setuid bit.

% sudo make install

Test

% sudo make permission && make test

Running tests...
Test project /usr/home/ochko/safeexec
    Start 1: memory-limit-exceeded
1/8 Test #1: memory-limit-exceeded ............   Passed    1.16 sec
    Start 2: time-limit-exceeded
2/8 Test #2: time-limit-exceeded ..............   Passed    2.10 sec
    Start 3: output-limit-exceeded
3/8 Test #3: output-limit-exceeded ............   Passed    2.10 sec
    Start 4: fork-attemp
4/8 Test #4: fork-attemp ......................   Passed    0.59 sec
    Start 5: file-write-attemp
5/8 Test #5: file-write-attemp ................   Passed    0.13 sec
    Start 6: file-read-attemp
6/8 Test #6: file-read-attemp .................   Passed    0.13 sec
    Start 7: return-code
7/8 Test #7: return-code ......................   Passed    0.13 sec
    Start 8: wall-time-limit-exceeded
8/8 Test #8: wall-time-limit-exceeded .........   Passed    3.13 sec

100% tests passed, 0 tests failed out of 8

Total Test time (real) =   9.48 sec

Todo

  • Fix file read/write permissions on linux when file owner is test runner and group permission is on:
    • can read chmod 0640 file, but can't read 0620 file.
    • can write chmod 0620 file, but can't write 0644 file.
  • Use Getopt similar argument parsing library.

Initially Cloned from https://github.com/ochko/safeexec.git