1.0 Introduction
This example describes how to create two Python Flask applications that run securely within an Amazon Web Service (AWS) Nitro enclave and communicate over a TLS connection. Each application receives a TLS certificate signed by Fortanix Confidential Computing Manager (CCM). The corresponding private key is generated within the enclave and is never exposed outside the enclave.
.png?sv=2026-02-06&spr=https&st=2026-06-26T22%3A03%3A37Z&se=2026-06-26T22%3A19%3A37Z&sr=c&sp=r&sig=%2FPqjU57biQoejnUdCqeOzqMMVtQF6yAKgfIrSsfA3tY%3D)
2.0 Create the Client and Server Applications
2.1 Create a Python Server Application
Create a file named server.py with the following contents:
import ssl
import uuid
from flask import jsonify, Flask
from flask_api import status
app = Flask("Python Flask TLS Server")
@app.route('/random')
def get_random_token():
random_token = uuid.uuid4().hex[:6]
return jsonify({
"token": random_token
}), status.HTTP_200_OK
if __name__ == "__main__":
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations("/app/cert/ca.crt")
context.load_cert_chain("/app/cert/server.crt", "/app/cert/server.key")
app.run(host="0.0.0.0", port=2222, use_reloader=False, threaded=True, ssl_context=context)This server listens on port 2222, accepts GET requests on the /random endpoint, and returns a random token.
The server is configured to use HTTPS with the certificate located at /app/cert/server.crt and the corresponding private key located at /app/cert/server.key. The Certificate Authority (CA) certificate that signed the server.crt certificate is located at /app/cert/ca.crt.
The line context.verify_mode = ssl.CERT_REQUIRED indicates that the sender of requests, in this case the client, must be verified using its certificate, establishing a mutual TLS connection.
When this application is deployed using Enclave OS, Enclave OS generates the certificate key and stores it at /app/cert/server.key within the enclave. It then submits a Certificate Signing Request (CSR) to Fortanix CCM and receives a signed certificate, which is stored at /app/cert/server.crt.
2.2 Create a Docker Container for the Server Application
Create a file named dockerfile.server with the following contents:
FROM python:3.7
RUN mkdir /app
RUN mkdir /app/cert
COPY server.py /app/
RUN pip3 install flask Flask-API
WORKDIR /app
ENTRYPOINT ["python3", "server.py"]Build the container and push it to a registry using the following commands:
docker build -t fortanix/python-tls-server -f dockerfile.server .
docker push fortanix/python-tls-serverIn the above commands, you can modify the tag to point to your own container registry or skip this step because the container is already available in the Fortanix public registry: https://hub.docker.com/r/fortanix/python-tls-server.
2.3 Create a Python Client Application
Similar to the server application, the client application receives a signed certificate from Fortanix CCM.
When the client application receives a request from a user, it uses this certificate to establish a mutual TLS connection with the server and submit a request. The server response, which is a random token in this example, is returned to the user.
Create a file named client.py with the following contents:
import os
import requests
import ssl
from flask import Flask
SERVER_DOMAIN = "flask-server.domain"
ENDPOINT = "https://" + SERVER_DOMAIN + ":2222"
app = Flask("Python Flask TLS Client")
@app.route("/submit")
def submit_request():
path = ENDPOINT + "/random"
req = requests.get(url=path, verify='/app/cert/ca.crt', cert=("/app/cert/client.crt", "/app/cert/client.key"))
return req.content
if __name__ == '__main__':
os.system("echo '0.0.0.0 " + SERVER_DOMAIN + "' >> /etc/hosts")
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_verify_locations("/app/cert/ca.crt")
context.load_cert_chain("/app/cert/client.crt", "/app/cert/client.key")
app.run(host="0.0.0.0", port=3333, use_reloader=False, threaded=True, ssl_context=context)2.4 Create a Docker Container for the Client Application
Create a file named dockerfile.client with the following contents:
FROM python:3.7
RUN mkdir /app
RUN mkdir /app/cert
COPY client.py /app/
RUN pip3 install flask Requests
WORKDIR /app
ENTRYPOINT ["python3", "client.py"]Build the container and push it to a registry using the following commands:
docker build -t fortanix/python-tls-client -f dockerfile.client .
docker push fortanix/python-tls-clientThe client container is already available in the Fortanix public registry: https://hub.docker.com/r/fortanix/python-tls-client
3.0 Create Fortanix CCM Applications
After pushing both application containers to a registry, create Fortanix CCM applications for the server and client applications to convert the containers into applications that run within enclaves using Fortanix Enclave OS.
Follow the steps described in the referenced example to:
Authenticate to Fortanix Armor.
Select an account.
Session tokens are short-lived. If you receive the following response, refresh the session token: {"message": "Forbidden", "code": "FORBIDDEN"} .
When creating a Fortanix CCM application, use the following server.json file with the API request. Replace output_image_name with your private registry.
curl -b $cpath -c $cpath -H "X-CSRF-Header:true" -H "Content-Type: application/json" -d @server.json -X POST https://ccm.fortanix.com/v1/apps{
"name": "Python TLS Server",
"description": "A python server using Flask and a CCM certificate",
"input_image_name": "fortanix/python-tls-server",
"output_image_name": "fortanix-private/python-tls-server-nitro",
"default_build_settings": {
"sgx": {},
"nitro_enclaves": {
"cpu_count": 2,
"mem_size": 1024,
"enable_overlay_filesystem_persistence": true
}
},
"group_id": "a8e8395e-096d-4eb8-9017-2098f2ab8327",
"allowed_domains": [
"flask-server.domain"
],
"advanced_settings": {
"entrypoint": [],
"manifestEnv": [],
"encryptedDirs": [],
"certificate": {
"issuer": "MANAGER_CA",
"subject": "flask-server.domain",
"keyType": "RSA",
"keyParam": {
"size": 2048
},
"keyPath": "/app/cert/server.key",
"certPath": "/app/cert/server.crt"
},
"caCertificate": {
"caPath": "/app/cert/ca.crt",
"system": "false"
},
"rw_dirs": [
"/app/cert"
]
},
"custom_metadata": {
"app_type": "ENCLAVE_OS"
}
}Note the <app_id> and pending_task_id values in the response. These values are required for subsequent requests.
{
"name":"Python TLS Server",
"app_id":"1ef7d352-28b6-4560-a714-a503435455bc",
"pending_task_id":"d5550480-697e-4782-9bc5-58aaaa97e712",
"domains_added":["flask-server.domain"],
...
}Approve the application domain using the following command:
curl -b $cpath -c $cpath -H "X-CSRF-Header:true" -H "Content-Type: application/json" -d '{"status":"APPROVED"}' -X PATCH https://ccm.fortanix.com/v1/tasks/<task_id>Follow the steps in the referenced example to create and approve a build. You will need the <app_id> as part of the build creation process.
Follow the same procedure to create a Fortanix CCM application for the client application and create and approve a build. Use the following client.json file when creating the client application:
{
"name": "Python TLS Client",
"description": "A python client using Flask and a CCM certificate",
"input_image_name": "fortanix/python-tls-client",
"output_image_name": "fortanix-private/python-tls-client-nitro",
"default_build_settings": {
"sgx": {},
"nitro_enclaves": {
"cpu_count": 2,
"mem_size": 1024,
"enable_overlay_filesystem_persistence": true
}
},
"group_id": "a8e8395e-096d-4eb8-9017-2098f2ab8327",
"allowed_domains": [
"flask-client.domain"
],
"advanced_settings": {
"entrypoint": [],
"manifestEnv": [],
"encryptedDirs": [],
"certificate": {
"issuer": "MANAGER_CA",
"subject": "flask-client.domain",
"keyType": "RSA",
"keyParam": {
"size": 2048
},
"keyPath": "/app/cert/client.key",
"certPath": "/app/cert/client.crt"
},
"caCertificate": {
"caPath": "/app/cert/ca.crt",
"system": "false"
},
"rw_dirs": [
"/app/cert"
]
},
"custom_metadata": {
"app_type": "ENCLAVE_OS"
}
}4.0 Run the Converted Applications
After creating and approving both application builds, deploy and test the applications on a compute node enrolled in CCM.
Run the server application using the following command. Replace <ip_addr> with the IP address of the compute node and use the build name from your private registry.
docker run --privileged --volume /dev:/dev -v /run/nitro_enclaves:/run/nitro_enclaves -e NODE_AGENT=http://<ip_addr>:9092/v1 -p 2222:2222 fortanix-private/python-tls-server-nitro:latestOn the same compute node, run the client application in a separate terminal window using the following command:
docker run --privileged --volume /dev:/dev -v /run/nitro_enclaves:/run/nitro_enclaves -e NODE_AGENT=http://:9092/v1 -p 3333:3333 --network host fortanix-private/python-tls-client-nitro:latestAfter both applications are running, submit a request using the following command:
curl -k --cacert ca.pem https://localhost:3333/submitca.pem is the certificate of the Certificate Authority (CA) that signed the server.crt and client.crt certificates delivered to the applications.
The ca.pem file is also referred to as the Zone Certificate and is specific to your Fortanix Armor account. All applications within the same account receive certificates signed by the same Zone Certificate.
To retrieve the CA certificate, use the attached fetch_ca.sh script.
5.0 Conclusion
In this example, you created two applications that run securely within an enclave. Both applications receive TLS certificates from Fortanix Confidential Computing Manager and use these certificates to establish secure communication channels with each other and with external users.