From a83d319cb2902d38db01cb127ad248f44ed166a0 Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Wed, 4 Oct 2023 10:43:32 +0530 Subject: [PATCH 1/8] Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/Makefile | 38 +++++++++++++++++++++++++++ gunicorn/README.md | 35 +++++++++++++++++++++++++ gunicorn/gunicorn.manifest.template | 40 +++++++++++++++++++++++++++++ gunicorn/main.py | 12 +++++++++ 4 files changed, 125 insertions(+) create mode 100644 gunicorn/Makefile create mode 100644 gunicorn/README.md create mode 100644 gunicorn/gunicorn.manifest.template create mode 100644 gunicorn/main.py diff --git a/gunicorn/Makefile b/gunicorn/Makefile new file mode 100644 index 0000000..b1276f6 --- /dev/null +++ b/gunicorn/Makefile @@ -0,0 +1,38 @@ +ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine) + +ifeq ($(DEBUG),1) +GRAMINE_LOG_LEVEL = debug +else +GRAMINE_LOG_LEVEL = error +endif + +.PHONY: all +all: gunicorn.manifest +ifeq ($(SGX),1) +all: gunicorn.manifest.sgx gunicorn.sig +endif + +gunicorn.manifest: gunicorn.manifest.template + gramine-manifest \ + -Dlog_level=$(GRAMINE_LOG_LEVEL) \ + -Darch_libdir=$(ARCH_LIBDIR) \ + -Dentrypoint=$(realpath $(shell sh -c "command -v gunicorn")) \ + -Dpython_exe_path=$(realpath $(shell sh -c "command -v python3")) \ + $< >$@ + +# Make on Ubuntu <= 20.04 doesn't support "Rules with Grouped Targets" (`&:`) +gunicorn.manifest.sgx gunicorn.sig: sgx_sign + @: + +.INTERMEDIATE: sgx_sign +sgx_sign: gunicorn.manifest + gramine-sgx-sign \ + --manifest $< \ + --output $<.sgx + +.PHONY: clean +clean: + $(RM) -rf *.token *.sig *.manifest *.manifest.sgx __pycache__ + +.PHONY: distclean +distclean: clean diff --git a/gunicorn/README.md b/gunicorn/README.md new file mode 100644 index 0000000..77b8047 --- /dev/null +++ b/gunicorn/README.md @@ -0,0 +1,35 @@ +# Gunicorn example + +This directory contains an example for running Gunicorn in Gramine, including the +Makefile and a template for generating the manifest. + +# Generating the manifest + +## Installing prerequisites + +Please run the following command to install Gunicorn and its dependencies on Ubuntu 22.04: +``` +sudo apt-get install python3 python3-flask gunicorn +``` + +## Building for Linux + +Run `make` (non-debug) or `make DEBUG=1` (debug) in the directory. + +## Building for SGX + +Run `make SGX=1` (non-debug) or `make SGX=1 DEBUG=1` (debug) in the directory. + +# Running Gunicorn with Gramine + +Here's an example of running Gunicorn under Gramine: + +Without SGX: +``` +gramine-direct gunicorn --workers 1 --timeout 600 main:app +``` + +With SGX: +``` +gramine-sgx gunicorn --workers 1 --timeout 600 main:app +``` diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template new file mode 100644 index 0000000..b05b731 --- /dev/null +++ b/gunicorn/gunicorn.manifest.template @@ -0,0 +1,40 @@ +loader.entrypoint = "file:{{ gramine.libos }}" +libos.entrypoint = "{{ entrypoint }}" + +loader.log_level = "{{ log_level }}" + +loader.insecure__use_cmdline_argv = true + +loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" + +fs.mounts = [ + { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, + { path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" }, + { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, +{% for path in python.get_sys_path(python_exe_path) %} + { path = "{{ path }}", uri = "file:{{ path }}" }, +{% endfor %} + { path = "/usr/bin/python3", uri = "file:/usr/bin/python3" }, + { path = "/usr/{{ arch_libdir }}", uri = "file:/usr/{{ arch_libdir }}" }, + { type = "tmpfs", path = "/tmp" }, +] + +sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }} +sgx.enclave_size = "512M" +sgx.max_threads = 64 + +sgx.trusted_files = [ + "file:{{ arch_libdir }}/", + "file:{{ entrypoint }}", + "file:{{ gramine.libos }}", + "file:{{ gramine.runtimedir() }}/", + "file:main.py", +{% for path in python.get_sys_path(python_exe_path) %} + "file:{{ path }}{{ '/' if path.is_dir() else '' }}", +{% endfor %} + "file:/usr/bin/python3", + "file:/usr/{{ arch_libdir }}/", +] + +# BSD (flock) locks are currently experimental +sys.experimental__enable_flock = true diff --git a/gunicorn/main.py b/gunicorn/main.py new file mode 100644 index 0000000..bd30ab8 --- /dev/null +++ b/gunicorn/main.py @@ -0,0 +1,12 @@ +from flask import Flask, jsonify, request + +app = Flask(__name__) + +@app.route('/hello', methods=['GET']) +def helloworld(): + if(request.method == 'GET'): + data = {"data": "Hello World"} + return jsonify(data) + +if __name__ == '__main__': + app.run(debug=True) From ff55d128c6bd360a20b978c99569db4037560770 Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Tue, 7 Nov 2023 14:54:50 +0530 Subject: [PATCH 2/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/README.md | 10 ++++++++-- gunicorn/gunicorn.manifest.template | 4 +--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gunicorn/README.md b/gunicorn/README.md index 77b8047..c24d8b8 100644 --- a/gunicorn/README.md +++ b/gunicorn/README.md @@ -1,7 +1,13 @@ # Gunicorn example -This directory contains an example for running Gunicorn in Gramine, including the -Makefile and a template for generating the manifest. +This directory contains an example for running Gunicorn in Gramine, including the Makefile and a +template for generating the manifest. Gunicorn is a webserver to deploy Flask application in +Python. Although, Flask comes with an internal webserver, this is widely considered to be not +viable for production. Common practice in production is to put Flask behind a real webserver that +communicates via the WSGI protocol. A common choice for that webserver is Gunicorn. Users can +protect their confidentiality and integrity of the Python based ML APIs and models using Gramine +for a more secure production deployment using Gunicorn. For more documentation, refer to +https://docs.gunicorn.org/en/stable/. # Generating the manifest diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template index b05b731..a055b1d 100644 --- a/gunicorn/gunicorn.manifest.template +++ b/gunicorn/gunicorn.manifest.template @@ -3,7 +3,7 @@ libos.entrypoint = "{{ entrypoint }}" loader.log_level = "{{ log_level }}" -loader.insecure__use_cmdline_argv = true +loader.argv = ["gunicorn", "--timeout", "600", "main:app"] loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" @@ -36,5 +36,3 @@ sgx.trusted_files = [ "file:/usr/{{ arch_libdir }}/", ] -# BSD (flock) locks are currently experimental -sys.experimental__enable_flock = true From db228fbe0b00a47e4725388c4b8a076aad080a46 Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Wed, 8 Nov 2023 13:35:17 +0530 Subject: [PATCH 3/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gunicorn/README.md b/gunicorn/README.md index c24d8b8..5a57346 100644 --- a/gunicorn/README.md +++ b/gunicorn/README.md @@ -32,10 +32,10 @@ Here's an example of running Gunicorn under Gramine: Without SGX: ``` -gramine-direct gunicorn --workers 1 --timeout 600 main:app +gramine-direct gunicorn ``` With SGX: ``` -gramine-sgx gunicorn --workers 1 --timeout 600 main:app +gramine-sgx gunicorn ``` From 0f9ac570a59b4cb3b1b21485289146346b04fa7d Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Thu, 9 Nov 2023 15:52:44 +0530 Subject: [PATCH 4/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/gunicorn.manifest.template | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template index a055b1d..a8a8148 100644 --- a/gunicorn/gunicorn.manifest.template +++ b/gunicorn/gunicorn.manifest.template @@ -5,6 +5,8 @@ loader.log_level = "{{ log_level }}" loader.argv = ["gunicorn", "--timeout", "600", "main:app"] +sys.enable_sigterm_injection = true + loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" fs.mounts = [ From 0055140056939b64524b44f644d2c739d6f50502 Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Wed, 15 Nov 2023 11:59:27 +0530 Subject: [PATCH 5/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/gunicorn.manifest.template | 9 ++++----- gunicorn/main.py | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template index a8a8148..cd8638b 100644 --- a/gunicorn/gunicorn.manifest.template +++ b/gunicorn/gunicorn.manifest.template @@ -7,16 +7,16 @@ loader.argv = ["gunicorn", "--timeout", "600", "main:app"] sys.enable_sigterm_injection = true -loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" +loader.env.LD_LIBRARY_PATH = "/gramine_lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" fs.mounts = [ { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, { path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" }, - { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, + { path = "/gramine_lib", uri = "file:{{ gramine.runtimedir() }}" }, {% for path in python.get_sys_path(python_exe_path) %} { path = "{{ path }}", uri = "file:{{ path }}" }, {% endfor %} - { path = "/usr/bin/python3", uri = "file:/usr/bin/python3" }, + { path = "/usr/bin", uri = "file:/usr/bin" }, { path = "/usr/{{ arch_libdir }}", uri = "file:/usr/{{ arch_libdir }}" }, { type = "tmpfs", path = "/tmp" }, ] @@ -34,7 +34,6 @@ sgx.trusted_files = [ {% for path in python.get_sys_path(python_exe_path) %} "file:{{ path }}{{ '/' if path.is_dir() else '' }}", {% endfor %} - "file:/usr/bin/python3", + "file:/usr/bin/", "file:/usr/{{ arch_libdir }}/", ] - diff --git a/gunicorn/main.py b/gunicorn/main.py index bd30ab8..7002419 100644 --- a/gunicorn/main.py +++ b/gunicorn/main.py @@ -1,6 +1,7 @@ from flask import Flask, jsonify, request app = Flask(__name__) +app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False @app.route('/hello', methods=['GET']) def helloworld(): From ab8de43cc26d6299b254612610af01924cb49e75 Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Thu, 16 Nov 2023 11:21:12 +0530 Subject: [PATCH 6/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/gunicorn.manifest.template | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template index cd8638b..7d350e7 100644 --- a/gunicorn/gunicorn.manifest.template +++ b/gunicorn/gunicorn.manifest.template @@ -25,6 +25,8 @@ sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }} sgx.enclave_size = "512M" sgx.max_threads = 64 +sgx.use_exinfo = true + sgx.trusted_files = [ "file:{{ arch_libdir }}/", "file:{{ entrypoint }}", From 67873101a3b5e84285fa8911329265813a5e7e1a Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Thu, 25 Jan 2024 14:51:19 +0530 Subject: [PATCH 7/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/Makefile | 3 +++ gunicorn/gunicorn.manifest.template | 3 +++ 2 files changed, 6 insertions(+) diff --git a/gunicorn/Makefile b/gunicorn/Makefile index b1276f6..b75563e 100644 --- a/gunicorn/Makefile +++ b/gunicorn/Makefile @@ -1,3 +1,6 @@ +# Copyright (C) 2024 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine) ifeq ($(DEBUG),1) diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template index 7d350e7..6adda6f 100644 --- a/gunicorn/gunicorn.manifest.template +++ b/gunicorn/gunicorn.manifest.template @@ -1,3 +1,6 @@ +# Copyright (C) 2024 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + loader.entrypoint = "file:{{ gramine.libos }}" libos.entrypoint = "{{ entrypoint }}" From 4e40fdca8dba420043a4216ba1a2cecefcddcfcb Mon Sep 17 00:00:00 2001 From: Sonali Saha Date: Tue, 30 Jan 2024 11:12:43 +0530 Subject: [PATCH 8/8] fixup! Add Gunicorn example Signed-off-by: Sonali Saha --- gunicorn/README.md | 19 +++++++++++++------ gunicorn/gunicorn.manifest.template | 11 ++++++++--- gunicorn/main.py | 3 +++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/gunicorn/README.md b/gunicorn/README.md index 5a57346..34ed236 100644 --- a/gunicorn/README.md +++ b/gunicorn/README.md @@ -1,13 +1,11 @@ # Gunicorn example This directory contains an example for running Gunicorn in Gramine, including the Makefile and a -template for generating the manifest. Gunicorn is a webserver to deploy Flask application in -Python. Although, Flask comes with an internal webserver, this is widely considered to be not +template for generating the manifest. Gunicorn is a webserver to deploy Flask applications in +Python. Although Flask comes with an internal webserver, this is widely considered to be not viable for production. Common practice in production is to put Flask behind a real webserver that -communicates via the WSGI protocol. A common choice for that webserver is Gunicorn. Users can -protect their confidentiality and integrity of the Python based ML APIs and models using Gramine -for a more secure production deployment using Gunicorn. For more documentation, refer to -https://docs.gunicorn.org/en/stable/. +communicates via the WSGI protocol. A common choice for that webserver is Gunicorn. For more +documentation, refer to https://docs.gunicorn.org/en/stable/. # Generating the manifest @@ -39,3 +37,12 @@ With SGX: ``` gramine-sgx gunicorn ``` + +Because these commands will start the Gunicorn server in the foreground, you will need to open +another console to run the client. + +Once the server has started, you can test it with `curl`. + +``` +curl http://127.0.0.1:8000/hello +``` diff --git a/gunicorn/gunicorn.manifest.template b/gunicorn/gunicorn.manifest.template index 6adda6f..2879e5a 100644 --- a/gunicorn/gunicorn.manifest.template +++ b/gunicorn/gunicorn.manifest.template @@ -6,6 +6,11 @@ libos.entrypoint = "{{ entrypoint }}" loader.log_level = "{{ log_level }}" +# We need `--timeout 600` to work around the problem of a constantly restarting worker process: +# the worker process is supposed to update the ctime of the shared file, and the parent process +# verifies that the worker process is alive by checking the ctime of this file every second. +# This ctime file metadata sharing between processes is currently not supported by Gramine; +# see also https://github.com/gramineproject/gramine/issues/1134. loader.argv = ["gunicorn", "--timeout", "600", "main:app"] sys.enable_sigterm_injection = true @@ -13,13 +18,13 @@ sys.enable_sigterm_injection = true loader.env.LD_LIBRARY_PATH = "/gramine_lib:{{ arch_libdir }}:/usr/{{ arch_libdir }}" fs.mounts = [ - { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, { path = "{{ entrypoint }}", uri = "file:{{ entrypoint }}" }, { path = "/gramine_lib", uri = "file:{{ gramine.runtimedir() }}" }, {% for path in python.get_sys_path(python_exe_path) %} { path = "{{ path }}", uri = "file:{{ path }}" }, {% endfor %} { path = "/usr/bin", uri = "file:/usr/bin" }, + { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, { path = "/usr/{{ arch_libdir }}", uri = "file:/usr/{{ arch_libdir }}" }, { type = "tmpfs", path = "/tmp" }, ] @@ -31,14 +36,14 @@ sgx.max_threads = 64 sgx.use_exinfo = true sgx.trusted_files = [ - "file:{{ arch_libdir }}/", "file:{{ entrypoint }}", "file:{{ gramine.libos }}", "file:{{ gramine.runtimedir() }}/", - "file:main.py", {% for path in python.get_sys_path(python_exe_path) %} "file:{{ path }}{{ '/' if path.is_dir() else '' }}", {% endfor %} "file:/usr/bin/", + "file:{{ arch_libdir }}/", "file:/usr/{{ arch_libdir }}/", + "file:main.py", ] diff --git a/gunicorn/main.py b/gunicorn/main.py index 7002419..8bc46d8 100644 --- a/gunicorn/main.py +++ b/gunicorn/main.py @@ -1,3 +1,6 @@ +# Copyright (C) 2024 Gramine contributors +# SPDX-License-Identifier: BSD-3-Clause + from flask import Flask, jsonify, request app = Flask(__name__)