diff --git a/.github/workflows/demo_test.yml b/.github/workflows/demo_test.yml index efa08c36..20139f54 100644 --- a/.github/workflows/demo_test.yml +++ b/.github/workflows/demo_test.yml @@ -675,3 +675,34 @@ jobs: - name: Run gvisor syscall test run: docker exec $gvisor_test bash -c "cd /root/gvisor/occlum && SGX_MODE=SIM ./run_occlum_passed_tests.sh" + + Flask_tls_test: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v1 + with: + submodules: true + + - uses: ./.github/workflows/composite_action/sim + with: + container-name: ${{ github.job }} + build-envs: 'OCCLUM_RELEASE_BUILD=1' + + - name: Download conda and build python + run: docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/python/flask; ./install_python_with_conda.sh" + + - name: Generate sample cert/key + run: docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/python/flask; ./gen-cert.sh" + + - name: Prepare and start Flask Occlum instance + run: docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/python/flask; + SGX_MODE=SIM ./build_occlum_instance.sh; ./run_flask_on_occlum.sh &" + + - name: Test PUT + run: | + sleep ${{ env.nap_time }}; + docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/python/flask; + curl --cacert flask.crt -X PUT https://localhost:4996/customer/1 -d "data=Tom"" + - name: Test Get + run: docker exec ${{ github.job }} bash -c "cd /root/occlum/demos/python/flask; + curl --cacert flask.crt -X GET https://localhost:4996/customer/1" diff --git a/demos/python/flask/README.md b/demos/python/flask/README.md new file mode 100644 index 00000000..972f9571 --- /dev/null +++ b/demos/python/flask/README.md @@ -0,0 +1,57 @@ +# Flask TLS demo on Occlum + +This project demonstrates how Occlum enables _unmodified_ [Python](https://www.python.org) program [`flask`](https://github.com/pallets/flask) running in SGX enclaves, which is based on glibc. + +`Flask` is a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications. + +## Sample Code: Flask TLS demo in Python + +To make the sample code more realistic, we choose to start a simple Flask TLS server by [`flask-restful`](https://flask-restful.readthedocs.io/en/latest/quickstart.html). The sample code can be found [here](rest_api.py). + +## How to Run + +This tutorial is written under the assumption that you have Docker installed and use Occlum in a Docker container. + +* Step 1: Download miniconda and install python to prefix position. +``` +bash ./install_python_with_conda.sh +``` + +* Step 2: Generate sample cert/key +``` +bash ./gen-cert.sh +``` + +* Step 3: Build Flask TLS Occlum instance +``` +bash ./build_occlum_instance.sh +``` + +* Step 4: Start the Flask TLS server on Occlum +``` +bash ./run_flask_on_occlum.sh +``` +It starts a sample Flask server like below: +``` +occlum run /bin/rest_api.py + * Serving Flask app "rest_api" (lazy loading) + * Environment: production + WARNING: This is a development server. Do not use it in a production deployment. + Use a production WSGI server instead. + * Debug mode: off + * Running on all addresses. + WARNING: This is a development server. Do not use it in a production deployment. + * Running on https://localhost:4996/ (Press CTRL+C to quit) + ``` + +* Step 5: Write some customers' info, such as +``` +# curl --cacert flask.crt -X PUT https://localhost:4996/customer/1 -d "data=Tom" +# curl --cacert flask.crt -X PUT https://localhost:4996/customer/2 -d "data=Jerry" +``` + +* Step 6: Read the customers' info back +``` +# curl --cacert flask.crt -X GET https://localhost:4996/customer/1 +# curl --cacert flask.crt -X GET https://localhost:4996/customer/2 +``` \ No newline at end of file diff --git a/demos/python/flask/build_occlum_instance.sh b/demos/python/flask/build_occlum_instance.sh new file mode 100755 index 00000000..d28e80f6 --- /dev/null +++ b/demos/python/flask/build_occlum_instance.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +BLUE='\033[1;34m' +NC='\033[0m' + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +python_dir="$script_dir/occlum_instance/image/opt/python-occlum" + +rm -rf occlum_instance && occlum new occlum_instance +cd occlum_instance && rm -rf image +copy_bom -f ../flask.yaml --root image --include-dir /opt/occlum/etc/template + +if [ ! -d $python_dir ];then + echo "Error: cannot stat '$python_dir' directory" + exit 1 +fi + +new_json="$(jq '.resource_limits.user_space_size = "640MB" | + .resource_limits.kernel_space_heap_size = "256MB" | + .process.default_mmap_size = "512MB" | + .env.default += ["PYTHONHOME=/opt/python-occlum"]' Occlum.json)" && \ +echo "${new_json}" > Occlum.json +occlum build + diff --git a/demos/python/flask/flask.yaml b/demos/python/flask/flask.yaml new file mode 100644 index 00000000..9ab8ada5 --- /dev/null +++ b/demos/python/flask/flask.yaml @@ -0,0 +1,23 @@ +includes: + - base.yaml +targets: + - target: /usr/bin + createlinks: + - src: /opt/python-occlum/bin/python3 + linkname: python3 + # python packages + - target: /opt + copy: + - dirs: + - ../python-occlum + # below are python code and data + - target: /bin + copy: + - files: + - ../rest_api.py + # Flask server key/cert + - target: /etc + copy: + - files: + - ../flask.crt + - ../flask.key diff --git a/demos/python/flask/gen-cert.sh b/demos/python/flask/gen-cert.sh new file mode 100755 index 00000000..1bafb9c1 --- /dev/null +++ b/demos/python/flask/gen-cert.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +pushd ~ +openssl rand -writerand .rnd +popd + +# Geneate self-signed key/cert +# Generate valid Flask server Key/Cert +openssl genrsa -out flask.key 2048 +openssl req -nodes -new -key flask.key -subj "/CN=localhost" -out flask.csr +openssl x509 -req -sha256 -days 365 -in flask.csr -signkey flask.key -out flask.crt + +# Remove passphrase from the Key +openssl rsa -in flask.key -out flask.key + + diff --git a/demos/python/flask/install_python_with_conda.sh b/demos/python/flask/install_python_with_conda.sh new file mode 100755 index 00000000..5281ae81 --- /dev/null +++ b/demos/python/flask/install_python_with_conda.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# 1. Init occlum workspace +[ -d occlum_instance ] || occlum new occlum_instance + +# 2. Install python and dependencies to specified position +[ -f Miniconda3-latest-Linux-x86_64.sh ] || wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh +[ -d miniconda ] || bash ./Miniconda3-latest-Linux-x86_64.sh -b -p $script_dir/miniconda +$script_dir/miniconda/bin/conda create --prefix $script_dir/python-occlum -y python=3.8 flask=1.1.2 flask-restful=0.3.8 diff --git a/demos/python/flask/rest_api.py b/demos/python/flask/rest_api.py new file mode 100755 index 00000000..f6a4dee3 --- /dev/null +++ b/demos/python/flask/rest_api.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 + +import sys +import os + +from flask import Flask, request +from flask_restful import Resource, Api + +sys.path.insert(0, os.path.dirname(__file__)) + +cert = '/etc/flask.crt' +cert_key = '/etc/flask.key' + +app = Flask(__name__) +api = Api(app) + +customers = {} + +class Customer(Resource): + def get(self, customer_id): + return {customer_id: customers[customer_id]} + + def put(self, customer_id): + customers[customer_id] = request.form['data'] + return {customer_id: customers[customer_id]} + +api.add_resource(Customer, '/customer/') + +if __name__ == '__main__': + app.debug = False + ssl_context = (cert, cert_key) + app.run(host='0.0.0.0', port=4996, threaded=True, ssl_context=ssl_context) + #app.run(host='0.0.0.0', port=4996, threaded=True) diff --git a/demos/python/flask/run_flask_on_occlum.sh b/demos/python/flask/run_flask_on_occlum.sh new file mode 100755 index 00000000..58ad724f --- /dev/null +++ b/demos/python/flask/run_flask_on_occlum.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +BLUE='\033[1;34m' +NC='\033[0m' + +# Run the python demo +cd occlum_instance +echo -e "${BLUE}occlum run /bin/rest_api.py${NC}" +occlum run /bin/rest_api.py