Archive for December, 2010

Configuring RDP access to Windows Azure VMs

No Comments »

As part of the launch of Windows Azure SDK 1.3 Microsoft has provided us with the possibility of connecting to our Windows Azure VMs using Remote Desktop.

Using Windows Azure Tools for Visual Studio 2010 it is straight forward to configure your application to allow remote desktop access to the VMs:

  • Right-click the service in VS2010 and choose “Publish”
  • Click the link “Configure Remote Desktop Connections…”
  • Check “Enable connections for all roles” and choose or create a certificate to encrypt the password
  • Enter username, password and the user’s expiry date and click “OK”.
  • Perform the rest of the deployment as usual (make sure to upload the certificate, if you chose to create a new one)

If all you want is remote desktop access to your Azure instances this will do. If you want to understand what is going on and how to manually configure your application for remote desktop access then read on.

Manually configuring the application for remote desktop access is a two-part process:

  • The application must be configured to allow RDP connections on port 3389
  • You need to create an encrypted password and enable the Fabric Controller to access the certificate used for the encryption.

Configuring the application

In order for the Windows Azure load balancer to allow inbound RDP-connections, you have to enable this in your service definition. Open the ServiceDefinition.csdef file and locate the WebRole-/WorkerRole sections describing the roles for which you want to allow remote desktop access. Import the module “RemoteAccess” in each section:

<Import moduleName="RemoteAccess" />

 

Because of this import the Fabric Controller will set up internal TCP endpoints on port 3389 for each role.

In addition to these internal endpoints, exactly one role needs to define an input endpoint to which the load balancer can route inbound RDP connections. When you access an instance via RDP, this role will forward the connection to the instance to which you intend to connect.

Thus, you have to add the following to exactly one of the sections in ServiceDefinition.csdef:

<Import moduleName="RemoteForwarder"/>

 

Assuming that we started out with a service containing one web role and one worker role, our service definition now looks like this:

<?xml version="1.0" encoding="utf-8"?>

<ServiceDefinition name="RDService" xmlns="http://schemas.microsoft.com/

ServiceHosting/2008/10/ServiceDefinition">

  <WebRole name="WebRole1">

    <Sites>

      <Site name="Web">

        <Bindings>

          <Binding name="Endpoint1" endpointName="Endpoint1" />

        </Bindings>

      </Site>

    </Sites>

  <Endpoints>

      <InputEndpoint name="Endpoint1" protocol="http" port="80" />

    </Endpoints>

    <Imports>

      <Import moduleName="RemoteAccess"/>

      <Import moduleName="Diagnostics" />

    </Imports>

  </WebRole>

  <WorkerRole name="WorkerRole1">

    <Imports>

      <Import moduleName="RemoteAccess"/>

      <Import moduleName="RemoteForwarder"/>

      <Import moduleName="Diagnostics" />

    </Imports>

  </WorkerRole>

</ServiceDefinition>

If you have used VS2010 to edit ServiceDefinition.csdef you will see that a number of settings have been added to each role in the service configuration file, ServiceConfiguration.cscfg (if you haven’t used VS2010 you will have to add them yourself):

<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="" />
<Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="" />

Moreover, for the role acting as Remote Forwarder VS2010 has added:

<Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="" />

 

and, finally, a new element has been added to the Certificates section:

<Certificates>
   <Certificate name="Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption"
thumbprint="" thumbprintAlgorithm="sha1" />
</Certificates>

The first four settings are obviously there to allow the Fabric Controller’en to configure each VM when the application is deployed. At this point, we can fill in all these fields with the exception of the AccountEncryptedPassword field. In my case, I enter “true”, “rune” and “2010-11-06 00:00:00Z” (see ‘Setting Up A Remote Desktop Connection For A Role’ for information on the date/time format). Moreover, for WorkerRole1  I put “true” for the “Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled” field.

Specifying the password

We need to choose a password for our remote desktop user, and for security reasons it needs to be encrypted before we enter it into ServiceConfiguration.cscfg. Thus, we need to perform the following tasks:

  • Create a X509 certificate
  • Encrypt a password using this the certificate 
  • Put the encrypted password in ServiceConfiguration.cscfg
  • Provide the certificate to the Fabric Controller
  • Specify the certificate’s unique name (thumbprint) in ServiceConfiguration.cscsf

To create a X509 certificate run a Visual Studio Command Prompt  as administrator and execute the following command:

makecert –sky exchange -r -n "CN=AzureRD" -pe -a sha1 -len 2048 -ss My "AzureRD.cer"

(you can run makecert –! to see a descriptions of the flags used and read general guidelines for creating a certificate here).

This will create a certificate and store it in the file AzureRD.cer as well as install it into the local certificate store. You can verify this using PowerShell:

PS C:\Users\Rune Ibsen> cd cert:\CurrentUser\My

PS cert:\CurrentUser\My> dir

    Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint Subject
———- ——-
FF89E4AEFF26E891CAD29F9C59F6E5F9050B2337 Windows Azure Tools
55825F4612F9EDCAB073DE98AA5419FA710A2973 AzureRD

Notice that this listing contains the certificates’ thumbprints. You will need your certificate’s thumbprint shortly.

Next, we need to actually use the certificate to encrypt the password. Fire up PowerShell and run the following commands:

[Reflection.Assembly]::LoadWithPartialName("System.Security")
$pass = [Text.Encoding]::UTF8.GetBytes("yourpassword")
$content = new-object Security.Cryptography.Pkcs.ContentInfo –argumentList (,$pass)
$env = new-object Security.Cryptography.Pkcs.EnvelopedCms $content
$env.Encrypt((new-object System.Security.Cryptography.Pkcs.CmsRecipient(gi cert:\CurrentUser\My\55825F4612F9EDCAB073DE98AA5419FA710A2973)))
[Convert]::ToBase64String($env.Encode())

The encoded string resulting from the last command is what you need to put in the fields AccountEncryptedPassword in ServiceConfiguration.cscfg. Moreover, put the certificate’s thumbprint as the value for the thumbprint attribute in ServiceConfiguration.cscfg.

In order for the Fabric Controller to configure the virtual machines with the chosen password, the Fabric Controller needs to have access to the certificate that was used to encrypt the password. In particular, you will have to provide a personal information exchange (PFX) certificate.

To create a PFX certificate go to a command prompt on your local machine and run

certmgr.msc

This will bring up the certificate manager. Locate the newly created certificate, right-click it and choose “All Tasks –> Export…”. Follow the wizard, indicating along the way that you want to export the private key. Once complete, this process will create a .pfx file.

Now go to the Windows Azure portal and create a new hosted service. Then select the Certificates-folder and click the Add Certificate in the upper left corner. Upload the .pfx file you just created.

Next, deploy the application to the hosted service just created and, once deployment is complete, connect to a VM using RDP. Presto! You see the familiar Windows 2008 desktop!

I think it is worth noticing that you can enable and disable remote desktop access on a per role basis at runtime, that is, without having to do a redeploy.

Also notice that if you enumerate the instance endpoints for your roles, you will see that they now have an instance endpoint named “Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp”. I did this by connecting to an instance via RDP and enumerating the endpoints using Powershell:

image

If you inspect the endpoints closer, you will see that they are listening on port 3389.

References