May 5, 2010 3 Comments
Yeah, I know. I said my next posts were going to be more articles about the Windows Azure Diagnostics. Course I also thought I’d have those up a week after the first. Life has just been too hectic of late I guess. I’d love to admit I’m back of my own free will but I feel I have a debt that needs to be paid.
So I’ve spent the last two months working this Azure POC project. Nearly everything was done except for one important point, connecting a hosted application to an on-premise SQL Server. The best practice recommendations I’d been seeing for the last year on the MSDN forums always said to expose your data tier via a series of web services that your hosted application could then consume. You could secure those services however you saw fit and thus help ensure that your internal database remained secure.
Problem is if my application is already coded for a direct database connection and it would require alot of rework for it to now use services. In my case, I didn’t have the time and I wasn’t very excited about the degree of risk reworking all that code was going to introduce to my project. So I wanted something that I could implement that required only a configuration string change. Alexsey, the MSFT architect that was providing guidance on this project suggested I check out a blog post by Clemens Vasters.
That article was about using the Azure AppFabric to create a port bridge between two processes. Many of you are likely familiar with the concept of port tunneling. Heck, most of us have likely used this technique to bypass firewalls and web proxies for both legitimate and more dubious reasons. This process allows you to route traffic that would normally go through one port to a different port on another machine that in turn reroutes the connection back to the original port we wanted. This differs from an Azure AppFabric port bridge in that like other AppFabric connections, both ends of our bridge make outbound connections to the AppFabric’s relay to establish the connection.
Going out the in door
Its this outbound connection that is so powerful. Those security guys we all love to hate and slander pay close attention to what they allow to come into their networks. But they’re usually pretty forgiving about outbound connections. They assume if we’re permitted on their network, we should be allowed a bit of leeway to reach out. Even in more hardened networks, its easier to get a request to allow an out-bound connection approved then it is a request the opening of a new in-bound connection.
Its this out-bound connection that will be our to what lies behind the firewall. You see, we’re just interested in establishing a connection. Once that’s done, traffic can go bi-directionally through it. Heck, we can even multi-plex the connections that multiple requests can operate simultaneously on the same connections.
How it works
Clemens has done a great job in his blog post and its sample code of getting much of the work out of our way. You’ve got a packaged service host that will run either as a service or a console application. You’ve also got a client application (and there’s a Windows Azure worker role out there that will be posted soon as well I hope). Both of these build on some almost magical connection management logic build on top of a nettcprelaybinding. But here’s Clemens’ picture of it:
Nice picture but it needs some explanation. The bridge agent opens a specified port (in the case of this diagram, 1433 for SQL Server) and monitors for connections to that port. Each connection gets accepted and its contents handed off to the Azure AppFabric. On the other end we have the Port Bridge Service Host. This host registered our AppFabric service bus endpoint and also looks for any inbound messages. When these messages are received, it will spin up a connection to the requested resource inside the firewall (at its lowest level this is just an instance of a tcpClient object) and pass the data to it. Return messages come back in just the same manner.
Now this is where the magic starts to kick in. You’d think that with all these moving parts that the connection would be pretty slow. And admittedly, there’s a bit of overhead in getting the connection established. But like any tcp based connection, once the “pipe” has been established, the overhead of moving information through all this isn’t much greater then just routing through multiple firewalls/routers. In fact, you can look at our agents and the bus as just software based routers.
Back to the problem at hand, my SQL connection
Ok, back to the task at hand. I’ve got my Windows Azure service that needs to connect to an on-premise SQL Server. First, I need an agent.
We start by standing up a worker role that has an endpoint configured for port 1433. This role will contain our agent code. If you look at Clemens’ sample, you can see its not that complicated to strip out what you need and put it into a worker role. I also know there’s a sample worker role that’s already been created by one of the folks at MSFT that hopefully they will let get posted soon.
With our agent role in place, the next step is to modify the connection string of my application. The only part we care about is the server portion of the connection string. We need to take something that looks like Server=mysqlserver and instead make it look more like Server=tcp:<servicename>.cloudapp.net,1433. This will route the application’s SQL connection to our worker role (and yes, this means its important that you have secured your worker role so that it doesn’t allow just anyone to connect to it). Course, I’d also recommend that you use a non-standard port. This will further help obscure things. If you do, make sure that the endpoint and the port on your connection string match.
I also need to point out that there’s some magic the Windows Azure plays with our port numbers, So in the code for your agent role, don’t have it just monitor the endpoint port. The reason for this is that the Windows Azure load balancers map the configured endpoint to a different port that’s assigned to each instance. This is how they are able to route traffic between multiple instances of the same application. So when your agent role starts up, have it use the RoleEnvironment to get the appropriate endpoint port(s).
On the service host side of things, we need to configure our service by indicating what server we’re connecting too and what port to use when talking to it. Now this is where things can get dicey. Its exceedingly important that the client’s “Port Bridge Target” and the service host’s “targetHost” values match. More importantly, these values should be either the server name or IP address of the box that represents the final endpoint of our bridge. The reason its so critical that these values match is that this is also the name that will be assigned to our AppFabric service endpoint. And if they don’t match, of course things won’t match up.
Not very deep, but there it is
I wish I had more time to show you some demonstration code and I’ll add that to my list for another day. But today I’ve walked you through the basics of what we accomplished and hopefully shared some of my excitement about this with you. SQL Server is just one practical application of this approach. You could bridge simple web service connections, smtp connection… basically anything that is TCP/IP based could get connected using this same approach.
There’s just one closing piece of business. I’m a firm believer in giving credit where it is due. And as I said at the top of this post I have a debt that needs to be paid. In this case, Alexsey Savateyev and Wade Wegner of MSFT. Both these gentlemen put up with my many stupid questions and blank stares as I tried to get this working. But now that it does, you can guarantee I’ll be using it again and again. Alexsey drew the short straw and was assigned to help me on my project. But Wade, bless him, saw me struggling on twitter and stepped up to help me out a well. Both these folks have my highest praise for their patience and technical know-how.
And of course, the next logical step is porting the service host to Windows Azure…