Sconify Container Image (Standard Edition)
We show how to sconify an existing container image using a tool provided by the SCONE Standard Edition we
- sconify the binary of the application running inside the container, i.e., converting it such that it runs inside of an SGX enclave
- encrypt the files that the application accesses including libraries and code examples (like Python code)
- creating a SCONE policy (a session) to ensure that the encryption key of the filesystem and the secrets that we generate can only be accessed by this application
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.
CI/CD Workflow
In a typical workflow, one would perform the sconification of an image as part of the CI/CD pipeline: One would test the native image first, sconify the image and test the sconified image again. After a successful test, the binary would be pushed to a container image repository.
Namespace
To reduce the chances of a name conflict, we first generate a namespace. Let's create a random namespace for this example:
export NS="NS-$RANDOM"
Policy Extension
We sconify the same image as in the previous section, i.e., native_flask_restapi_image
. This image requires the generation of a TLS certificate and a private key. To do so, we define a secret section for the generated policy that we store in a local file:
mkdir -p policy
cat > policy/secrets.yml <<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
We also define the location in which the sconified application can read the certificate and the private key:
cat > policy/injected_files.yml <<EOF
injection_files:
- path: "/tls/flask.crt"
content: \$\$SCONE::flask.crt\$\$
- path: "/tls/flask.key"
content: "\$\$SCONE::flask.key\$\$"
EOF
Creating Sconified Image
To attest the CAS and uploaded a policy, we need access to the SGX device. Hence, we first determine the SGX device (see):
determine_sgx_device
mkdir -p cas
We use a public CAS service (--cas=4-2-1.scone-cas.cf
) that is running in debug mode and hence, we set --cas_debug
to ensure that attestation of CAS passes despite the CAS being in debug mode. The clients key used in the attestation are stored in the local directory cas.
SESSION=$(docker run -it --rm $MOUNT_SGXDEVICE -v "$(pwd)/policy:/policy" -v "$(pwd)/cas:/root/.cas" -v /var/run/docker.sock:/var/run/docker.sock registry.scontain.com:5050/sconecuratedimages/sconecli:sconify_image --name=flask --from=native_flask_restapi_image --to=new_image --cas=4-2-1.scone-cas.cf --cas_debug --binary=/usr/bin/python3 --dir=/home --dir=/usr/local/lib --dir=/app --dir=/usr/lib/python3.7 --dir=/lib --plain=/usr/lib/libpython3.7m.so.1.0 --secrets=/policy/secrets.yml --injectedfiles=/policy/injected_files.yml --create_namespace --namespace=$NS)
For production, you need to drop --cas_debug
and to sign the binary
You can now execute the image in the context of session $SESSION
: please read the previous section to learn how to do so.
Supported Options
sconify_image
supports the following options:
--from=NATIVE_IMAGE
: name of native image (mandatory)--to=TO_IMAGE
: name of encrypted image (mandatory)--template=TEMPLATE_FILE
: file containing policy template (default=session-template.yml)--session=SESSION_FILE
: file that will contain the session (default=session.yml)--namespace=NAMESPACE
: namespace of this session (default is a random namespace)--create_namespace
: create namespace NAMESPACE first--dir=DIRECTORY
: add directory to encrypt; add one option per directory--plain=DIRECTORIES
: copy directories that are not encrypted--cas=CAS_ADDR
: set the name of the CAS_ADDR--cas_debug
: permit CAS to be in debug mode--base=BASE
: set the base image used to generate the encrypted image--cli=CLI
: set the SCONE CLI image--trace=TRACEFILE
: use tracefile to create encrypted files--name=SESSION
: name of CAS policy session--secrets=FILE
: policy section that defines secrets--injectedfiles=FILE
: policy section that defines the injected files--sign=KEYPATH
: sign the binary (uses other arguments from environment)--heap=INT
: heap size in [K,M,G]--stack=INT
: stack size in [K,M,G]--dlopen=[0|1|2]
: dlopen: 0 - disable, 1 - enable and require authentication (default), 2 - debug only [SCONE_ALLOW_DLOPEN]
Next Step
Using this approach, we 1) attest CAS and 2) ensure that the policy is only pushed to a trusted CAS. For production, you need to sign the service: for this, you need to provide a signing key that you need to pass via option --sign=KEYPATH
.