YARBACS or Yet Another RBAC Solution

RBAC is all the rage (as it should be) these days, but it’s not really new.  The concepts have been in use for many years, it’s just a case of bringing it into modern cloud-based scenarios.  Azure continues to invest in this concept so that we can increasingly lock down the control and use of various cloud-based services they offer.  There’s other “interesting” aspects to these scenarios though that can impact the availability and usefulness of it – things such as existing systems and identities, cross tenant applications, etc.

It’s these interesting aspects that bring me to this post today.  At Office365Mon we are ALL about integration with Azure Active Directory and all of the goodness it provides.  We also have had from day 1 the concept of allowing users from different tenants to be able to work on a set of resources using a common security model.  That really means that when you create Office365Mon subscriptions, you can assign subscription administrators to anyone with an Azure AD account.  It doesn’t require Azure B2C, or Azure B2B, or any kind of trust relationship between organizations.  It all “just works”, which is exactly what you want.  IMPORTANT DISCLAIMER:  the RBAC space frequently changes.  You should check in often to see what’s provided by the service and decide for yourself what works best for your scenario.

“Just works” in this case started out being really just a binary type operation – depending on who you are, you either had access or you didn’t.  There was no limited access based on one or more roles that you had.  As we got into more complex application scenarios it became increasingly important to develop some type of RBAC support capabilities, but there really wasn’t an out of the box solution for us.  That led us to develop this fairly simple framework of YARBACS as I call it, or “Yet Another RBAC Solution”.  It’s not a particularly complicated approach (I don’t think) so I thought I’d share the high level details here in case it may help those of you who are living with the same sort of constraints that we have.  We have a large existing user base, everyone is effectively a cloud user to us, we don’t have any type of trust relationship with other Azure AD tenants, but we need to be able to support a single resource (Office365Mon subscription) to be managed by users from any number of tenants and with varying sets of permissions.

With that in mind, these are the basic building blocks that we use for our YARBACS:

  1. Enable support for Roles in our Azure AD secured ASP.NET application
  2. Create application roles in our AD tenant that will be used for access control
  3. Store user and role relationships so that it can be used in our application
  4. Add role attributes to views, etc.
  5. Use a custom MVC FilterAction to properly populate users from different tenants with application roles from our Azure AD tenant

Let’s walk through each of these in a little more detail.

 

Enable Support for Roles

Enabling support for roles in your Azure AD secured application is something that has been explained very nicely by Dushyant Gill, who works as a PM on the Azure team.  You can find his blog explaining this process in more detail here:  http://www.dushyantgill.com/blog/2014/12/10/roles-based-access-control-in-cloud-applications-using-azure-ad/.   Rather than trying to plagiarize or restate his post here, just go read his.  J   The net effect is that you will end up with code in your Startup.Auth.cs class in your ASP.NET project that looks something like this:

app.UseOpenIdConnectAuthentication(

new OpenIdConnectAuthenticationOptions

{

ClientId = clientId,

Authority = Authority,

TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters

{

//for role support

RoleClaimType = “roles”

},

… //other unrelated stuff here

Okay, step 1 accomplished – your application supports the standard use of ASP.NET roles now – permission demands, IsInRole, etc.

 

Create Application Roles in Azure AD

This next step is also covered in Dushyant’s blog post mentioned above.  I’ll quickly recap here, but for complete steps and examples see Dushyant’s blog post.  Briefly, you’ll go to the application for which you want to use YARBACS and create applications roles.  Download the app manifest locally and add roles for the application – as many as you want.  Upload the manifest back to Azure AD, and now you’re ready to start using them.  For your internal users, you can navigate to the app in the Azure management portal and click on the Users tab.  You’ll see all of the users that have used your app there.  You can select whichever user(s) you want to assign to an app role and then click on the Assign button at the bottom.  The trick of course is now adding users to these groups (i.e. roles) when the user is not part of your Azure AD tenant.

 

Store User and Role Relationships for the Application

This is another step that requires no rocket science.  I’m not going to cover any implementation details here because it’s likely going to be different for every company in terms of how and what they implement it.  The goal here is simple though – you need something – like a SQL database – to store the user identifier and the role(s) that user is associated with.

In terms of identifying your user in code so you can look up what roles are associated with it, I use the UPN.  When you’ve authenticated to Azure AD you’ll find the UPN is in the ClaimsPrincipal.Current.Identity.Name property.  To be fair, it’s possible for a user’s UPN to change, so if you find that to be a scenario that concerns you then you should use something else.  As an alternative, I typically have code in my Startup.Auth.cs class that creates an AuthenticationResult as part of registering the application in the user’s Azure AD tenant after consent has been given.  You can always go back and look at doing something with the AuthenticationResult’s UserInfo.UniqueId property, which is basically just a GUID that identifies the user in Azure AD and never changes, even if the UPN does.

Now that you have the information stored, when we build our MVC ActionFilter we’ll pull this data out to plug into application roles.

 

Add Role Attributes to Views, Etc.

This is where the power of the ASP.NET permissions model and roles really comes into play.  In step 1 after you follow the steps in Dushyant’s blog, you are basically telling ASP.NET that anything in the claim type “roles” should be considered a role claim.  As such, you can start using it the way you would any other kind of ASP.NET role checking.  Here are a couple of examples:

  • As a PrincipalPermission demand – you can add a permission demand to a view in your controller (like an ActionResult) with a simple attribute, like this:  [PrincipalPermission(SecurityAction.Demand, Role = “MyAppAdmin”)].  So if someone tries to access a view and they have not been added to the MyAppAdmin role (i.e. if they don’t have a “roles” claim with that value), then they will get denied access to that view.
  • Using IsInRole – you can use the standard ASP.NET method to determine if a user is in a particular role and then based on that make options available, hide UI, redirect the user, etc. Here’s an example of that:  if (System.Security.Claims.ClaimsPrincipal.Current.IsInRole(“MyAppAdmin “))…  In this case if the user has the “roles” claim with a value of MyAppAdmin then I can do whatever – enabling a feature, etc.

Those are just a couple of simple and the most common examples, but as I said, anything you can do with ASP.NET roles, you can now do with this YARBACS.

 

Use a Custom FilterAction to Populate Roles for Users

This is really where the magic happens – we look at an individual and determine what roles we want to assign to it.   To start with, we create a new class and have it inherit from ActionFilterAttribute.  Then we override the OnActionExecuting event and plug in our code to look for the roles for the user and assign them.

Here’s what the skeleton of the class looks like:

public class Office365MonSecurity : ActionFilterAttribute

{

public override void OnActionExecuting(ActionExecutingContext filterContext)

{

//code here to assign roles

}

}

Within the override, we plug in our code.  To begin with, this code isn’t going to do any good unless the user is already authenticated.  If they haven’t then I have no way to identify them and look up the roles (short of something like a cookie, which would be a really bad approach for many reasons).  So first I check to make sure the request is authenticated:

if (filterContext.RequestContext.HttpContext.Request.IsAuthenticated)

{

//assign roles if user is authenticated

}

 

Inside this block then, I can do my look up and assign roles because I know the user has been authenticated and I can identify him.  So here’s a little pseudo code to demonstrate:

SqlHelper sql = new SqlHelper();

List<string> roles = sql.GetRoles(ClaimsPrincipal.Current.Identity.Name);

 

foreach(string role in roles)

{

Claim roleClaim = new Claim(“roles”, role);

ClaimsPrincipal.Current.Identities.First().AddClaim(roleClaim);

}

 

So that’s a pretty good and generic example of how you can implement it.  Also, unlike application roles that you define through the UI in the Azure management portal, you can assign multiple roles to a user this way.  It works because they just show up as role claims.  Here’s an example of some simple code to demonstrate:

 

//assign everyone to the app reader role

Claim readerClaim = new Claim(“roles”, “MyAppReader”);

ClaimsPrincipal.Current.Identities.First().AddClaim(readerClaim);

 

//add a role I just made up

Claim madeUpClaim = new Claim(“roles”, “BlazerFan”);

ClaimsPrincipal.Current.Identities.First().AddClaim(madeUpClaim);

This code actually demonstrates a couple of interesting things.  First, as I pointed out above, it adds multiple roles to the user.  Second, it demonstrates using a completely “made up” role.  What I mean by that is that the “BlazerFan” role does not exist in the list of application roles in Azure AD for my app.  Instead I just created it on the fly, but again it all works because it’s added as a standard Claim of type “roles”, which is what we’ve configured our application to use as a role claim.  Here’s a partial snippet of what my claims collection looks like after running through this demo code:

yarbacs

To actually use the FilterAction, I just need to add it as an attribute on the controller(s) where I’m going to use it.  Here’s an example – Office365MonSecurity is the class name of my FilterAction:

[Office365MonSecurity]

public class SignupController : RoutingControllerBase

There you have it.  All of the pieces to implement your own RBAC solution when the current service offering is not necessarily adequate for your scenario.  It’s pretty simple to implement and maintain, and should support a wide range of scenarios.

Fun with Azure Key Vault Services

I was able to spend a little time recently with a new Azure service, the Key Vault service, for some work I was doing. It’s a pretty valuable and not too difficult service that solves an age old problem – where can I securely keep secrets for my applications in Windows Azure. Actually because of the way it’s implanted you really don’t even need to have your application hosted in Azure…but I’m getting a little ahead of myself. Let’s start with the basics. As a precursor to what I have here, I’ll just point out that there’s actually some pretty good documentation on this service available at http://azure.microsoft.com/en-us/services/key-vault/.

Getting Started

Before you start trying to build anything, you really need to have the latest version of the Azure PowerShell cmdlets, as well as the new cmdlets they’ve built for working with Key Vault. You can get the very latest of the Azure PowerShell cmdlets by going here: https://github.com/Azure/azure-powershell/releases. You can get the Key Vault cmdlets by going here: https://gallery.technet.microsoft.com/scriptcenter/Azure-Key-Vault-Powershell-1349b091.

Create a New Vault and Secret(s)

The next step is to crack open your Azure PowerShell window and load up the Key Vault cmdlets. You can do that like this:

Set-ExecutionPolicy Bypass -Scope Process

import-module C:\DirectoryYouExtractedKeyVaultCmdletsTo\KeyVaultManager

I’m just turning off policy to only run signed cmdlets with the first line of code (and just in this process), and then loading up the cmdlets with the next line of code. After that you need to connect to your Azure AD tenant like this:

add-azureaccount

If you have multiple subscriptions and you want to target the specific subscription where you want to create your Key Vault and secrets and keys, then you can do this:

Set-AzureSubscription -SubscriptionId some-guid-here

You’ll see a list of guids for your subscription after you log in with the add-azureaccount cmdlet. Now that you’re logged in and set in your subscription, you can do the first step, which is to create a new vault. The PowerShell for it is pretty easy – just this one line of code:

New-AzureKeyVault -VaultName “SteveDemo” -ResourceGroupName “SteveResources” -Location “West US”

There are a few things worth noting here:

  • The VaultName must be unique amongst ALL vaults in Azure.  It’s just a like an Azure storage account in that sense.  The name will become part of the unique Url you use to address it later.
  • The ResourceGroupName can be whatever you want.  If it doesn’t exist, the cmdlets will create it.
  • The locations are limited right now.  In the US you can create a vault in the West and East but not Central.  Azure should have some documentation somewhere on which locations you can use (i.e. I’m sure they do, I just haven’t gone looking for it yet).

Okay cool – we got a vault created, now we can create keys and secrets. In this case I’m going to use the scenario where I need some kind of database connection string. Assume as well that like in most cases, I have a separate database for dev and production. So what I’m going to do is just create two secrets, one for each. Here’s what that looks like:

$conStrKey = ConvertTo-SecureString ‘myDevConStr’ -AsPlainText -Force

$devSecret = Set-AzureKeyVaultSecret -VaultName ‘SteveDemo’ -Name ‘DevConStr’ -SecretValue $ conStrKey

$ conStrKey = ConvertTo-SecureString ‘myProdConStr’ -AsPlainText -Force

$prodSecret = Set-AzureKeyVaultSecret -VaultName ‘SteveDemo’ -Name ‘ProdConStr’ -SecretValue $ conStrKey

Awesome, we’re ready to go, right? Mmm, almost, but not quite. Like all things Azure, you need to configure an application that you’ll use to access these secrets. But actually its different (of course) than other apps, in that you don’t pick it from a list of services and select the features you want to use. That may come later, but it’s not here yet.

Grant Your Application Rights to the Secrets

There are a few steps here to get your app rights to the secrets you created.

  1. Create a new application in Azure AD.  I won’t go through all the steps here because they’re documented all over the place.  But the net is you create a new app, say it’s one you are developing, and type in whatever value you want for the sign in and app ID.  After that you need to go copy the client ID and save it somewhere, then create a new key and save it somewhere.  You’ll use these later.
  2. Go back to your PowerShell window and grant rights to read keys and/or secrets to your application.  You can do that with a line of PowerShell that looks like this:

Set-AzureKeyVaultAccessPolicy -VaultName ‘SteveDemo’ -ServicePrincipalName theClientIdOfYourAzureApp -PermissionsToKeys all -PermissionsToSecrets all

In this case I kind of cheated and took the easy way out by granting rights to all possible permissions to my app. If you just wanted it to be able to read secrets then you could configure it that way. One other thing worth noting – one of the most common errors I have seen from folks using this service is that they only remember to grant PermissionsToKeys or PermissionsToSecrets but not both. If you do that then you will get these kind of weird errors that are say something like “operation ‘foo’ is not allowed”. Well, yeah, technically that’s correct. What’s really happening is that you’re forbidden from doing something that you have not expressly granted your application rights to do. So be on the lookout for that.

Use Your Secrets

Okay cool, now that we’ve got our app all set up we can start using these secrets, right? Yes! 🙂  The first thing I would recommend doing is downloading the sample application that shows off performing all of the major Key Vault functions. The latest sample is at http://www.microsoft.com/en-us/download/details.aspx?id=45343. I just know they will change the location any day now and my blog will be out of date, but hey, that’s what it is today. Now there is supposed to be a Nuget package that would allow you to use this more easily from .NET managed code, but I currently cannot find it. If you go get the code download though you will see a sample project that you can easily compile for a nice assembly-based access to the vault.

Going back to something I mentioned at the beginning though – about not “even need(ing) to have your application hosted in Azure” – one of the really great things about this service is that it’s all accessible via REST. So…that gives you LOTS of options, both for working with the content in the vault, as well as the tools, platforms, and hosting options for your applications that use it. For me, I feel that the most common use case by a wide, wide margin is an application that needs to read a secret – like a connection string. So what I did was go through the sample code and reverse engineer out a few things. The result was a much smaller, simpler piece that I use just for retrieving a secret. The pseudo-code goes something like this:

  1. Get an Azure AD access token
    1. Use the client ID and client secret you obtained earlier, and combine that with the Azure AD resource ID for the Key Vault service – which is https://vault.azure.net.
    2. Get the access token by creating a ClientCredential (with the client ID and client secret), and using the resource ID.  The code looks something like this:

var clientCredential = new ClientCredential(client_id, client_secret);
var context = new AuthenticationContext(Authority + authorityContext, null);
var result = context.AcquireToken(“https://vault.azure.net&#8221;, clientCredential);

There’s one other REALLY important thing to note here. For those of you familiar with Azure AD and AuthenticationResults, you may be used to getting your them using the Common Login authority. In this case, you will get an AuthenticationResult, but you will get a 403 Forbidden when trying to use it. Mucho painful debugging to figure this out. You MUST use the tenant ID of the Azure AD instance where your application is registered. The tenant ID is just another GUID you can get out of the Azure portal. So for instance in the code above where it’s getting the AuthenticationContext, those two variables look like this:

const string Authority = “https://login.windows.net/&#8221;;
string authorityContext = “cc64c719-d217-4c44-dd24-42c18f9cb9f2”;

  1. Create a new HttpClient and plug in the access token from step 1 into an authorization header.  That code looks something like this:

HttpClient hc = new HttpClient();

hc.DefaultRequestHeaders.Authorization = new
System.Net.Http.Headers.AuthenticationHeaderValue(
“Bearer”, accessToken);

  1. Configure the request headers to indicate that you accept application/json, like this:

hc.DefaultRequestHeaders.Accept.Add((new
MediaTypeWithQualityHeaderValue(“application/json”)));

  1. Create the Url you are going to use to access your secret.  Here’s my example:

string targetUri = “https://stevedemo.vault.azure.net/secrets/prodconstr?api-version=2014-12-08-preview&#8221;;

A couple of key things to remember about this Url:

  • “stevedemo” is the name of my vault.  This is why it has to be unique in all of Azure.
  • “prodconstr” is the name of the secret I created.  There is also an ability to add a GUID after that which corresponds to a specific version of a secret if needed.  I won’t cover it here, but it’s in the documentation if you need it.
  1. Request your secret.  My code looks like this:

//NOTE:  Using await here without ConfigureAwait causes the
//thing to disappear into the ether
HttpResponseMessage hrm = await hc.GetAsync(new
Uri(targetUri)).ConfigureAwait(false);

  1. Get the results.  My code:

if (hrm.IsSuccessStatusCode)
{
Secret data = await DeserializeAsync<Secret>(hrm);
result = data.Value;
}

In the spirit of being honest… 🙂 …I borrowed the code to deserialize the results into a class. It’s extremely short and the class is quite simple, so it only takes about 30 seconds of copy and paste.

Using the Secret

So there you have it – a pretty quick and easy way to get at your secrets. Using it within my code then is really simple. I have one line of code to retrieve the secret using the code shown above:

conStr = VaultSecret.GetSecret(KEY_VAULT_NAME, KEY_VAULT_SECRET_NAME).Result;

Where VaultSecret is the name of my class where I put the code I showed above, GetSecret is the method I created that contains that code, and, well, hopefully the two parameters are self-explanatory. Overall – good stuff, all things being equal relatively quick to get up and going with it.