Introduction
In this example we will describe how to convert and run two Flask applications over TLS:
- REST Flask Server:
#flask-server.py
from flask import Flask
from werkzeug import serving
import ssl
import uuid
app = Flask(__name__)
@app.route("/")
def ping():
return "\n============\nYou have reached the Flask Server running inside Enclave\n============\n"
@app.route('/random', methods=['POST'])
def post_call():
random_token = uuid.uuid4().hex[:6]
return ("\n============\nRandom Generated Token from Flask Server is: {}\n============\n".format(random_token))
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations("ca.pem")
context.load_cert_chain("flask-server.crt", "flask-server.key")
serving.run_simple("0.0.0.0", 9988, app, ssl_context=context) - REST Flask Client:
#flask-client.py
import requests
import logging
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route("/")
def info():
return "append /get or /put to the URL (for example - curl http://127.0.0.1:9999/get) to connect with the server for get or put requests\n"
@app.route("/get")
def run_get_rqst():
URL = "https://flask-app:9988/"
get_rqst = requests.get(url = URL, verify='ca.pem', cert=("flask-client.crt", "flask-client.key"))
return get_rqst.content
@app.route("/put")
def run_put_rqst():
URL = "https://flask-app:9988/random"
put_rqst = requests.post(url = URL, verify='ca.pem', cert=("flask-client.crt", "flask-client.key"))
return put_rqst.content
if __name__ == '__main__':
import os
cmd = "echo '0.0.0.0 flask-app' >> /etc/hosts"
os.system(cmd)
app.run(host='127.0.0.1', port=9999)The REST Server will be running in an Enclave and supports Get and Put requests. The following sections describe how to call these APIs using the REST Client running in an enclave over TLS.
Server Side Operations
Docker File and Requirement.txt File
Create a server docker image by running docker build. This will build the container for the server image. Below is the docker file for the Server application.
FROM python:3-stretch
RUN apt-get update -y
RUN apt-get -y --purge remove mysql-common libmariadbclient-dev
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["flask-server.py"]
Below is the requirements.txt file for server.
Flask==1.1.1
App.json File
Create an app according to the configuration in app.json specified in the server directory.
#app.json
{
"name": "flask-server",
"description": "",
"input_image_name": "/flask-server",
"output_image_name": "/flask-server-converted",
"isvprodid": 1,
"isvsvn": 1,
"mem_size": 2048,
"threads": 24,
"allowed_domains": ["flask-app"],
"advanced_settings": {
"certificate": {
"capath": "/app/ca.pem",
"keyType": "RSA",
"keyParam": {
"size": 2048
},
"subject": "flask-app",
"keyPath": "/app/flask-server.key",
"certPath": "/app/flask-server.crt"
},
"caCertificate":{
"capath": "/app/ca.pem",
"system":"false"
}
}
}
Fetch Bearer Token
Using the credentials used for signing up a new user, fetch the bearer token.
curl -u <username>:<password> -X POST https://em.fortanix.com/v1/sys/auth
Get All Accounts
After fetching the bearer token, select the account using the bearer token. To select an account, use the GET command to get all the accounts and select the account using the account_id.
curl -H 'Authorization: Bearer <Bearer Token>' -X GET https://em.fortanix.com/v1/accounts
Select the Account
Note the account_id of the account you want to select.
curl -H 'Authorization: Bearer <Bearer Token>' -X POST https://em.fortanix.com/v1/accounts/select_account/<account-id>
Create an Application
curl -s -H 'Content-Type: application/json' -d @app.json -H "Authorization: Bearer <Bearer token>" -X POST https://em.fortanix.com/v1/apps
Fetch the Domain Whitelisting Tasks
curl -s -H "Authorization: Bearer <Bearer Token>" -X GET https://em.fortanix.com/v1/tasks?task_type=DOMAIN_WHITELIST > all_domain_tasks.json
All the tasks fetched will be stored in all_domain_tasks.json file. Select the task_id to approve the task in the next step.
Approve Domain Whitelist Task
Among the tasks fetched in the previous step, approve the application-specific task using the task_id.
curl -s -H 'Content-Type: application/json' -d '{"status":"APPROVED"}' -H "Authorization: Bearer <Bearer Token>" -X PATCH https://em.fortanix.com/v1/tasks/<task_id>
All the tasks fetched will be stored in all_domain_tasks.json file. Select the task_id to approve the task in the next step.
Create a Build
Create a build of the application.
curl -s -H 'Content-Type: application/json' -d @build.json -H "Authorization: Bearer <Bearer token>" -X POST https://em.fortanix.com/v1/builds/convert-app
The build.json is as below.
{
"app_id": <app_id>,
"docker_version": <tag>,
"inputAuthConfig":
{"username": <username>,
"password": <password>
},
"outputAuthConfig":
{"username": <username>,
"password": <password>
}
}
Fetch all the Build Whitelist Tasks
curl -s -H "Authorization: Bearer <Bearer token>" -X GET https://em.fortanix.com/v1/tasks?task_type=BUILD_WHITELIST > all_build_tasks.json
All the build whitelist tasks will be stored in all_build_tasks.json file. Select the build whitelist task ID to approve the build in the next step.
Approve the Build Whitelist Task
curl -s -H 'Content-Type: application/json' -d '{"status":"APPROVED"}' -H "Authorization: Bearer <Bearer token>" -X PATCH https://em.fortanix.com/v1/tasks/<task_id>
Client Side Operations
Docker File and Requirement.txt File
Create a client docker image by running docker build . command. This will build the container for the client image. Below is the docker file for the client application.
FROM python:3-stretch
RUN apt-get update -y
RUN apt-get -y --purge remove mysql-common libmariadbclient-dev
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["flask-client.py"]
Below is the requirements.txt file for server.
Flask==1.1.1
Requests
App.json File
Create an app according to the configuration in app.json specified in the client directory.
#app.json
{
"name": "flask-client",
"description": "",
"input_image_name": ""<image-path>/flask-client",
"output_image_name": "<image-path>/flask-client-converted",
"isvprodid": 2,
"isvsvn": 1,
"mem_size": 2048,
"threads": 128,
"allowed_domains": ["flask-app"],
"advanced_settings": {
"certificate": {
"issuer": "MANAGER_CA",
"keyType": "RSA",
"keyParam": {
"size": 2048
},
"subject": "flask-app",
"keyPath": "/app/flask-client.key",
"certPath": "/app/flask-client.crt"
},
"caCertificate":{
"capath": "/app/ca.pem",
"system":"false"
}
}
}
Fetch Bearer Token
Using the credentials used for signing up a new user, fetch the bearer token.
curl -u <username>:<password> -X POST https://em.fortanix.com/v1/sys/auth
Get All Accounts
After fetching the bearer token, select the account using the bearer token. To select an account, use the GET command to get all the accounts and select the account using the account_id.
curl -H 'Authorization: Bearer <Bearer Token>' -X GET https://em.fortanix.com/v1/accounts
Select the Account
Note the account_id of the account you want to select.
curl -H 'Authorization: Bearer <Bearer Token>' -X POST https://em.fortanix.com/v1/accounts/select_account/<account-id>
Create an Application
curl -s -H 'Content-Type: application/json' -d @app.json -H "Authorization: Bearer <Bearer token>" -X POST https://em.fortanix.com/v1/apps
Fetch the Domain Whitelisting Tasks
curl -s -H "Authorization: Bearer <Bearer Token>" -X GET https://em.fortanix.com/v1/tasks?task_type=DOMAIN_WHITELIST > all_domain_tasks.json
All the tasks fetched will be stored in all_domain_tasks.json file. Select the task_id to approve the task in the next step.
Approve Domain Whitelist Task
Among the tasks fetched in the previous step, approve the application-specific task using the task_id.
curl -s -H 'Content-Type: application/json' -d '{"status":"APPROVED"}' -H "Authorization: Bearer <Bearer Token>" -X PATCH https://em.fortanix.com/v1/tasks/<task_id>
All the tasks fetched will be stored in all_domain_tasks.json file. Select the task_id to approve the task in the next step.
Create a Build
Create a build of the application.
curl -s -H 'Content-Type: application/json' -d @build.json -H "Authorization: Bearer <Bearer token>" -X POST https://em.fortanix.com/v1/builds/convert-app
The build.json is as below.
{
"app_id": <app_id>,
"docker_version": <tag>,
"inputAuthConfig":
{"username": <username>,
"password": <password>
},
"outputAuthConfig":
{"username": <username>,
"password": <password>
}
}
Fetch all the Build Whitelist Tasks
curl -s -H "Authorization: Bearer <Bearer token>" -X GET https://em.fortanix.com/v1/tasks?task_type=BUILD_WHITELIST > all_build_tasks.json
All the build whitelist tasks will be stored in all_build_tasks.json file. Select the build whitelist task ID to approve the build in the next step.
Approve the Build Whitelist Task
curl -s -H 'Content-Type: application/json' -d '{"status":"APPROVED"}' -H "Authorization: Bearer <Bearer token>" -X PATCH https://em.fortanix.com/v1/tasks/<task_id>
Run the Converted Server and Client Application
You now have to run the converted application on an SGX compute node. The following is the command to run the program.
# Run the Converted Server App
docker run -d --name <flask-server-container> -it --device /dev/isgx:/dev/isgx --device /dev/gsgx:/dev/gsgx -v /var/run/aesmd/aesm.socket:/var/run/aesmd/aesm.socket -e NODE_AGENT_BASE_URL=http://<node-agent-ip>:9092/v1/ -p 9988:9988 <flask-server-converted-image-id>
# Run the Converted Client App
docker run -d –name <flask-client-container> -it --device /dev/isgx:/dev/isgx --device /dev/gsgx:/dev/gsgx -v /var/run/aesmd/aesm.socket:/var/run/aesmd/aesm.socket -e NODE_AGENT_BASE_URL=http://<node-agent-ip>:9092/v1/ --network host <flask-client-converted-image-id>
Where,
- <node-agent-ip> is the IP address of the compute node registered on Fortanix Confidential Computing Manager (CCM).
Query Flask Server Using Flask Client
Once Client and Server are running, query the RESTful flask server using Rest flask Client. The Flask client has started on 127.0.0.1:9999. So you can query:
curl http://127.0.0.1:9999
or
curl http://127.0.0.1:9999/get
or
curl http://127.0.0.1:9999/put
The REST client running inside the enclave is querying and receiving the response from the REST server below.
root@client:~# curl http://127.0.0.1:9999
append /get or /put to the URL (for example - curl http://127.0.0.1:9999/get) to connect with the server for get or put requests
root@client:~#
root@client:~# curl http://127.0.0.1:9999/get
============
You have reached the Flask Server running inside Enclave
============
root@client:~#
root@client:~# curl http://127.0.0.1:9999/put
============
Random Generated Token from Flask Server is: 50086a
============
root@client:~#
root@client:~# curl http://127.0.0.1:9999/put
============
Random Generated Token from Flask Server is: 6b1ed5
============
root@client:~#