From ee9f4a68f68dae02e8133c29a3dc125e5c245f46 Mon Sep 17 00:00:00 2001 From: DonMartin76 Date: Wed, 20 Jan 2016 11:17:33 +0100 Subject: [PATCH] Blog post on Azure Storage Shared Access Signatures --- _posts/2016-01-25-azure-storage-sas.md | 120 +++++++++++++++++++++ images/azure-storage-sas-1.png | Bin 0 -> 11314 bytes resources/azure-storage-sas-resources.pptx | Bin 0 -> 33898 bytes 3 files changed, 120 insertions(+) create mode 100644 _posts/2016-01-25-azure-storage-sas.md create mode 100644 images/azure-storage-sas-1.png create mode 100644 resources/azure-storage-sas-resources.pptx diff --git a/_posts/2016-01-25-azure-storage-sas.md b/_posts/2016-01-25-azure-storage-sas.md new file mode 100644 index 0000000..b44bf1c --- /dev/null +++ b/_posts/2016-01-25-azure-storage-sas.md @@ -0,0 +1,120 @@ +--- +layout: post +title: Shared Access Signatures with Azure Storage +subtitle: How to replace your more or less secure ftp server. +description: Shared Access Signatures with Azure Storage +category: general +author: [Martin Danielsson](https://github.com/DonMartin76) +author_email: martin.danielsson@haufe-lexware.com +--- + +Continuing our API journey, we're currently designing an API for one of our most valuable assets: Our content, such as law texts and commentaries. Let's call this project the "Content Hub". The API will eventually consist of different sub-APIs: content search, retrieval and ingestion ("upload"). This blog post will shed some light on how we will support bulk ingestion (uploading) of documents into our content hub using Azure out of the box technology: [Azure Storage SAS - Shared Access Signatures](https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-shared-access-signature-part-1/). + +### Problem description + +In order to create new content, our API needs a means to upload content into the Content Hub, both single documents and bulk ZIP files, which for example correspond to updated products (blocks of content). Ingesting single documents via an API is less a problem (and not covered in this blog post), but supporting large size ZIP files (up to 2 GB and even larger) is a different story, for various reasons: + +* Large http transfers need to be supported by all layers of the web application stack (chunked transfer), which potentially introduces additional complexity +* Transferring large files is a rather difficult problem we do not want to solve on our own (again) +* Most SaaS API gateways (such as [Azure API Management](https://azure.microsoft.com/en-us/services/api-management/)) have traffic limits on their API gateways + +### First approach: Setting up an sftp server + +Our first architectural solution approach was to set up an (s)ftp server for use with the bulk ingestion API. From a high level perspective, this looks like a valid solution: We make use of existing technology which is known to work well. When detailing the solution, we found a series of caveats which made us look a little further: + +* Providing secure access to the sftp server requires dynamic creation of users; this would also - to make the API developer experience smooth - have to be integrated into the API provisioning process (via an API portal) +* Likewise: After a document upload has succeeded, we would want to revoke the API client's rights on the sftp server to avoid misuse/abuse +* Setting up an (s)ftp server requires an additional VM, which introduces operations efforts, e.g. for OS patching and monitoring. +* The sftp server has to provide reasonable storage space for multiple ZIP ingest processes, and this storage would have to be provided up front and paid for subsequently. + +### Second approach: Enter Azure Storage + +Knowing we will most probably host our content hub on the [Microsoft Azure](https://azure.microsoft.com) cloud, looks immediately went to services offered via Azure, and in our case we we took a closer look at [Azure Storage](https://azure.microsoft.com/en-us/services/storage/). The immediate benefits of having such a storage as a SaaS offering are striking: + +* You only pay for the storage you actually use, and the cost is rather low +* Storage capacity is unlimited for most use cases (where you don't actually need multiple TB of storage), and for our use case (document ingest) more than sufficient +* Storage capacity is does not need to be defined up front, but adapts automatically as you upload more files ("blobs") into the storage +* Azure Storage has a large variety of SDKs for use with it, all leveraging a standard REST API (which you can also use fairly simple) + +Remains the question of securing the access to the storage, which was one of the main reasons why an ftp server seemed like a less than optimal idea. + +### Accessing Azure Storage + +Accessing an Azure Storage usually involves passing a storage identifier and an access key (the application secret), which in turn grants full access to the storage. Having an API client have access to these secrets is obviously a security risk, and as such not advisable. Similarly to the ftp server approach, it would in principle be possible to create multiple users/roles which have limited access to the storage, but this is also an additional administrational effort, and/or an extra implementation effort to make it automatic. + +### Azure Storage Shared Access Signatures + +Luckily, Azure already provides a means of anonymous and restricted access to storages using a technique which is know e.g. from JWT tokens: Signed access tokens with a limited time span, a.k.a. "Shared Access Signatures" ("SAS"). These SAS tokens actually match our requirements: + +* Using a SAS, you can limit access to the storage to either a "container" (similar to a folder/directory) or a specific "blob" (similar to a file) +* The SAS only has a limited validity which you can define freely, e.g. from "now" to "now plus 30 minutes"; after the validity of the token has expired, the storage can no longer be accessed +* Using an Azure Storage SDK, creating SAS URLs is extremely simple. Tokens are created without Storage API interaction, simply by *signing* the URL with the application secret key. This in turn can be validated by Azure Storage (which obviously also has the secret key). + +We leverage the SAS feature to explicitly grant **write** access to one single blob (file) on the storage for which we define the file name. The access is granted for 60 minutes (one hour), which is enough to transfer large scale files. Our Contant API exposes an end point which returns an URL containing the SAS token which can immediately be used to do a `PUT` to the storage. + +
+![Azure Storage SAS - Diagram]({{ site.url }}/images/azure-storage-sas-1.png) +
+ +The upload to the storage can either be done using any http library (using a `PUT`), or using an Azure Storage SDK ([available for multiple languages](https://github.com/Azure?utf8=%E2%9C%93&query=storage), it's on github), which in turn enables features like parallel uploading or block uploading (for more robust uploading). + +### How does this look in code? + +The best part of all this is that it's not only simple in theory to use the Storage API, it's actually simple in practice, too. When I tried to do this, I chose [node.js](https://nodejs.org) to implement a service which issues SAS tokens. Azure Storage has an `npm` package for that: `azure-storage`, which can be installed just like any other `npm` package using `npm install azure-storage [--save]`. + +To get things up and running fast, I created a simple [Express](https://expressjs.com) application and replaced a couple of lines. The actual code for issuing a token is just the following: + +```javascript +app.post('/bulk/token', function(req, res) { + var blobService = azure.createBlobService(); + var startDate = new Date(); + var expiryDate = new Date(startDate); + expiryDate.setMinutes(startDate.getMinutes() + 100); + startDate.setMinutes(startDate.getMinutes() - 10); + + var filename = uuidGen.v4() + ".zip"; + + var container = 'bulkingest'; + if (process.env.AZURE_STORAGE_SAS_CONTAINER) + { + container = process.env.AZURE_STORAGE_SAS_CONTAINER; + } + + var sharedAccessPolicy = { + AccessPolicy: { + Permissions: azure.BlobUtilities.SharedAccessPermissions.READ + + azure.BlobUtilities.SharedAccessPermissions.WRITE, + Start: startDate, + Expiry: expiryDate + }, + }; + + var token = blobService.generateSharedAccessSignature(container, filename, sharedAccessPolicy); + var sasUrl = blobService.getUrl(container, filename, token); + + res.jsonp({ storageUrl: sasUrl, + filename: filename, + headers: [ { header: "x-ms-blob-type", value: "BlockBlob" } ], + method: "PUT" }); +}); +``` + +So, what does this do? + +* Creates a Blob Service (from the `azure-storage` package), using defined credentials +* Defines Start and End dates for the token validity (here, from 10 minutes ago, until in 100 minutes from now) +* Defines the container for which the token is to be created +* Creates a GUID as the file name for which to grant write access to +* Defines a shared access policy which combines permissions with start and end dates +* Generates a shared access signature (this is serverless, in the SDK) and assembles this into a URL +* Returns a JSON structure containing information on how to access the storage (hypermedia-like) + +For more information on how to actually try this out, see below. + +### Can I haz the codez? + +My sample project can be found on github: + +https://github.com/DonMartin76/azure-storage-sas + +Have fun! diff --git a/images/azure-storage-sas-1.png b/images/azure-storage-sas-1.png new file mode 100644 index 0000000000000000000000000000000000000000..832499e3c26fb964f8df4f3bde0409bacd5d5e3b GIT binary patch literal 11314 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 JNZ0Ak{{XfxR&D?Q literal 0 HcmV?d00001 diff --git a/resources/azure-storage-sas-resources.pptx b/resources/azure-storage-sas-resources.pptx new file mode 100644 index 0000000000000000000000000000000000000000..a68f3283def82ecb5dd898d4b2ab43f5efdbf11c GIT binary patch literal 33898 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 R0B9gLSxDYtB&VBK{{>{76v+Sp literal 0 HcmV?d00001