Skip to main content

A sandbox/supervisor for python modules.

Project description

secimport

secimport

Secure import for python modules using dtrace under the hood.
Medium Article

secimport can be used to:

  • Confine/Restrict specific python modules inside your production environment.
    • Open Source, 3rd party from unstrusted sources.
    • Audit the flow of your python application at user-space/os/kernel level.
  • Run an entire python application under unified configuration
    • Like seccomp but not limited to Linux kernels. Cross platform.

Quick Start

For the full list of examples, see EXAMPLES.md.

YAML Template Example

modules:
  requests:
    destructive: true
    syscall_allowlist:
      - write
      - ioctl
      ...
      - stat64
  fastapi:
    destructive: true
    syscall_allowlist:
      - bind
      - fchmod
      ...
      - stat64
  uvicorn:
    destructive: true
    syscall_allowlist:
      - getpeername
      - getpgrp
      ...
      - stat64

See YAML Profiles Usage

Python Shell Interactive Example

Python 3.10.0 (default, May  2 2022, 21:43:20) [Clang 13.0.0 (clang-1300.0.27.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

# Let's import subprocess module, limiting it's syscall access.
>>> import secimport
>>> subprocess = secimport.secure_import("subprocess")

# Let's import os 
>>> import os
>>> os.system("ps")
  PID TTY           TIME CMD
 2022 ttys000    0:00.61 /bin/zsh -l
50092 ttys001    0:04.66 /bin/zsh -l
75860 ttys001    0:00.13 python
0
# It worked as expected, returning exit code 0.


# Now, let's try to invoke the same logic using a different module, "subprocess", that was imported using secure_import:
>>> subprocess.check_call('ps')
[1]    75860 killed     python

# Damn! That's cool.
  • The dtrace profile for the module is saved under:
    • /tmp/.secimport/sandbox_subprocess.d:
  • The log file for this module is under
    • /tmp/.secimport/sandbox_subprocess.log:
      ...
      
      (OPENING SHELL using posix_spawn): (pid 75860) (thread 344676) (user 501) (python module: <stdin>) (probe mod=, name=entry, prov=syscall func=posix_spawn) /bin/sh 
          #posix_spawn,
      
      (TOUCHING FILESYSTEM): write(140339021606912) from thread 344676
                  libsystem_kernel.dylib`__fork+0xb
                  _posixsubprocess.cpython-310-darwin.so`do_fork_exec+0x29
                  _posixsubprocess.cpython-310-darwin.so`subprocess_fork_exec+0x71f
                  python.exe`cfunction_call+0x86
      killing...
      killed.
      

Shell blocking

# example.py - Executes code upon import;
import os;

os.system('Hello World!');
# production.py - Your production code
from secimport import secure_import 

example = secure_import('example', allow_shells=False)

Let's run the and see what happens:

(root) sh-3.2#  export PYTHONPATH=$(pwd)/src:$(pwd)/examples:$(pwd):$PYTHONPATH
(root) sh-3.2#  python examples/production.py 
Successfully compiled dtrace profile:  /tmp/.secimport/sandbox_example.d
Killed: 9
  • We imported example with limited capabilities.
  • If a syscall like spawn/exec/fork/forkexec will be executed
    • The process will be killed with -9 signal.

Network blocking

>>> import requests
>>> requests.get('https://google.com')
<Response [200]>
  

>>> from secimport import secure_import
>>> requests = secure_import('requests', allow_networking=False)

# The next call should kill the process,
# because we disallowed networking for the requests module.
>>> requests.get('https://google.com')
[1]    86664 killed

Requirements

The only requirement is a Python interpreter that was built with --with-dtrace.

  • See INSTALL.md for a detailed setup from scratch.
  • pip
    • python3 -m pip install secimport
  • Poetry
    • python3 -m pip install poetry && python3 -m poetry build

Tests

python -m pytest

Log4Shell as an example

Not related for python, but for the sake of explanation (Equivilant Demo soon).

  • Log4Shell - CVE-2021-44228
    • Let's say we want to block log4j from doing crazy things.
    • In the following import we deny log4j from opening an LDAP connection / shell:
      • log4j = secure_import('log4j', allow_shells=False, allow_networking=False)
    • This would disable log4j from opening sockets and execute commands, IN THE KERNEL.
    • You can choose any policy you like for any module.

Useful References

TODO:

  • Node support (dtrace hooks)
  • Go support (dtrace hooks)
  • Allow/Block list configuration
  • Use current_module_str together with thread ID
  • Create a .yaml configuration per module in the code
    • Use secimport to compile that yml
    • Create a single dcript policy
    • Run an application with that policy using dtrace, without using secure_import

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

secimport-0.4.0.tar.gz (15.6 kB view hashes)

Uploaded Source

Built Distribution

secimport-0.4.0-py3-none-any.whl (20.1 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page