Sconifying A Stateful Service
This is a simple example of how to sconify a stateful service into a confidential, stateful service. The service is already containerized. To sconify the service, we transform its container image into a confidential container image and in the same step, we generate a security policy as well as a helm chart to be able to deploy the confidential container image in a Kubernetes cluster.
Prerequisites
To be able to use these workflows, we need to grant you access:
- please register a free account,
- send an email to info@scontain.com
- to get access to
sconify_image
, and - to Github repos containing the
helm
charts to deploy SCONE LAS, SCONE CAS, and optionally, the SCONE SGX Plugin.
You can clone this example:
git clone https://github.com/scontain/hello-world-kubernetes.git
Standard Image Generation
To generate the standard container image, we use a simple Dockerfile-based flow:
The native service consists of a very simple Python program (see https://github.com/scontain/hello-world-kubernetes/blob/master/native-image/code/app.py)
that implements a counter. The counter is stored in a file. It creates this file and initializes it with the value "0" - in case the file does not yet exist. The path of this file can be changed via option -v PATH
. Next, it reads the integer stored in the file, increments it by 1, and writes back the new content of the file.
...
@click.command()
@click.option('-v', default='/V1/num', help='file number of executions.')
def main(v):
if not os.path.exists(v):
print(f"{v} does not exist, creating ... ")
os.mknod(v)
print("And writing 0 to it ... ")
write_int(v,0)
num = read_int(v)
print(f"The number of executions is {num}" )
write_int(v, num+1)
...
You can build a native container image from this via a Dockerfile that we provide (see https://github.com/scontain/hello-world-kubernetes/blob/master/native-image/Dockerfile) and run it on your local machine or in a Kubernetes cluster.
Protection Goals
This example focuses on ensuring the integrity of the counter value stored in file /V1/num
. This counter could represent, for example, the number
of times this program has been executed. We want to ensure that this counter cannot be modified by any other programs or any administrator with access to our system - without being noticed. To do so, we need to protect against the following attacks:
- the adversary changes the program code or the libraries used the code,
- the adversary changes the python engine that executes the code,
- the adversary changes the content of the file
/V1/num
- the adversary overwrites the content of the file
/V1/num
with an old value - the adversary deletes file
/V1/num
- the adversary changes the argument of the program to another file, say,
-v /tmp/num
- ...
To protect the counter, we sconify the container image.
Sconification
The sconification follows the following flow:
In the sconification process, we
- create a confidential container image that protects all code against modifications by an adversary,
- to deploy the confidential service, we generate a helm chart, and
- we create a SCONE policy which
- defines how to attest the application, and
- defines that the file is transparently encrypted and is protected against modifications, rollbacks, and deletions by an adversary.
The sconify_image
performs all these steps (see https://github.com/scontain/hello-world-kubernetes/blob/master/demo.sh#L174-L199): We specify as arguments (amongst other details):
- the name of the native image (
--from=...
), - the name of the created confidential image (
--to=...
), - the command that is executed by the confidential image (
--command=...
) - a persistent Kubernetes volume that is used to store the file
/V1/num
Deploying the example
The deployment follows the following flow:
The repository contains a script demo.sh
(see https://github.com/scontain/hello-world-kubernetes/blob/master/demo.sh) to execute this example using
- minikube on your local machine (
./demo.sh -m
), - Azure Kubernetes Service (AKS) via
./demo.sh -a -r <your registry>
, or - any Kubernetes cluster via
./demo.sh -n -r <your registry>
For more details on how to run this, please check https://github.com/scontain/hello-world-kubernetes.
Attacking the execution
You can log onto the Kubernetes node that executes this example. The volume is stored at path /mnt/test-pvc
and you can copy the
content of the file num
and later restore this content before running the next instance of the code. In this case, the log
will show an error (something like this):
SCONE version: 5.4.0-25-g25a661e71-master (Thu May 27 18:04:17 2021 +0200)
Enclave hash: e2f7cb1c13fa16d2a48f1727ebf1c20ac8185e72e14f647e876072e1f006377d
[SCONE|FATAL] src/shielding/file_content_processor.c:307:_load_chunk(): Verification of file chunk 0 (bytes 0 to 4091) of file /V1/num failed.
Generated Policy
The sconification creates a policy for this Python program. This defines
- defines a namespace (
ns-8991241572316
) and a session name (python-session
) - defines that only the creator of this policy can access the policy (note that there is no root admin that could overwrite this access right)
- define how the name of the service (
python-service
) - how to attest the program (
mrenclaves
) - defines encryption key for the container image (
fspf_key ...
) - defines an encrypted volume (
V1
) and where it is mounted (/V1
)
The actual policy would look like this:
name: ns-8991241572316/python-session
version: "0.3"
# Access control:
# - only the data owner (CREATOR) can read or update the session
# - even the data owner cannot read the session secrets (i.e., the volume key and tag) or delete the session
access_policy:
read:
- CREATOR
update:
- CREATOR
services:
- name: python-service
image_name: python-service_image
mrenclaves: [e2f7cb1c13fa16d2a48f1727ebf1c20ac8185e72e14f647e876072e1f006377d]
command: "python3 /code/app.py"
environment:
SCONE_MODE: hw
SCONE_LOG: "7"
LANG: "C.UTF-8"
PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
pwd: /
fspf_key: 4ee511ed8963f1b8034f33cfbb57f8800a6c2f17e64e4ed89122fc25e2ee50b1
fspf_tag: 849565fe4a941e2be3bcc3c9d46f40c1
fspf_path: /fspf/fs.fspf
volumes:
- name: V1
images:
- name: python-service_image
volumes:
- name: V1
path: /V1
security:
attestation:
tolerate: [debug-mode, hyperthreading, insecure-igpu, outdated-tcb, software-hardening-needed, insecure-configuration]
ignore_advisories: "*"
Generated Helm Chart
The generated helm chart is located in the directory charts/python-service
. The file values.yml
defines a set of arguments that can be set when deploying the chart via helm install
. Moreover, we also generate a README.md
that describes the arguments of the charts.