Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Monday, September 7, 2020

Azure Web Services Transformation


Summary

In this post I'll demonstrate an interim transformation of a premise-based web service to cloud-based services.  I'll be utilizing the MS Azure services stack for this transformation.

Premise Service Example

I'll use the key-value pair store implementation discussed in my previous post as the premise service to be transformed.  As a recap, this was a simple Nodejs/Express REST service defining Create (POST), Retrieve (GET), Update (PUT), and Delete (DELETE) service calls for key-value pairs stored in memory on the server.  Additionally, this service group used mutual TLS/client certificates for authentication to all the REST services.  Diagram below of the service.



FaaS Proxy

The first step in transforming this API to a cloud-based service will be creating proxy functions for each of the REST calls via Azure Functions.  Azure has a strong integration with VS Code via the Azure Functions Extension.  Below is a VS Code screen-shot of the resulting local REST API set developed for Azure Functions.  Note the 'authLevel' has been set to 'function'.  That means an Azure function-level API key must be provided to execute this function.



Code snippet below of the Azure CREATE function (REST POST).
'use strict';
'use esversion 6';
const https = require('https');
const fs = require('fs');
const fetch = require('node-fetch');
const key = fs.readFileSync("clientKey.pem");
const cert = fs.readFileSync("clientCert.pem");
const options = {
    key: key,
    cert: cert,
    rejectUnauthorized: false
};
const tlsAgent = new https.Agent(options);
const url = 'https://premiseServer:8443/kvp/';

module.exports = async function (context) { 
    try {
        const response = await fetch(url, {
            method: 'POST',
            body: JSON.stringify(context.req.body),
            headers: {'Content-Type': 'application/json'},
            agent: tlsAgent
        });

        context.res = {
            headers: {'Content-Type': 'application/json'},
            status: response.status, 
            body: await response.json()
        };
    }
    catch (err) {
        context.res = {
            headers: {'Content-Type': 'application/json'},
            status: 400, 
            body: {'error': err}
        };
    }
}



Deployment to the cloud can be accomplished via the VS Code extension as well.  Screen-shot below of the resulting push of this code/config to Azure.


Finally, diagram below of the full FaaS proxy model implemented with Azure Functions.  Function calls require the Azure Function key per function at this point.

 

API Gateway

Next step in the transformation is to place these Azure Functions behind an API Gateway.  The Azure implementation of that is the Azure API Management (APIM) service.  APIM adds important functionality such as authentication management, monitoring, throttling, etc.  Screen-shot below of the resulting mapping of each of the Azure functions to an APIM endpoint.  By default, APIM function access will require a 'subscription key'.  Additionally, APIM will auto-provision 'host key' authentication for access to the Azure functions that have 'function' auth level defined. 



Resulting architecture below:

 

Final Thoughts

Further Azure service integrations can be provisioned at this point.  Examples:  APIM will integrate directly with OAuth 2.0 authentications schemes, the APIM REST surface can be placed behind Azure Cloud CDN or Front Door services to add further functionality and resilience to the services.

Copyright ©1993-2024 Joey E Whelan, All rights reserved.

Saturday, August 29, 2020

Mutual TLS Authentication


Summary

This post will cover the mutual TLS/client-side certificate approach to API authentication.  This is an authentication scheme that's suitable for machine to machine authentication of a limited number of clients.  I'll demonstrate the approach with Node.js implementations of the server and client using self-signed certificates.

Message Flow

Diagram  below depicting the message exchanges (from a CURL session) for a mutual TLS authentication.



Generating Self-Signed Certs

Below is a sequence of OpenSSL commands to generate the server-side private key and self-signed public certificate, the client-side private key and certificate signing request, and finally client-side certificate signing by the server-side cert.

#generate server private key and cert
openssl req -x509 -newkey rsa:4096 -keyout serverKey.pem -out serverCert.pem -nodes -days 365 -subj "/CN=localhost"

#generate client private key and cert signing request
openssl req -newkey rsa:4096 -keyout clientKey.pem -out clientCsr.pem -nodes -subj "/CN=Client"

#sign client cert
openssl x509 -req -in clientCsr.pem -CA serverCert.pem -CAkey serverKey.pem -out clientCert.pem -set_serial 01 -days 365

Server Snippet

Node.js implementation of a REST server that will request mutual TLS/client-side cert. Highlighted areas are of note for mutual TLS.

const https = require('https');
const express = require('express');
const fs = require('fs');

const port = 8443;
const key = fs.readFileSync('serverKey.pem');
const cert = fs.readFileSync('serverCert.pem');
const options = {
        key: key,
        cert: cert,
        requestCert: true,
        ca: [cert]
};

let kvpStore = {};

const app = express();
app.use(express.json());

//create
app.post('/kvp', (req, res) => {
    const key = req.body.key;
    const value = req.body.value;
    if (key && value) {
        if (key in kvpStore) {
            res.status(400).json({error: 'kvp already exists'});
        }
        else {
            kvpStore[key] = value;
            res.status(201).json({key: key, value: kvpStore[key]});
        }
    } 
    else {
        res.status(400).json({error: 'missing key value pair'});
    }   
});

CURL Client Commands

#CREATE
curl -v -k --key clientKey.pem --cert clientCert.pem -H "Content-Type: application/json" -d '{"key":"1", "value":"abc"}' https://localhost:8443/kvp

#RETRIEVE
curl -v -k --key clientKey.pem --cert clientCert.pem https://localhost:8443/kvp/1

#UPDATE
curl -v -k --key clientKey.pem --cert clientCert.pem -X PUT -H "Content-Type: application/json" -d '{"key":"1", "value":"def"}' https://localhost:8443/kvp

#DELETE
curl -v -k --key clientKey.pem --cert clientCert.pem -X DELETE https://localhost:8443/kvp/1

Client Snippet

Node.js implementation of REST client supporting mutual TLS.  Again, highlighted areas depicting where mutual TLS-specific configuration is necessary.

const https = require('https');
const fs = require('fs');
const fetch = require('node-fetch');

const key = fs.readFileSync('clientKey.pem');
const cert = fs.readFileSync('clientCert.pem');
const options = {
        key: key,
        cert: cert,
        rejectUnauthorized: false
};
const url = 'https://localhost:8443/kvp/';
const tlsAgent = new https.Agent(options)

async function create(kvp) {
    const response = await fetch(url, {
        method: 'POST',
        body: JSON.stringify(kvp),
        headers: {'Content-Type': 'application/json'},
        agent: tlsAgent
    });

    const json = await response.json();
    console.log(`CREATE - ${response.status} ${JSON.stringify(json)}`);
}

Source

https://github.com/joeywhelan/mutualtls

Copyright ©1993-2024 Joey E Whelan, All rights reserved.