Node Example with Attestation and Secret Provisioning
We show how to inject keys (/tls/key.pem
) and certificate ('/tls/cert.pem
) in a simple node program with a SCONE policy:
var express = require('express');
const https = require("https"),
fs = require("fs");
console.log('Example tls app start!');
console.log('read the secret :'+process.env.GREETING)
const keyFilePath = '/tls/key.pem';
const certFilePath = '/tls/cert.pem';
var privateKey = fs.readFileSync(keyFilePath, 'utf8');
var certificate = fs.readFileSync(certFilePath, 'utf8');
var credentials = {key: privateKey, cert: certificate};
var app = express();
var httpsServer = https.createServer(credentials, app);
app.get('/', function (req, res) {
console.log('scone mode is :'+process.env.GREETING)
res.send('Hello World!' + process.env.GREETING);
});
httpsServer.listen(443, function () {
console.log('Example tls app listening on port 443!');
console.log('scone mode is :'+process.env.GREETING)
console.log('Ok.');
});
A policy template defines a private key and a certificate that is signed with a CA certificate
that is also created in this policy. We set the DNS name of the certifcate to app
. We define a secret GREETING: "Hello NodeJS"
as an environment variable passed to app
. In this policy, we assume that our development machine has not seen many firmware updates recently:
name: $NODE_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: app
image_name: node_image
mrenclaves: [$MRENCLAVE]
command: node /app/app.js
environment:
SCONE_MODE: hw
SCONE_LOG: "7"
GREETING: "Hello NodeJS"
pwd: /
images:
- name: node_image
injection_files:
- path: /tls/cert.pem
content: $$SCONE::app.crt$$
- path: /tls/key.pem
content: $$SCONE::app.key$$
# Import client credentials from DB session.
secrets:
- name: api_ca_key
kind: private-key
- name: api_ca_cert
kind: x509-ca
export_public: true
private_key: api_ca_key
- name: app_key
kind: private-key
- name: app
kind: x509
private_key: app_key
issuer: api_ca_cert
dns:
- app
security:
attestation:
tolerate: [debug-mode, hyperthreading, insecure-igpu, outdated-tcb]
ignore_advisories: ["INTEL-SA-00076", "INTEL-SA-00088", "INTEL-SA-00106", "INTEL-SA-00115", "INTEL-SA-00135", "INTEL-SA-00203", "INTEL-SA-00161", "INTEL-SA-00220", "INTEL-SA-00270", "INTEL-SA-00293", "INTEL-SA-00320", "INTEL-SA-00329", "INTEL-SA-00233", "INTEL-SA-00220", "INTEL-SA-00270", "INTEL-SA-00293", "INTEL-SA-00320", "INTEL-SA-00329"]
We need to set the MRENCLAVE
and the name of the policy (NODE_SESSION
) before we upload the session to a CAS instance. To simplify this upload and running of the node application, we pushed the code to github. Just clone it and enter the newly cloned directory
git clone https://github.com/scontain/node_example.git
cd node_example
We need to set some environment variables and upload our session:
export CAS_ADDR="4-2-1.scone-cas.cf" # we use a public SCONE CAS to store the session policies
export IMAGE="sconecuratedimages/apps:node-10.14-alpine"
unset NODE_SESSION
export NODE_SESSION=$(./upload_session --template=nodejs-template.yml --session=nodejs-session.yml --image=$IMAGE --cas=$CAS_ADDR)
export DEVICE=$(./determine_sgx_device) # determine the SGX device of the local computer
and then run locally by executing
docker-compose up
This will print some log messages and eventually print
node_1 | Example tls app start!
node_1 | read the secret :Hello NodeJS
node_1 | Example tls app listening on port 443!
node_1 | scone mode is :Hello NodeJS
node_1 | Ok.
indicating that the app
is ready to process requests.
Client Request
Execute client request via https:
curl -k https://localhost:443
The output will be:
Hello World!Hello NodeJS
Note that '-k' is the insecure mode and if we drop this option, the output will look like this:
curl https://localhost:443
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
Let's fix this warning and by doing so we also attest the node service.
Get CA Certificate used to sign certificate of node app
Retrieve the exported ca certificate with curl from CAS. Note that we told CAS to export the CA certifcate by setting export_public: true
in the policy. We store the certificate in file `ca-cert':
export ca_cert=$(curl -k https://${CAS_ADDR}:8081/v1/values/session=$NODE_SESSION | jq ".values.api_ca_cert.value" | tr -d \" )
printf "\n$ca_cert" > ca_cert
We can now use this ca certificate to verify the certificate from our application:
curl --capath "$(pwd)" --cacert ca_cert --verbose https://app:443 --resolve app:443:127.0.0.1
Notes
-
Before restarting the service, please shut it down properly with
docker-compose down
. Also executeunset NODE_SESSION
to ensure that you do not reuse an old session afterwards. -
You need to ensure that CAS executes inside of an enclave in production mode. To do so, you would need to use our SCONE CLI to attest CAS and to upload a session
-
You need to encrypt the app code and parts of the node image before you go to production. See our flask exmaple on how to do this.