HTTPS websites are more and more popular recently. Now you
can also build websites with HTTPS connections via Azure Container Instance,
with secret volumes and DNS Name Labels. This article is to demonstrate the
steps about how to setup HTTS connections for a Node.js website.
Prerequisite: Have a Certificate
You may have a certificate for the SSL connection. If your DNS name label xyz, and your container group is going to be created in WestUS
region, the fully qualified domain name looks like xyz.westus.azurecontainer.io and your certificate should match it. If you have a CName for your website, your certificate should match the CName.
Step 1: Build the Image
If you are going to build your website with Node.js, you may
setup HTTPS connections with the following code:
const fs = require('fs');
const https = require('https');
const express = require('express');
const morgan = require('morgan');
const options = {
pfx: fs.readFileSync('certificate.pfx'),
passphrase: fs.readFileSync('certificatepassword.txt')
};
const app = express();
app.use(morgan('combined'));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html')
});
var listener = https.createServer(options,
app).listen(process.env.PORT || 443, function () {
console.log('listening
on port ' + listener.address().port);
});
Notice that the two files ‘certificate.pfx’ and ‘certificatepassword.txt’
are the certificate and its password. Before we start to run Node.js, these two
files are copied from the path /mnt/secrets, as shown in Dockerfile:
CMD cp
/mnt/secrets/sslcertificateData /usr/src/app/certificate.pfx && cp
/mnt/secrets/sslcertificatePwd /usr/src/app/certificatepassword.txt && node
/usr/src/app/index.js
We are going to pass these secrets from the Azure deployment
template.
All other code for the Node.js website and the Docker file
are shared at https://github.com/zhedahht/aci-ssl-helloworld.
And the built image is shared at https://hub.docker.com/r/containerinstance/helloworld/.
Step2: Define the Deployment Template
The next step is to define Azure deployment template, and
the following is a sample:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"containergroupname": {
"type": "string",
"metadata": {
"description": "Name for the container
group"
}
},
"containername": {
"type": "string",
"metadata": {
"description": "Name for the container"
},
"defaultValue": "container1"
},
"imagename": {
"type": "string",
"metadata": {
"description": "Name for the image"
},
"defaultValue": "containerinstance/helloworld:ssl"
},
"volumename": {
"type": "string",
"metadata": {
"description": "Name for the secret
volume"
},
"defaultValue": "volume1"
},
"dnsnamelabel": {
"type": "string",
"metadata": {
"description": "The DSN name label"
}
},
"sslcertificateData": {
"type": "securestring",
"metadata": {
"description": "Base-64 encoded
authentication PFX certificate."
}
},
"sslcertificatePwd": {
"type": "securestring",
"metadata": {
"description": "Base-64 encoded password of
authentication PFX certificate."
}
},
"port": {
"type": "string",
"metadata": {
"description": "Port to open on the
container and the public IP address."
},
"defaultValue": "443"
},
"cpuCores": {
"type": "string",
"metadata": {
"description": "The number of CPU cores to
allocate to the container."
},
"defaultValue": "1.0"
},
"memoryInGb": {
"type": "string",
"metadata": {
"description": "The amount of memory to
allocate to the container in gigabytes."
},
"defaultValue": "1.5"
}
},
"variables": {},
"resources": [
{
"name": "[parameters('containergroupname')]",
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2018-02-01-preview",
"location": "[resourceGroup().location]",
"dependsOn": [],
"properties": {
"containers": [
{
"name": "[parameters('containername')]",
"properties": {
"command": [],
"image": "[parameters('imagename')]",
"ports": [
{
"port": "[parameters('port')]"
}
],
"resources": {
"requests": {
"cpu": "[parameters('cpuCores')]",
"memoryInGb": "[parameters('memoryInGb')]"
}
},
"volumeMounts": [
{
"name": "[parameters('volumename')]",
"mountPath": "/mnt/secrets",
"readOnly": false
}
]
}
}
],
"osType": "Linux",
"ipAddress": {
"type": "Public",
"dnsNameLabel": "[parameters('dnsnamelabel')]",
"ports": [
{
"protocol": "tcp",
"port": "[parameters('port')]"
}
]
},
"volumes": [
{
"name": "[parameters('volumename')]",
"secret": {
"sslcertificateData": "[parameters('sslcertificateData')]",
"sslcertificatePwd": "[base64(parameters('sslcertificatePwd'))]"
}
}
]
}
}
],
"outputs": {
"containerIPAddressFqdn": {
"type": "string",
"value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/',
parameters('containergroupname'))).ipAddress.fqdn]"
}
}
}
In the template above, the certificate is mounted as the
file /mnt/secrets/sslcertificateData, and the password is mounted as the file
/mnt/secrets/sslcertificatePwd. These files will be copied and pasted as
/user/src/app/certificate.pfx and /user/src/app/certificatepassword.txt
respondingly, and they are accessible to Node.js.
The DNS name labels is defined in the property dnsNameLabel of
ipAddress. The fully qualified domain name is a concatenation of the DSN name
label, the location and “azurecontainer.io”.
The deployment template and the corresponding parameters are
shared at https://github.com/Azure/azure-quickstart-templates/tree/master/201-aci-linuxcontainer-volume-secret.