Introduction
In this tip, I show how you can secure access to a Microsoft Azure Service Bus queue by generating a Shared Access Signature.
Why Shared Access Signatures?
Whenever you’re working with resources in the cloud, whether you’re reading and writing data in blob storage or publishing and subscribing to messages on a queue, it’s always best to work with the “principle of least privilege”. In Microsoft Azure, this is achieved by using “Shared Access Signatures”.
A Shared Access Signature gives the holder of that signature access to a particular resource (like a blob or a queue), for a limited time, and with limited permissions (e.g. read only). The advantage is that you don’t need to give someone the full “connection string” just to allow them write to a specific blob, or post to a particular queue.
I have an article on my blog about how you can control access to an Azure Blob container with Shared Access Signatures, and it is relatively straightforward. However, when I tried to do the same for a Service Bus namespace, it turned out to be a bit trickier than I hoped, as the MSDN documentation omits certain key details.
So in this tip, I’ll demonstrate how we can create a Shared Access Signature that we can give someone to allow them to read messages off a specific queue.
Creating a Shared Access Signature
First of all, we need to create an authorization rule for our queue. You don’t need to create one for every SAS token you want to generate, just one for each set of permissions you want to grant.
Typically, you’d create one with “Listen
” rights, and one with “Send
” rights. The following code snippet shows us checking if the queue exists, and creating it if needed, and then checking if the shared access authorization rule exists, and creating it if needed. (Obviously, this rule is created by a process that does have the privileges to create shared access authorization rules).
var connectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;
SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=sgjasdhgjkasdhgawe3857238212";
const string queueName = "MyTestQueue";
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.QueueExists(queueName))
{
namespaceManager.CreateQueue(queueName);
}
var sharedAccessAuthorizationRuleKeyName = "MyQueue_Listen";
var queueDescription = namespaceManager.GetQueue(queueName);
var authRule = queueDescription.Authorization
.OfType<SharedAccessAuthorizationRule>()
.FirstOrDefault(a => a.KeyName == sharedAccessAuthorizationRuleKeyName);
if (authRule == null)
{
authRule = new SharedAccessAuthorizationRule(
sharedAccessAuthorizationRuleKeyName,
SharedAccessAuthorizationRule.GenerateRandomKey(),
new[] { AccessRights.Listen });
queueDescription.Authorization.Add(authRule);
namespaceManager.UpdateQueue(queueDescription);
}
Now we’re ready to create the shared access signature itself (with thanks to Brent for pointing me in the right direction here). We can choose an expiry time for the signature, which ideally would be fairly short. And we generate the Shared Access Signature like this:
var expiry = TimeSpan.FromMinutes(30);
var serviceUri = ServiceBusEnvironment.CreateServiceUri
("https", "mynamespace", queueName).ToString().Trim('/');
string generatedSaS = SharedAccessSignatureTokenProvider
.GetSharedAccessSignature(sharedAccessAuthorizationRuleKeyName,
? authRule.PrimaryKey, serviceUri, expiry);
Now we have a signature which will look something like this:
SharedAccessSignature sr=https%3a%2f%2fmynamespace.servicebus.windows.net%2fMyTestQueue&
sig=fFWmdMmWjsdTqPyhyvRS9LQqLjJNPc87xhInhYai9OM%3d&se=1453286209&skn=MyQueue_Listen
This is the string
we can give to the client we want to grant access to. Obviously it’s still a secret, but if it does leak, the potential damage is limited to only listening on this one queue, and only for the specified duration.
Using the Shared Access Signature
Now we’re ready to actually use the Shared Access Signature, and we do that with the TokenProvider
and MessagingFactory
. These will allow us to create a QueueClient
which we can use to receive a message (since our Shared Access Authorization Rule specified listen permissions).
var tp = TokenProvider.CreateSharedAccessSignatureTokenProvider(generatedSaS);
var mf = MessagingFactory.Create("sb://mynamespace.servicebus.windows.net", tp);
var listenClient = mf.CreateQueueClient(queueName);
var rxMessage = listenClient.Receive(TimeSpan.FromSeconds(10));
And for completeness, let’s check that our permissions really worked. We’ll try to send a message on this listen client. And we’ll try to create a new listen client for another queue that we weren’t granted access to:
try
{
listenClient.Send(new BrokeredMessage("should not be sent"));
throw new InvalidOperationException("SHOULD HAVE BEEN BLOCKED");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Very good - can't send");
}
try
{
var listenClient2 = mf.CreateQueueClient("AnotherQueue");
var m = listenClient2.Receive();
throw new InvalidOperationException("SHOULD HAVE BEEN BLOCKED");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Very good - can't listen on another queue");
}
Anyway, that’s it for creating Shared Access Signatures with Azure Service Bus queues. For topics, it’s a little more involved as you’ll want to grant manage rights to allow the holder of the SAS token to create a subscription, but otherwise the process is roughly the same.
History
- 21 Jan 2016 - Initial version