Skip to content

Python

SCONE supports running Python programs inside of SGX enclaves. We maintain Docker images for various Python versions / Python engines. We have most of the current Python versions in repo registry.scontain.com/sconecuratedimages/python.

  • Latest Python: registry.scontain.com/sconecuratedimages/python:3-alpine

This Python repo requires a subscription.

PyPy for SCONE

PyPySCONE's speed is close to PyPy ("just in time Python") and in almost all SpeedCenter benchmarks is PyPy inside an enclave faster than native Python.

Workflow

SCONE supports the typical Docker workflow to create Docker images that contain the Python engine as well as the Python program. SCONE supports the encryption of the Python programs to ensure both the confidentiality as well as the integrity of the programs. A typical workflow might look like this:

SCONE Python Workflow

We at scontain.com maintain the SCONE-Python image and push this to registry.scontain.com. This image can be used by authorized application developers to add encrypted Python programs and encrypted libraries. The SCONE runtime of the Python engine needs to get access to the encryption key to be able to decrypt transparently the Python scripts for the Python engine. To do so, the application developer defines a security policy that ensures that only the Python engine that executes the application of the application provider gets access to this encryption key.

When the Python engine starts completely inside of an SGX enclave. The SCONE runtime transparently attests the Python engine as well as the filesystem: Only if both the Python engine has the expected MrEnclave as well as the filesystem state is exactly as expected, the SCONE runtime gets the encryption key from the SCONE CAS (Configuration and Attestation Service). The application developer therefore adds the expected MrEnclave and the initial filesystem state in form of a security policy to SCONE CAS.

Complex Workflows

SCONE supports more complex workflows in which the user can also specify encrypted volumes for input as well as output data. We explain a more complex example in the context of our blender use case.

Image

Getting access to Python Images

You need access to a private SCONTAIN hub repository registry.scontain.com/sconecuratedimages/python and registry.scontain.com/sconecuratedimages/apps (for Python 2) to be able to evaluate our Python images. Please register a free account at gitlab.scontain.com.

Currently, we provide images with Python 3.5 - 3.10 that are based on the standard alpine Python images and on glibc distribution (Ubuntu/debian) Python3.8.

docker pull registry.scontain.com/sconecuratedimages/python:3.10.6-alpine3.16
docker pull registry.scontain.com/sconecuratedimages/python:3.8-bullseye
docker pull registry.scontain.com/sconecuratedimages/python:3.8-ubuntu20.04

Also, we provide a simple Python2.7 image that is based on the standard Python image python:2.7-alpine.

You can pull this image as follows:

docker pull registry.scontain.com/sconecuratedimages/apps:python-2.7-alpine3.6

Python Interpreter

Thinking about Python in production?

For production please use singelton.

First, we determine which SGX device to mount with function determine_sgx_device. All following commands assume that MOUNT_SGXDEVICE was set by determine_sgx_device.

To run the Python interpreter inside an enclave in interactive mode, first start the container:

determine_sgx_device
docker run --rm -it $MOUNT_SGXDEVICE registry.scontain.com/sconecuratedimages/python:3.10.6-alpine3.16 sh

The execute python inside of the container:

SCONE_ALLOW_DLOPEN=2 SCONE_HEAP=256M SCONE_VERSION=1 python3

Since we set SCONE_VERSION=1, we get the following outputs1:

SCONE_QUEUES=4
SCONE_SLOTS=256
SCONE_SIGPIPE=0
SCONE_MMAP32BIT=0
SCONE_SSPINS=100
SCONE_SSLEEP=4000
SCONE_CONFIG=/etc/sgx-musl.conf
SCONE_TCS=8
SCONE_LOG=WARNING
SCONE_HEAP=268435456
SCONE_STACK=2097152
SCONE_ESPINS=10000
SCONE_MODE=hw
SCONE_ALLOW_DLOPEN=yes (unprotected)
SCONE_MPROTECT=no
SCONE_FORK=no
SCONE_FORK_OS=0

SCONE_CONFIG_ID: <not specified>
musl version: 1.2.4
SCONE version: 5.8.0 (2023-06-08 15:13:27)
Enclave hash: 345bc79902e646d52d9271d0509da045550f537f7e1d7b8a4da8d6805733718c
[SCONE|WARN] src/enclave/dispatch.c:136:print_runtime_info(): Application runs in SGX debug mode. Its memory can be read from outside the enclave with a debugger! This is not secure!
Python 3.10.6 (main, Aug 10 2022, 00:19:55) [GCC 11.2.1 20220219] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Potential error messages:

Could not create enclave: Error opening SGX device

Your machine / container does not support SGX. Set mode to automatic via SCONE_MODE=AUTO: in AUTO mode, SCONE will use SGX enclaves when available and emulation mode otherwise.

Killed

Your machine / container has most likely too little memory: the Linux OOM (Out Of Memory) killer, terminated your program. Try to reduce memory size by reducing environment variable SCONE_HEAP appropriately.

The meaning of protected versus unprotected library is explained in the faq.

Running an application

Say, you have a Python application called myapp.py in your current directory. To execute this with Pyhton 3 inside an enclave, you need to set some environment variables.

  • To run Python inside of an enclave, you can set the environment variable SCONE_MODE=HW and SCONE_ALPINE=1.

  • To issue some debug messages that show that we are running inside an enclave, set SCONE_VERSION=1

  • In general, we only permit the loading of dynamic libraries during the startup of a program - these libraries are part of MRENCLAVE, i.e., the hash of the enclave. To enable the loading of dynamic libraries after startup (and without requiring the authentication of this library via the file shield), one can set SCONE_ALLOW_DLOPEN=2. For operations, the environment variables are set by the CAS and you must set SCONE_ALLOW_DLOPEN either to SCONE_ALLOW_DLOPEN=1 to enable loading of dynamic libraries or must not define SCONE_ALLOW_DLOPEN.

  • Python applications often require large heaps and large stacks. The current SGX CPUs (SGXv1) do not permit to increase the size of enclaves dynamically. This implies that enclaves might run out of memory if the initial enclave size was set to small. Selecting large enclave size by default would result in long startup times for all programs. SCONE permits to set the heap size via environment variable SCONE_HEAP and the stack size via STACK_SIZE at startup.

Python program exits

Python does not always deal gracefully with out of memory situations: often, it terminates with some misleading error message if Python runs out of heap or stack memory. Please try to give python sufficient stack and heap size if this happens. We recommend to start with a large heap, like, SCONE_HEAP=256M to ensure that Python has sufficient heap. If your program runs without any problem with a large heap, you can try to reduce the heap size to speedup program startup times.**

Note that you can set the environment variable of a process - in our case python - running inside a container with docker option -e:

docker run --rm $MOUNT_SGXDEVICE -v "$PWD":/usr/src/myapp -w /usr/src/myapp -e SCONE_HEAP=256M -e SCONE_MODE=HW -e SCONE_ALLOW_DLOPEN=2 -e SCONE_ALPINE=1 -e SCONE_VERSION=1 registry.scontain.com/sconecuratedimages/python:3.10.6-alpine3.16b python3 myapp.py

Will produce an output like

SCONE_QUEUES=4
SCONE_SLOTS=256
SCONE_SIGPIPE=0
SCONE_MMAP32BIT=0
...

NumPy

Let's see how we can install some extra packages that your python program might need. Let us focus on NumPy first, a very popular package for scientific computing. Note that the following steps you would typically perform as part of a Dockerfile.

docker run $MOUNT_SGXDEVICE -it --rm rregistry.scontain.com/sconecuratedimages/python:3.10.6-alpine3.16 sh

This is a minimal image and you need to add some packages to be able to install packages that compile external code:

apk add --no-cache alpine-sdk blas

We then install numpy inside of the container with the help of pip:

pip install numpy

This results in an output like

Collecting numpy
  Downloading numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl (18.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.1/18.1 MB 8.5 MB/s eta 0:00:00
Installing collected packages: numpy
Successfully installed numpy-1.26.4

Now, we can create application that uses numpy using following script:

cd /
    cat > numpytest.py << EOF
import numpy as np
a = np.arange(15).reshape(3, 5)
print(a)
print(a.shape)
print(a.ndim)
print(a.dtype.name)
print(a.itemsize)
print(type(a).__name__)
EOF 

Ok, let's try to execute this application with NumPy. Let's run Python inside an enclave, give it plenty of heap memory and ask SCONE to print some debug messages:

SCONE_ALLOW_DLOPEN=2 SCONE_HEAP=256M SCONE_VERSION=1 python3 numpytest.py

Result should look something like this:

[SCONE] Using the following values (default values or specified in the untrusted environment):

SCONE_QUEUES=4
SCONE_SLOTS=256
SCONE_SIGPIPE=0
SCONE_MMAP32BIT=0
SCONE_SSPINS=100
SCONE_SSLEEP=4000
SCONE_CONFIG=/etc/sgx-musl.conf
SCONE_TCS=8
SCONE_LOG=WARNING
SCONE_HEAP=268435456
SCONE_STACK=2097152
SCONE_ESPINS=10000
SCONE_MODE=hw
SCONE_ALLOW_DLOPEN=yes (unprotected)
SCONE_MPROTECT=no
SCONE_FORK=no
SCONE_FORK_OS=0

SCONE_CONFIG_ID: <not specified>
musl version: 1.2.4
SCONE version: 5.8.0 (2023-06-08 15:13:27)
Enclave hash: 345bc79902e646d52d9271d0509da045550f537f7e1d7b8a4da8d6805733718c
[SCONE|WARN] src/enclave/dispatch.c:136:print_runtime_info(): Application runs in SGX debug mode. Its memory can be read from outside the enclave with a debugger! This is not secure!
[SCONE|WARN] src/syscall/syscall.c:31:__scone_ni_syscall(): system call: membarrier, number 324 is not supported
[SCONE|WARN] src/syscall/syscall.c:31:__scone_ni_syscall(): system call: membarrier, number 324 is not supported
[SCONE|WARN] src/syscall/syscall.c:31:__scone_ni_syscall(): system call: mbind, number 237 is not supported
[SCONE|WARN] src/syscall/syscall.c:31:__scone_ni_syscall(): system call: mbind, number 237 is not supported
[SCONE|WARN] src/syscall/syscall.c:31:__scone_ni_syscall(): system call: mbind, number 237 is not supported
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]
(3, 5)
2
int64
8
ndarray

Cairo

Let's look at another popular library: the cairo graphics library. cairo is written in C and has Python bindings provided by package pycairo. In this case, we need to install the C-library first:

In Alpine Linux - which is the basis of the SCONE Python image - we can install cairo as follows:

apk add --no-cache  cairo-dev cairo
fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.16/community/x86_64/APKINDEX.tar.gz
(1/59) Upgrading expat (2.4.8-r0 -> 2.6.0-r0)
...
(59/59) Installing cairo-dev (1.17.4-r2)
Executing busybox-1.35.0-r17.trigger
Executing glib-2.72.4-r0.trigger
No schema files found: doing nothing.
OK: 333 MiB in 145 packages
$ 

Now we can install the Python bindings of cairo with pip:

pip install pycairo
Collecting pycairo
  Downloading pycairo-1.26.0.tar.gz (346 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 346.9/346.9 kB 4.2 MB/s eta 0:00:00
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Installing backend dependencies ... done
  Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: pycairo
  Building wheel for pycairo (pyproject.toml) ... done
  Created wheel for pycairo: filename=pycairo-1.26.0-cp310-cp310-linux_x86_64.whl size=136777 sha256=794ca543ff6d84f8674343d081ec5438c248a0dd9b77c22d95093d72c7de4dee
  Stored in directory: /root/.cache/pip/wheels/e3/46/83/453eb7915b034ce1a9fee5a6023def2030633f6a73dc6d2de8
Successfully built pycairo
Installing collected packages: pycairo
Successfully installed pycairo-1.26.0

Let's create script that uses cairo:

cat > cairotest.py << EOF
import cairo
import math

WIDTH, HEIGHT = 256, 256
surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, WIDTH, HEIGHT)

ctx = cairo.Context (surface)

ctx.scale (WIDTH, HEIGHT)
pat = cairo.LinearGradient (0.0, 0.0, 0.0, 1.0)
pat.add_color_stop_rgba (1, 0.7, 0, 0, 0.5)
pat.add_color_stop_rgba (0, 0.9, 0.7, 0.2, 1)

ctx.rectangle (0, 0, 1, 1)

ctx.set_source (pat)

ctx.fill ()
ctx.translate (0.1, 0.1)
ctx.move_to (0, 0)
ctx.arc (0.2, 0.1, 0.1, -math.pi/2, 0)
ctx.line_to (0.5, 0.1)
ctx.curve_to (0.5, 0.2, 0.5, 0.4, 0.2, 0.8)
ctx.close_path ()

ctx.set_source_rgb (0.3, 0.2, 0.5)
ctx.set_line_width (0.02)

ctx.stroke ()
surface.write_to_png ("example.png")
exit()
EOF

We now can start that script:

SCONE_ALLOW_DLOPEN=2 SCONE_VERSION=1 python3 cairotest.py

This generates a file example.png in the working directory.

Example

Let's look at another example:

     cat > chessclient.py << EOF
> import chess
> board = chess.Board()
> board.legal_moves
> chess.Move.from_uci("a8a1") in board.legal_moves
> board.push_san("e4")
> board.push_san("e5")
> board.push_san("Qh5")
> board.push_san("Nc6")
> board.push_san("Bc4")
> board.push_san("Nf6")
> board.push_san("Qxf7")
> board.is_checkmate()
> print(board)
> exit()
> EOF
  • Then we run our chess script:
SCONE_ALLOW_DLOPEN=2 SCONE_VERSION=1 python3 chessclient.py

Screencast

Chess

Screencast was done for Python2. Current version of chess requires Python3.7+


  1. The other environment variables are explained below. Also read Section Environment Variables for further details.