{"id":31333386,"url":"https://github.com/kernc/sandbox-venv","last_synced_at":"2025-09-26T01:57:18.542Z","repository":{"id":310409804,"uuid":"1035230572","full_name":"kernc/sandbox-venv","owner":"kernc","description":"Secure container sandbox Python virtualenv wrapper","archived":false,"fork":false,"pushed_at":"2025-09-25T02:25:50.000Z","size":82,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-25T04:28:29.497Z","etag":null,"topics":["bubblewrap","bwrap","container","firejail","namespaces","opsec","posix-sh","python","sandbox","sandboxing","secure","security","security-tools","venv","venv-python","virtualenv"],"latest_commit_sha":null,"homepage":"https://github.com/kernc/sandbox-venv/","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kernc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-09T23:53:46.000Z","updated_at":"2025-09-25T02:25:48.000Z","dependencies_parsed_at":"2025-09-25T04:16:26.007Z","dependency_job_id":"6c59e4bd-45ed-4532-91d3-8fdda70dfdc3","html_url":"https://github.com/kernc/sandbox-venv","commit_stats":null,"previous_names":["kernc/sandbox-venv"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/kernc/sandbox-venv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kernc%2Fsandbox-venv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kernc%2Fsandbox-venv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kernc%2Fsandbox-venv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kernc%2Fsandbox-venv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kernc","download_url":"https://codeload.github.com/kernc/sandbox-venv/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kernc%2Fsandbox-venv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":277009877,"owners_count":25744543,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-25T02:00:09.612Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bubblewrap","bwrap","container","firejail","namespaces","opsec","posix-sh","python","sandbox","sandboxing","secure","security","security-tools","venv","venv-python","virtualenv"],"created_at":"2025-09-26T01:57:15.592Z","updated_at":"2025-09-26T01:57:18.536Z","avatar_url":"https://github.com/kernc.png","language":"Shell","funding_links":["https://github.com/sponsors/kernc"],"categories":[],"sub_categories":[],"readme":"sandbox-venv: secure container virtualenv wrapper\n=================================================\n\n[![Build status](https://img.shields.io/github/actions/workflow/status/kernc/sandbox-venv/ci.yml?branch=master\u0026style=for-the-badge)](https://github.com/kernc/sandbox-venv/actions)\n[![Language: shell / Bash](https://img.shields.io/badge/lang-Shell-peachpuff?style=for-the-badge)](https://github.com/kernc/sandbox-venv)\n[![Source lines of code](https://img.shields.io/endpoint?url=https%3A%2F%2Fghloc.vercel.app%2Fapi%2Fkernc%2Fsandbox-venv%2Fbadge?filter=.sh%26format=human\u0026style=for-the-badge\u0026label=SLOC\u0026color=skyblue)](https://ghloc.vercel.app/kernc/sandbox-venv)\n[![Script size](https://img.shields.io/github/size/kernc/sandbox-venv/build/sandbox-venv?style=for-the-badge\u0026color=skyblue)](https://github.com/kernc/sandbox-venv)\n[![Issues](https://img.shields.io/github/issues/kernc/sandbox-venv?style=for-the-badge)](https://github.com/kernc/sandbox-venv/issues)\n[![Sponsors](https://img.shields.io/github/sponsors/kernc?color=pink\u0026style=for-the-badge)](https://github.com/sponsors/kernc)\n\n\n#### Problem statement\n\nPython virtual environments (package\n[`virtualenv`](https://virtualenv.pypa.io/en/latest/)\nor built-in module\n[venv](https://docs.python.org/3/library/venv.html))\nisolate your project’s interpreter and dependencies, but they offer\n**no security or execution sandboxing** like a virtual machine or a Docker\ncontainer would. Therefore, running virtualenv Python programs as-is (unsecured),\n**any [rogue dependency](https://www.google.com/search?q=malicious+python+packages\u0026tbm=nws)\\*\n🎯 or [hacked library code](https://www.google.com/search?q=(hacked+OR+hijacked+OR+backdoored+OR+\"supply+chain+attack\")+(npm+OR+pypi)\u0026tbm=nws\u0026num=100)\n:pirate_flag: ([et cet.](https://slsa.dev/spec/draft/threats-overview) :warning:)\ncan wreak havoc, including access all your private parts** :bangbang:—think\ncurrent user's credentials and personal bits like:\n* `~/.ssh/id_ed25519`,\n* `~/.pki/nssdb/`,\n* `~/.mozilla/firefox/\u003cprofile\u003e/key4.db`,\n* `~/.mozilla/firefox/\u003cprofile\u003e/formhistory.sqlite` ...\n\n\u003csub\u003e✱ Installing something as seemingly harmless as the popular **package _poetry_**\npulls in [nearly a hundred dependencies or over 70 MB](doc/deps-stats.txt)\nof Python sources! 😬\u003c/sub\u003e\n\nIn someone else's words:\n\n\u003e\u003e Using virtualenv is more secure?\n\u003e \n\u003e [No. Not in the slightest.](https://www.reddit.com/r/Python/comments/5sm6zm/using_virtualenv_is_more_secure/)\n\n#### Solution\n\nIn order to execute installed Python programs in secure virtual environments,\none is better advised to either look to OS VM primitives like those provided by Docker\nand [containers](https://github.com/containers/), e.g.:\n```shell\npodman run -it -v .:/src python:3 bash  # ...\n```\nThe simpler alternative is **automatic lightweight container wrapping with\n[bubblewrap](https://github.com/containers/bubblewrap)** (of\n[Flatpak](https://en.wikipedia.org/wiki/Flatpak) fame)\nusing `sandbox-venv` script from this repo.\n\n\nInstallation\n------------\nThere are **no dependencies other than a POSIX shell** with\n[its standard set of utilities](https://en.wikipedia.org/wiki/List_of_POSIX_commands)\n**and `bubblewrap`**.\nThe installation process, as well as the script runtime,\nshould behave similarly on all relevant compute platforms,\nincluding GNU/Linux and even\n[Windos/WSL](https://learn.microsoft.com/en-us/windows/wsl/install). 🤞\n\n```shell\n# Install the few, unlikely to be missing dependencies, e.g.\nsudo apt install coreutils binutils bubblewrap libseccomp2 python3\n# A working XDG Desktop Portal is recommended to xdg-open hyperlinks\nsudo apt install xdg-dbus-proxy xdg-desktop-portal*  # Note: only need one\n\n# Download the script and put it somewhere on PATH\ncurl -vL 'https://bit.ly/sandbox-venv' | sudo tee /usr/local/bin/sandbox-venv\nsudo chmod +x /usr/local/bin/sandbox-venv  # Mark executable\n\nsandbox-venv --help\n# Usage: sandbox-venv [VENV_DIR] [BWRAP_OPTS]\nsandbox-venv path/to/my-project/.venv\n```\n\nUsage\n-----\nWhenever you create a new virtual environment,\nsimply invoke `sandbox-venv` on it afterwards, e.g.:\n```shell\ncd project\npython -m venv .venv  # Create a new project virtualenv\nsandbox-venv .venv    # Passing virtualenv dir is optional; defaults to \".venv\"\n```\nFrom now on, directory _.venv_ and everything under it\n(in particular, everything in the _bin_ folder,\ne.g. `.venv/bin/python`, `.venv/bin/pip` etc.)\nsets up and transparently runs in a secure container sandbox.\n\n\n#### Extra Bubblewrap arguments\n\nOther than the optional virtualenv dir, **all arguments** initially passed to\n`sandbox-venv` are **forwarded to bubblewrap**. See `bubblewrap --help` or\n[`man 1 bwrap`](https://manpages.debian.org/unstable/bwrap). You can also pass additional bubblewrap arguments to individual\nprocess invocations via **`$BWRAP_ARGS` environment variable**. E.g.:\n\n```sh\nBWRAP_ARGS='--bind /lib /lib' \\\n    python -c 'import os; print(os.listdir(\"/lib\"))'\n```\n\nTo run the sandboxed process as **superuser**\n(while still retaining all the security functionality of the container sandbox),\ne.g. to open privileged ports, use args:\n\n    --uid 0 --cap-add cap_net_bind_service\n\n\n#### Filesystem mounts\n\nThe directory that contains your venv dir, i.e. `.venv/..` or\n**the project directory, is mounted with read-write permissions**,\nwhile everything else (including `project/.git`)\nis mounted read-only. In addition:\n\n* `\"$venv/cache\"` is bind-mounted as `\"$HOME/.cache\"`\n* `\"$HOME/.cache/pip\"` is bind-mounted as `\"$HOME/.cache/pip\"`\n  (only if environment variable `SANDBOX_USE_PIP_CACHE=` is set as this may\n  enable cache poisoning attachs).\n\nTo mount extra endpoints, use Bubblewrap switches `--bind` or `--bind-ro`.\nAnything else not explicitly mounted by an extra CLI switch\nis lost upon container termination.\n\n\n#### Linux Seccomp\n\nLinux kernel `seccomp` facility for restricting syscalls is\n**automatically enabled when the appropriate package is\navailable**—`apt install libseccomp2 python3-seccomp` (requires virtualenv with `--system-site-packages`)\nor `pip install pyseccomp` (also requires `libseccomp2`).\nThe initializing module `sitecustomize.py` installs a filter\nthat thereafter only allows syscalls listed in the environment variable\n`SANDBOX_SECCOMP_ALLOW=`\n(or, by default, some 200 syscalls that should cover all non-special cases).\nYou can populate the variable at runtime with a custom, stricter syscalls list\nor set it to blank\n(i.e. `export SANDBOX_SECCOMP_ALLOW=`) to force-disable seccomp completely.\n\n\n#### Runtime monitoring\n\nIf **environment variable `VERBOSE=`** is set to a non-empty value,\nthe full `bwrap` command line is emitted to stderr before execution.\n\nYou can list bubblewraped processes using the\n[command `lsns`](https://manpages.debian.org/unstable/lsns)\nor the following shell function:\n\n```sh\nlist_bwrap () { lsns -u -W | { IFS= read header; echo \"$header\"; grep bwrap; }; }\n\nlist_bwrap  # Function call\n```\n\nYou can run `$venv/bin/shell` to spawn **interactive shell inside the sandbox**.\n\n\n#### Environment variables\n\n* `BWRAP_ARGS=`– Extra arguments passed to `bwrap` process; space or line-delimited (if arguments such as paths themselves contain spaces).\n* `SANDBOX_SECCOMP_ALLOW=`– Space-separated list of system calls allowed by the installed Linux seccomp filter. Requires `libseccomp2` and `python3-seccomp` / Python package `pyseccomp`\n* `SANDBOX_USE_PIP_CACHE=`– Mount current user's `$HOME/.cache/pip` inside the sandbox. Insecure, but can be used to cache large, trusted package downloads. Otherwise, pip is always invoked with a clean cache as if `pip --no-cache-dir ...` were used.\n* `VERBOSE=`– Print full `exec bwrap` command line right before execution.\n\n\nExamples\n--------\nTo install a heavy package that requires a compiler, it is easiest to\nsupply it with full _/usr_ and _/lib_:\n```sh\nBWRAP_ARGS='--ro-bind /usr /usr --ro-bind /lib /lib'  pip install ...\n```\n\nTo pass extra environment variables, other than those filtered by default,\nuse `bwrap --setenv`, e.g.:\n```sh\nBWRAP_ARGS='--setenv OPENAI_API_KEY c4f3b4b3'  my-ai-prog\n```\n\nTo run GUI (X11) apps, some prior success was achieved using e.g.:\n```sh\nBWRAP_ARGS='--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X8 --setenv DISPLAY :8'\n```\nSee [more examples on the ArchWiki](https://wiki.archlinux.org/title/Bubblewrap#Using_X11).\n\n\nSecurity Model\n--------------\nEntrypoints in `$venv/bin` are wrapped to `exec bwrap`\nso that every invocation runs inside a fresh Bubblewrap container.\n\n`$venv/bin/pip` wrapper re-wraps any newly created executables in `$venv/bin`,\nensuring they always use the wrapped `$venv/bin/python`.\n\nRather than giving the sandbox full filesystem access,\nminimal shared library (`*.so`) dependencies are collected and made available inside the container,\nas well as specific host binaries (e.g. `/usr/bin/python3`, `/usr/bin/git`, `/bin/sh` etc.)—actual\nruntime behavior depends on this list and which paths exist on the host.\nMost paths are mounted read-only, while the project directory\n(sans its `.venv`, `.git`) is mounted read-write.\n\nOptionally, a seccomp filter is installed at Python startup using the `sitecustomize` mechanism.\nThe `BWRAP_ARGS=` environment variable lets you extend or relax the sandbox at runtime.\n\nPaths inside the sandbox mirror the host paths, potentially exposing your username,\ndirectory layout etc. This was done for simplicity—pull requests appreciated!\n\n```mermaid\nflowchart TB\n  Attacker[Malicious package /\n           rogue dependency]\n  Attacker --\u003e|filesystem access| TryFS\n  Attacker --\u003e|use Linux syscalls| TrySys\n  Attacker --\u003e|network bind| TryNet\n\n  subgraph Threats\n    TryFS[try to read ~/.ssh, /etc,\n          or other host secrets]\n    TrySys[call a forbidden /\n           privileged syscall]\n    TryNet[bind to a privileged port]\n  end\n\n  TryFS --\u003e|failure| FS\n  TrySys --\u003e|blocked| Seccomp[seccomp sandbox]\n  TryNet --\u003e|optional| Caps\n\n  subgraph Mitigations\n    FS[**Mount policy**: project dir is RW;\n       everything else is RO or absent]\n    Seccomp[**seccomp** syscall whitelist\n            \u003ccode\u003eSANDBOX_SECCOMP_ALLOW=\u003c/code\u003e]\n    Caps[capabilities / UID mapping\n         controlled by \u003ccode\u003e\u003cb\u003eBWRAP_ARGS=\u003c/b\u003e\u003c/code\u003e]\n  end\n```\n\n\nViable alternatives\n-------------------\n1. A popular alternative are the aforementioned Docker/OCI containers\n   and manual management of their runtime. This comes free when the\n   worked on project itself deals in\n   [Continerfiles](https://manpages.debian.org/unstable/Containerfile). \n2. On Linux, [AppArmor](https://apparmor.net), even with\n   [apparmor.d](https://github.com/roddhjav/apparmor.d)\n   applied, doesn't ship a generic `python` profile, so one would go\n   through direct `aa-exec --profile my-custom-env`, but writing\n   custom AppArmor profiles is less common than simply using containers.\n3. [Firejail](https://github.com/netblue30/firejail/).\n   An indie C project with virtually no dependencies (which\n   [\u003cdel\u003eRed Hat\u003c/del\u003e\u003cins\u003eIBM\u003c/ins\u003e has a reasonable position on](https://github.com/containers/bubblewrap?tab=readme-ov-file#related-project-comparison-firejail))\n   that sets up its own sandbox. I guess it's a matter of trust.\n   Similarly to AppArmor, requires writing a custom profile.\n4. A custom\n   [`seccomp` initialization script](https://healeycodes.com/running-untrusted-python-code),\n   executed at interpreter startup using\n   [~~`PYTHONSTARTUP=`~~](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSTARTUP)\n   [`sitecustomize`](https://docs.python.org/3/library/site.html#module-sitecustomize)\n   startup hook.\n5. On macOS, [`sandbox-exec`](https://igorstechnoclub.com/sandbox-exec/)\n   or Apple Containerization®.\n\nIn comparison to the above, `sandbox-venv` is like `chroot` on steroids.\nIt uses the same isolation primitives that containers use\n(process sandbox via Linux namespaces, isolated filesystem view),\nbut without all of the container runtime baggage—YMMV.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkernc%2Fsandbox-venv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkernc%2Fsandbox-venv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkernc%2Fsandbox-venv/lists"}