Skip to main content

Screen Sharing using Service Bus Relay (Azure) - an out of the box Tunneling (Port Bridge) solution

Part 2:  How to open a RDP connection to a device that doesn't has direct access to internet using Azure Service Bus Relay http://vunvulearadu.blogspot.ro/2015/04/how-to-open-rdp-connection-to-device.html

The problem
We started to work on a project where we need to be able to offer a reliable solution for Remote Screen Sharing - RDP (Remote Desktop Protocol). Luckily, all devices are running Windows operating system and we can use with success the support from Windows.
Because all the devices are spreaded world wide we need a tunneling solution. All the content that is send over the wire needs to be encrypted. In theory we would need some relays servers that would be ‘the man on the middle’ between devices and people that would like to access that device - tunneling.

There are some solutions on the market for Relay Servers and Tunneling. Some of them are open source and free, others are expensive and very exotic. Our solution would run on Azure, because of this, we want to reduce the cost of operational and maintenance tasks as much as possible. Having some instances for Relay Server would require extra maintenance costs of that virtual machines. In the end we want to be as flexible as possible and to be able to scale up and down base on clients needs.
On top of this, we need to support peeks that can reach 500 RDP sessions in the same time. Normally we need to support 30-50 RDP sessions, but we need to be able to support 500 RDP sessions.
From the moment when a client notify us that he wants to open a RDP session to the moment when he actually needs open the session we have 8 seconds to do all the magic. In this short period of time we need to notify device, open the tunneling channel with the relay server and establish the secure connection.
I tried to look on a solution that are scalable, because now we can have a peak of 500 RDP sessions, but we need to be prepared to support more than that. I don't want to manage or debug and fix issues in a tunneling solution that don't work as expected. This would require extra time to understand how the system works, how the implementation is done and so on – in the end this means extra costs and all the time we would need to have at least one person that know very good the tunneling solution and the problems with it.

The solution
Service Bus Relay is a service that was built for hybrid application, which run on Azure and on-premises. It allow us to expose WCF services from on-premises system that can be accessed from Azure without having to manage firewall rules, open ports or other things. It helps us to expose a secure channel (a tunnel) between two endpoints in a secure and reliable way. All the communication with Service Bus Relay is made over HTTPS.

Implementation
Hmmm…  Service Bus Relay could help us a lot. In this case it sounds like the perfect solution. If we could redirect the traffic generated by RDP connection through Service Bus Relay to the other endpoint...
Luckily this can be done easily. There is a great sample code written by Clemens Vasters (http://blogs.msdn.com/b/clemensv/archive/2009/11/18/port-bridge.aspx) that works very well. It is not yet production ready, but it is a good starting point, especially for a PoC, when you want to validate if a solution works or not.
I will not describes in details the solution that is offered by Clemens Vasters, because if it describes in details in his blog post.
The solution includes an agent called Port Bridge Agent that will run on our devices. The agent will redirect the traffic of a specific port to Service Bus Relay. In our case we will redirect all the traffic generated by RDP session to Service Bus Relay
On the client machine, that wants to access the device, we will start another application (service) that will do a similar thing – map a custom port to the same relay from Service Bus Relay. In the moment when we will start the Remote Desktop Connection client we will specify the port where Service Bus Relay is configured to expose the data.

In this way we will have a tunneling solution between this two machines over Service Bus Relay without having to open firewalls ports, having complex IP confirmations or having to manage relay servers.
If you want to make the sample work with Shared Access Policy token you will need to change the configuration of ‘TransportClientEndpointBehavior. The token provider needs to be specified using Shared Access Signature – see the below code.
relayCreds = new TransportClientEndpointBehavior 
{ 
   TokenProvider = TokenProvider.
       CreateSharedAccessSignatureTokenProvider(
       sharedAccessKeyName,sharedAccessKey) 
};


Authentication
In this moment Service Bus Relay is the only service from Service Bus that does not have support for Shared Access Signature (SAS). Because of this we need to create and work with Shared Access Policies. This are pretty similar with SAS, but we cannot control access at relay level, only at namespace level.
Because of this we could have a security issue there. If the same namespace is shared between two different tunneling sessions, than both sessions content could be accessed by both clients (and this is possible and pretty simple - at package level)

Another problem with them is the number of policies that can be defined on a Service Bus Namespace. We can have maximum 12 of them (Shared Access Policies).
The simplest solution is to create and use a different namespace for each tunneling session. In this way from a security perspective we can manage access in a secure and reliable way. More about this will be described in the next part.

Proposed solution
We can create a pool of Service Bus namespaces that are managed by a server components. All the time we can have 30 namespace available in the pool for example. The service can increase or decrease the number of namespaces available in the pool based on the load or the hour. Creating a Service Bus namespace don’t require a long period of time. Even if there is no SLA related to how long it takes, we observed normally it does not takes more than 20 seconds. This pool can contains namespaces from different Azure subscriptions.
The cost of a namespace per month is ~7€. But if we create a namespace only for 2 days, we will pay only for that 2 days.

Above we can see the service that will be able to allocate namespaces from the pool. After that the service will be able to recycle them. When a session ends, the system will  be able to invalidated the access key and regenerate new keys. In this way the old keys cannot be used anymore.

Price
In this moment the price of 100 hours of relay is 0.10$. 100.000 messages send over a relay would cost us 0.01$.
We calculate how much traffic a RDP session generates for 1920x1080 screen size (32b colors), using RDP 6.1. For normal use, we observed that in general for every 5 minutes of connection we consume:

  • 18MB data traffic
  • 9000 messages over relay

Based on this estimations, in one hour we will consume:

  • 106.800 messages
  • 204 MB data traffic (outbound)
  • 0.001$ the cost of an hour of open relay

The total cost for 1 hour of RDP is 0.21$, for normal use at a screen resolution of 1920x1080. A great price for an out of the box solution that does not requires maintenance and custom code and configuration on the relay server.

Possible problems
The maximum number of namespaces under an Azure subscriptions is 100. To be able to have more than 100 namespaces available we can request the limitation to be changed for our Azure subscription or  to use and manage more than one Azure subscription.
From time to time, using the default Windows RDP protocol we received “Because of an error in data encryption, this session will end”. It is not clear for us the root cause of the problem. The problem occurs especially in the moment when the screen content is updates and a lot of content is send by RDP. We suspect that the problem is related to package size. It seems that if we change the security layer to SSL (TLS 1.0) the problem disappear entirely. Further investigation needs to be done.
Last thing is not a real problem but we should be aware of it. To be able to use RDP over Service Bus Relay we need to deactivate the certificate security from Remote Desktop Connection. The client is behind a tunnel (Service Bus Relay) that will not be able to ‘virtualize’ the client certificate. Take into account that we are already using a secure connection, offered by the tunnel.

Conclusion
Service Bus Relay is an extremely interesting service that is very capable and scalable. It works great as a tunnel mechanism for RDP – an out of the box solution. Without having to manage custom configuration, relay servers or other custom components, we can say that it is a great option for tunneling. Also, what is great is that we don't have to use a Remote Desktop solution from Microsoft. In the end we can use any kind of protocol, Service Bus Relay is only the por bridge solution for tunneling.
The sample code for port bridge it is not a production code, but it is a good starting point that I highly recommend.

Part 2:  How to open a RDP connection to a device that doesn't has direct access to internet using Azure Service Bus Relay http://vunvulearadu.blogspot.ro/2015/04/how-to-open-rdp-connection-to-device.html

Comments

Popular posts from this blog

Windows Docker Containers can make WIN32 API calls, use COM and ASP.NET WebForms

After the last post , I received two interesting questions related to Docker and Windows. People were interested if we do Win32 API calls from a Docker container and if there is support for COM. WIN32 Support To test calls to WIN32 API, let’s try to populate SYSTEM_INFO class. [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } ... [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); ... SYSTEM_INFO pSI = new SYSTEM_INFO(

Azure AD and AWS Cognito side-by-side

In the last few weeks, I was involved in multiple opportunities on Microsoft Azure and Amazon, where we had to analyse AWS Cognito, Azure AD and other solutions that are available on the market. I decided to consolidate in one post all features and differences that I identified for both of them that we should need to take into account. Take into account that Azure AD is an identity and access management services well integrated with Microsoft stack. In comparison, AWS Cognito is just a user sign-up, sign-in and access control and nothing more. The focus is not on the main features, is more on small things that can make a difference when you want to decide where we want to store and manage our users.  This information might be useful in the future when we need to decide where we want to keep and manage our users.  Feature Azure AD (B2C, B2C) AWS Cognito Access token lifetime Default 1h – the value is configurable 1h – cannot be modified

What to do when you hit the throughput limits of Azure Storage (Blobs)

In this post we will talk about how we can detect when we hit a throughput limit of Azure Storage and what we can do in that moment. Context If we take a look on Scalability Targets of Azure Storage ( https://azure.microsoft.com/en-us/documentation/articles/storage-scalability-targets/ ) we will observe that the limits are prety high. But, based on our business logic we can end up at this limits. If you create a system that is hitted by a high number of device, you can hit easily the total number of requests rate that can be done on a Storage Account. This limits on Azure is 20.000 IOPS (entities or messages per second) where (and this is very important) the size of the request is 1KB. Normally, if you make a load tests where 20.000 clients will hit different blobs storages from the same Azure Storage Account, this limits can be reached. How we can detect this problem? From client, we can detect that this limits was reached based on the HTTP error code that is returned by HTTP