Sconify Container Images (Community Version)
To integrate with existing container image pipelines, we support the encryption of existing images. In this example, we
-
first, generate a native image that contains a Flask-based application: this image is the result of an existing image generation pipeline
-
second, we sconify this native image, i.e., we
- generate an encrypted image in which all Python code and dependencies are encrypted. Note that is required for both integrity as well as confidentiality of the Python code.
- generate a SCONE security policy that ensures that only our application can read the code in clear text
sconify_image
supports only Alpine Linux-based images
This 1-step conversion supports native images based on Alpine Linux only. We plan to support Ubuntu-based images in the near future too.
This second step uses a SCONE community version of Python instead of actually sconifying the native Python binary. For the commercial version, we support to convert a binary suh that it runs inside of an SGX enclave.
This community version is only intended for development
To run the sconified binary in production, the binary must be signed using our scone-signer
utility: only signed binaries can be considered sufficiently secure!
Moreover, this code does not attest the SCONE CAS, i.e., the SCONE Configuration and Attestation service.
You can clone the code of this example as follows:
git clone https://github.com/scontain/sconify_image.git
cd sconify_image
Stage One: Native Image Generation
We generate our native image with the help of a Dockerfile by executing:
./create_native_image.sh
You could try out this image by running
docker-compose --file native-docker-compose.yml up
You can now submit requests as follows:
export URL=https://api:4996
curl -k -X POST ${URL}/patient/patient_3 -d "fname=Jane&lname=Doe&address='123 Main Street'&city=Richmond&state=Washington&ssn=123-223-2345&email=nr@aaa.com&dob=01/01/2010&contactphone=123-234-3456&drugallergies='Sulpha, Penicillin, Tree Nut'&preexistingconditions='diabetes, hypertension, asthma'&dateadmitted=01/05/2010&insurancedetails='Primera Blue Cross'" --resolve api:4996:127.0.0.1
curl -k -X GET ${URL}/patient/patient_3 --resolve api:4996:127.0.0.1
Stage Two: Encrypted Image
We now transform the native image that we generated in stage one, into a new encrypted image in which the application runs inside of an SGX enclave.
Determine directories that we need to encrypt
One can use a dynamic analyis approach to determine which directories are accessed by the application:
docker run --init --cap-add=SYS_PTRACE -it --rm native_flask_restapi_image timeout 30 sh -c "apk add --no-cache strace ; strace python3 /app/rest_api.py" > strace.log
One can see that the application access the following directories:
/usr/local/lib
/app
/usr/lib/python3.7
Security Policy
We provide a default security policy template (see file session-template.yml
). In this example, we need to provide the application, i.e., flask, with a certificate and a private key. We added two hooks
in the default policy to 1) define secrets in the policy, and 2) inject these files in the filesystem of the application, i.e., they are only visible to the application after successfully attesting the application.
We define for our application a TLS certificate (flask
) and a private key (flask_key
). Note that we specify a DNS name api
, i.e., clients need to access this service as https://api
. To generate
a certificate, we require a CA (Certification Authority). Hence, we generate a CA (api_ca_cert
) and the private key of the CA (api_ca_key
).
export SECRETS=$(cat <<EOF
secrets:
- name: api_ca_key
kind: private-key
- name: api_ca_cert
kind: x509-ca
export_public: true
private_key: api_ca_key
- name: flask_key
kind: private-key
- name: flask
kind: x509
private_key: flask_key
issuer: api_ca_cert
dns:
- api
EOF
)
The certificate and the private key need to be made available to the program - which expects
these at paths /tls/flask.crt
and /tls/flask.key
, respectively. Hence,
we inject these secrets into files:
export INJECTED_FILES=$(cat <<EOF
injection_files:
- path: "/tls/flask.crt"
content: \$\$SCONE::flask.crt\$\$
- path: "/tls/flask.key"
content: "\$\$SCONE::flask.key\$\$"
EOF
)
Generate the encrypted image
We define some environment variables to sconify the image
- The name of the container image that was created by
./create_native_image.sh
:
export NATIVE_IMAGE="native_flask_restapi_image"
- The name of the generated encrypted container image:
export IMAGE="flask_restapi_image"
- We use a public SCONE CAS to store the session policies
export SCONE_CAS_ADDR="scone-cas.cf"
- we define a random namespace for this policy:
export NAMESPACE="my_namespace-$RANDOM"
- we define the binary that needs to be sconified:
export BINARY="/usr/local/bin/python"
Now, we can create the encrypted image, instantiate policy template and upload the policy in one step:
export SESSION=$(./sconify_image --namespace=$NAMESPACE --name=flask --from=$NATIVE_IMAGE --to=$IMAGE --cas=$SCONE_CAS_ADDR --dir="/home" --dir="/usr/local/lib" --dir="/app" --dir="/usr/lib/python3.7" --binary=$BINARY --create-namespace)
Environment variable SESSION will contain the name of the session, which in this case would be "$NAMESPACE-flask" because we set the name of the session to be flask
by passing argument --name=flask
.
Sconification of binary
The community version of sconify_image
requires the specification of a --base
image that contains the sconified binary. The commercial version can also sconify
the binary, i.e., convert it such that it can run automagically inside of SGX enclaves and it can sign the binary to run this in production.
STEP 3: Execute Encrypted Image
We show how to run this locally by executing. We first determine the SGX device name of the local computer and export a few SCONE environment variables to start the enclave:
export DEVICE=$(./determine_sgx_device)
export SCONE_HEAP=1G
export SCONE_STACK=4M
export SCONE_ALLOW_DLOPEN=2
and then we run the encrypted image using docker-compose
:
docker-compose up
We show next how to run a little more elaborated version on AKS.