For a few months, it is clear that the Azure Service Bus team is moving away from ACS (but still supports it!) as the main authentication mechanism. This has been detailed in various blog posts and news items and the intention of this blog post is not to repeat what has been said before.
Instead, we will focus on how it is possible to secure relay endpoints with Shared Access Signatures. This is currently not well-documented. This blog post describes how you can easily secure a relay service, using SAS.
Too often, we see the usage of the RootManageSharedAccessKey in demo and sample code (mine sometimes included). And earlier, a lot of those samples were using the 'owner' SecretKey. What's worse, I've seen these things in production. The main reason is probably that it is too much work for people to create specific user rights on messaging entities or on relay endpoints. But ignoring security is never good, right?
Especially when deploying services in the field, in remote data centers, computers and even devices. There it is crucial to ensure that the credentials only allows that specific service, device or client to perform the actions they are entitled to. By doing so, it is also easy to revoke one of those clients if that would be needed, without impacting the other existing connections.
Secure on path-level (Uri)
Just like we can enable authorization rules on messaging entities, such as queues or topics, we can also enable specific access rules for the hierarchical paths on relay endpoints. When we deploy several of our cloud connectors for Integration Cloud (these are on prem agents, exposing a relay endpoint), we always foresee a unique path per connector endpoint and we also make sure that the credentials that are deployed with the connector have the minimum rights to listen on that exact URI (path) and not on other locations.
Security validation with Relay Services
When using the WCF bindings for relay services, you specify service credentials and the Path where the service will be registered. When a service wants to register on such an endpoint, the Azure Service Bus Relay service will verify if the following conditions are met:
- Is the service registering with valid credentials? (authentication)
- Is the service allowed to Listen on that specific Uri/Path with those credentials? (authorization)
The same method is used when a client is calling a relay endpoint with specific credentials. Instead now it is verified if he has the Send access rights. The next table shows the various access rights.
The following access rights are available to secure the hierarchy of a Service Bus namespace. There is a difference in usage and working between the Relay Services and Messaging entities.
|Relay Services||Messaging entities|
Right to open a service endpoint on the Uri
Right to receive messages from entities on the entities under that Uri
Right to call a service endpoint on the Uri
Right to send messages to entities on the entities under that Uri
Right to change settings of relay endpoints
Right to create, change or delete entities under that Uri
Create SAS authorization rules
In order to deploy a relay service in the wild, we want to use security credentials that have the minimum required Access Rights. The following code sample shows how to do this.
What's important to notice here is that we explicitly define the RelayDescription for a specific Path and on that Path, we create one or more SharedAccessAuthorizationRules. These can be used to give Listen, Send or Manage rights.
In practice, there will be a SharedAccessAuthorizationRule for the service (ListenAccessKey and one for every client that needs to call the service (in this case we created one: SendAccessKey). This way we have full flexibility in revoking clients and when the SAS Key of the service gets compromised, the potential damage is restricted to the subpath of our namespace.
Using the keys in the web service or web clients
To use the keys in the web client or the web service, the following app/web.config settings can be specified. There is one thing that is crucial here: the binding has to specify that it is not a dynamic binding! If this is not done, you can get the AddressAlreadyInUseException: System.ServiceModel.AddressAlreadyInUseException: System error. Therefore, use the isDynamic="false" setting.
Note: This took me a real long time to find out and it was Dan Rosanova who pointed that one out to me. Thanks for that!
The web service web.config
The web client app.config
These things are also be available through the latest update of the ServiceBusExplorer tool, by Paolo Salvatori.