Showing posts with label Cloud. Show all posts
Showing posts with label Cloud. Show all posts

Saturday, 13 October 2018

AWS Cognito integration with lambda functions using the Serverless Framework

Problem

I have been writing an AWS lambda service based on the Serverless Framework. The question is, how do I secure the lambda using AWS Cognito?

Note that this post deals with Serverless Framework configuration and not how you setup Cogito user pools and clients etc. It is also assumed that you understand the basics of the Serverless Framework.

Solution

Basic authorizer configuration

Securing a lambda function with Cognito can be very simple. All you need to do is add some additional configuration – an authorizer - to your function in the serverless.yml file. Here’s an example:

functionName:
    handler: My.Assembly::My.Namespace.MyClass::MyMethod
    events:
      - http: 
          path: mypath/{id}
          method: get
          cors:
            origin: '*'
            headers:
                - Authorization
            authorizer:
              name: name-of-authorizer 
              arn: arn:aws:cognito-idp:eu-west-1:000000000000:userpool/eu-west-1_000000000

Give the authorizer a name (this will be the name of the authorizer that’s created in the API gateway). Also provide the ARN of the user pool containing the user accounts to be used for authentication. You can get the ARN from the AWS Cognito console.

SNAGHTML2d99d5f

After you have deployed your service using the Serverless Framework (sls deploy) an authorizer with the name you have given it will be created. You can find it in the AWS console.

SNAGHTML2dc34b3There is a limitation with this approach however. If you add an authorizer to each of you lambda functions like this you the number of authorizers will quickly proliferate. AWS limits the number of authorizers per API to 10 so for complex APIs you may run out of authorizers.

An alternative is to use a shared authorizer.

Configuring a shared authorizer

It is possible to configure a single authorizer with the Serverless Framework and share it across all the functions in your API. Here’s an example:

functionName:
    handler: My.Assembly::My.Namespace.MyClass::MyMethod
    events:
      - http: 
          path: mypath/{id}
          method: get
          cors:
            origin: '*'
            headers:
                - Authorization
          authorizer:
            type: COGNITO_USER_POOLS
            authorizerId: 
              Ref: ApiGatewayAuthorizer

resources:
  Resources:
    ApiGatewayAuthorizer: 
      Type: AWS::ApiGateway::Authorizer
      Properties: 
        AuthorizerResultTtlInSeconds: 300
        IdentitySource: method.request.header.Authorization
        Name: name-of-authorizer
        RestApiId: 
          Ref: "ApiGatewayRestApi"
        Type: COGNITO_USER_POOLS
        ProviderARNs: 
          - arn: arn:aws:cognito-idp:eu-west-1:000000000000:userpool/eu-west-1_000000000

As you can see we have created an authorizer as a resource and referenced it from the lambda function. So, you can now refer to the same authorizer (called ApiGatewayAuthorizer in this case) from each of your lambda functions. Only one authorizer will be created in the API Gateway.

Note that the shared authorizer specifies an IdentitySource. In this case it’s an Authorization header in the HTTP request.

Accessing an API using an Authorization header

Once you have secured you API using Cognito you will need to pass an Identity Token as part of your HTTP request. If you are calling your API from a JavaScript-based application you could use Amplify which has support for Cognito.

For testing using an HTTP client such as Postman you’ll need to get an Identity Token from Cognito. You can do this using the AWS CLI. Here’s as example:

aws cognito-idp admin-initiate-auth --user-pool-id eu-west-1_000000000 --client-id 00000000000000000000000000 --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=user_name_here,PASSWORD=password_here --region eu-west-1

Obviously you’ll need to change the various parameters to match your environment (user pool ID, client ID, user name etc.). This will return 3 tokens: IdToken, RefreshToken, and BearerToken.

Copy the IdToken and paste it in to the Authorization header of your HTTP request.

SNAGHTML2f7516f

That’s it.

Accessing claims in your function handler

As a final note this is how you can access Cognito claims in your lambda function. I use .Net Core so the following example is in C#. The way to get the claims is to go via the incoming request object:

foreach (var claim in request.RequestContext.Authorizer.Claims)
{
    Console.WriteLine("{0} : {1}", claim.Key, claim.Value);
}


See also

Sunday, 24 May 2015

How to setup a TeamCity build server with Azure

I thought it would be interesting to see what it would take to setup a Continuous Integration (CI) build server with Azure infrastructure. No doubt there are alternative ways of doing it but this approach certainly got me to a point where had a CI server installed and ready to go. In this case I used TeamCity.

Throughout this post I’ll be using the updated Azure portal.

Create a virtual network (optional)

The first thing I did was create a Virtual Network (VN). Why? Well, this would allow backend virtual machines to communicate with each other should that be necessary. If you’re unsure if you need one this article might help.

To set up a VN click the NEW icon and choose Networking > Virtual Network.

SNAGHTML6d27dde
To create a VN enter a name and click ADDRESS SPACE. This section is required. In the example above I’ve added an Address space CIDR block of 10.0.0.0/16. I’ve then created a subnet with a CIDR block of 10.0.1.0/24. I’ll be adding other resources such as a Virtual Machine (VM) to this subnet.

You might be interested in this if you’re new to Azure VN: http://azure.microsoft.com/en-gb/documentation/services/virtual-network/

Create storage account (optional)

The next thing I did was to create a Storage Account. This would be used for the virtual disks etc. required by the VM I created later. You can omit this step in which case a storage account will be created for you when you create the virtual machine.

SNAGHTML6d1ea58

To add a new storage account click the NEW icon and go to Data + Storage > Storage. It’s quite annoying but when adding the name for your storage account you’re restricted to letters and numbers only. If like me you use a naming convention that involved hyphens you’re hosed.

Create SQL server database (optional)

I chose to use a SQL Server database for TeamCity. Of course there are other options including the default HSQLDB database engine that ships with TeamCity. The problem with the default database is that it can crash and lose your data as well as other problems:

“The internal database may crash and lose all your data (e.g. on out of disk space condition). Also, internal database can become extremely slow on large data sets (say, database storage files over 200Mb). Please also note that our support does not cover any performance or database data loss issues if you are using the internal database.
In short, do not EVER use internal HSQLDB database for production TeamCity instances.” - Setting up an External Database

SQL Server it is. To add a new SQL Server database click the NEW icon and go to Data + Storage > SQL Database. You may need to add a new SQL Server instance or you can use an existing one.


SNAGHTML6deb84c

 

It’s worth having a look at the pricing information for SQL Server in Azure. I chose S0 for this installation that comes out at about £9.16 per month at the time of writing.


SNAGHTML6e17f3c

 

You can get the connection details for your server from the settings but if you try to connect straightaway using SQL Server Management Studio (SSMS) from your local machine you will be disappointed. Before you can connect you need to punch a hole in the firewall.

Select your SQL Server from the browse list in the Azure portal and go to Settings > Firewall. Add a rule with a name and an IP address range. I didn’t add a range because I just wanted my desktop machine to be able to connect - so the start and end IP addresses were the same.


SNAGHTML6e87ad5

 

Don’t forget to save! I did first time around.

SNAGHTML6e93a9b

 

At this point I was able to connect to the newly created SQL Server using SSMS and was able to create a blank database ready to be used by TeamCity. I also added a login and a database user, something like this:

CREATE LOGIN teamcity 
    WITH PASSWORD = 'password_goes_here' 
GO

CREATE USER teamcity
    FOR LOGIN teamcity
GO

Create a Virtual Machine

So far I had a virtual network, a storage account and a SQL Server complete with blank database. Now it was time to create the Virtual Machine (VM) ready to install TeamCity.
To create a new VM click the NEW icon and go to Compute > Windows Server 2012 R2 Datacenter. Fill in the host name, user name and password. The next step is to choose your pricing plan. For this example I chose D1 but you can always check out the pricing guide.


SNAGHTML701ba07

After choosing the price band etc. it’s time to set up the networking side of things. Remember the VN I setup right at the beginning? Well, this is where it came in to play. By selecting the OTIONAL CONFIGURATION I was able to set the Virtual Network and assign the VM to a subnet under NETWORK.


SNAGHTML70fe672

I was also able to assign the storage account that I created earlier on too. This time under the STORAGE ACCOUNT in the optional configuration.

So, at this point I had a Virtual Machine on my Virtual Network and using my storage account.Time to logon to the Virtual Machine.

Logon to the Virtual Machine and install TeamCity

Logging on the the VM was quite easy. Just click on Connect when viewing the VM properties.

 
SNAGHTML7159d18
HINT: I had a few issues with connecting this was from the new portal. I was able to connect by switching to the old portal and doing it from there.

Once I had logged in to the new VM the first job was to download the TeamCity installation package an run it. For this exercise I installed to the default locations but you might consider setting up a separate disk in your VM. Note that I configured TeamCity to run on port 80. More on this later.

Once the basic installation was complete I ran the browser-based UI which starts the setup of TeamCity. When it came to the database I selected the SQL Server database type and entered the details I prepared earlier on my Azure-based SQL Server.

One thing to remember is you need to install the SQL Server JDBC driver by copying the jar into the JDBC directory.


SNAGHTML78e711a

Once the setup was complete I could check with SSMS back on my local machine and see the tables etc. had been created in the SQL Server database.


SNAGHTML798609e
At this point TeamCity was installed and the UI was viewable locally on the VM on port 80. However to make the TeamCity UI visible from my local development machine there were a couple more steps to take.

Firstly, in the VM needed to add some firewall rules added to allow inbound and outbound traffic on port 80. To do this I opened the Server Manager and selected Tools > Windows Firewall with Advanced Security.


SNAGHTML7946da2

Then it was a case of adding the new rules. A picture is worth a thousand words so here’s a few thousand words worth showing the basic steps and settings for the inbound rule (the outbound rule is much the same):


image


image


image


image


image


image


With the firewall rules completed in the VM there was one thing left to do: add a firewall rule to the VM in the Azure portal. This involved the following:

  • Select the VM in the browse list in the Azure portal.
  • Click Settings.
  • Click Endpoints.
  • Click Add and create a new endpoint for the TCP protocol on port 80. 


SNAGHTML79a4ea3

With that done I was able to view the TeamCity UI over the web from my local development machine.

Job’s a good ‘un.