Restrict Remote Access to Cloud VMs
Recently we are working with a project intending to restrict the RDP access of Azure role (worker role/ web role) instance to some specific IP address (a special thanks to Ugo Meex). The tricky thing we should be careful is there is a port mapping for RDP access between public port (3889) and internal port (20000) for role instances. So instead of applying firewall rule restrictions to public port 3389 we need to apply it to internal port 20000. Please find more details below.
About setting up remote access in Azure, please refer to https://msdn.microsoft.com/library/azure/hh124107.aspx/
Requirement:
Restrict RDP access to some specific IP address in Azure Role instances.
Background: How Windows Azure RemoteForwarder works?
The remote desktop feature is comprised of two imports: RemoteAccess and RemoteForwarder. The RemoteForwarder is working as follows:
- RemoteForwarder is imported on a single role and takes care of dealing with the fact that Windows Azure only provides load-balanced input endpoints.
- The forwarder runs on every instance of the role in which it is imported and listens on port 3389 for remote desktop connections.
- When a connection is received, it searches the first packet for a load balancing cookie which you can see if you open the Portal-supplied .rdp file in a text editor.
- Using this cookie data it then opens a connection internally to the desired instance and forwards all traffic to that instance.
So when we set up the RDP restrict firewall rule, instead on apply the restriction on public port 3389 we need to apply it to internal port 20000. By default the endpoint mapping between public and private for RDP access is 3889 to 20000, which you can find in the service config file located in C:/Config of your cloud VM .
Here is a default firewall initialization in a cloud service VM:
More details about Azure Remote Access service in cloud service:
Solution 1: apply firewall rule by startup task
1. In your web role project(where web.config exist), Create a file with content below and name it as SetFirewallRestrictions.ps1
Get-NetFirewallPortFilter | ? LocalPort -eq '20000' | Get-NetFirewallRule | Set-NetFirewallRule -RemoteAddress “Allowed IP Address”
Get-NetFirewallRule –DisplayName “WaRemoteForwarderService Rule” | Set-NetFirewallRule -RemoteAddress “Allowed IP Address”
2. Also in the same web role project, create a file with content below and name it as SetFirewallRestrictions.cmd
Powershell set-executionpolicy remotesigned –force
Powershell .\SetFirewallRestrictions.ps1 >> SetFirewallRestrictions.out.log 2>> SetFirewallRestrictions.err.log
3. Set copy to output property of these 2 files above as “copy always” in Visual Studio
4. Add content below in service definition file to start the above commands as startup
<WebRole name="WebRole1" vmsize="Small">
...
<Startup>
<Task commandLine="SetFirewallRestrictions.cmd" executionContext="elevated" taskType="simple" />
</Startup>
<Runtime executionContext="elevated" />
</WebRole>
</ServiceDefinition>
Reference on running startup task in Azure cloud service: https://msdn.microsoft.com/en-us/library/azure/hh180155.aspx
Tips: the IP address in file SetFirewallRestrictions.ps1 should be public IP address of the target client, but not its local IP address. How to get your client public IP address: https://whatismyipaddress.com/
Solution 2: apply firewall rule by code part in WebRole.cs
1. Add same file SetFirewallRestrictions.cmd and SetFirewallRestrictions.ps1 as above and set their property to “copy always”
2. Add content below in service definition file of your web role:
<WebRole name="WebRole1">
<Runtime executionContext="elevated"/>
<Sites>
3. Add codes below in OnStart():
// you can do some code to change the IP addresses in SetFirewallRestrictions.ps1
string cmd = string.Format("SetFirewallRestrictions.cmd ");
Process p = new Process();
p.StartInfo.FileName = @"d:\Windows\System32\cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = false;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
string Parameter = cmd;
try
{
p.Start();
p.StandardInput.WriteLine(cmd);
p.StandardInput.WriteLine("exit");
p.StandardInput.WriteLine("exit");
p.WaitForExit();
p.Close();
}
catch (Exception e)
{
}
This solution may provide more options to VM owners to grant remote access. For example, VM owners can store permitted IP addresses in a blob storage file, then before the code above, some extra code can be added to access that target blob storage file and generate a corresponding file SetFirewallRestrictions.ps1. More than that, the code above can be added into a while loop in Run() rather than OnStart(), then the while loop will run the PowerShell command for its time, then in further round, it check blob file and determine sleep or run PowerShell again, by this way, any IP change in target blog file by VM owners can be applied immediately by the code logistics. Does that fun?
Solution 3: applying ACL(Access Control List) in Azure deployment
Tutorial for using ACL in Azure PaaS project: https://blogs.msdn.com/b/walterm/archive/2014/04/22/windows-azure-paas-acls-are-here.aspx
Key Points: as with IaaS ACLs, PaaS ACLs allow you to do the following:
- Selectively permit or deny incoming traffic based on remote subnet IPv4 address range to a virtual machine input endpoint
- Blacklist IP addresses
- Create multiple rules per virtual machine endpoint
- Specify up to 50 ACL rules per virtual machine endpoint
- Use rule ordering to ensure the correct set of rules are applied on a given virtual machine endpoint (lowest to highest)
- Specify an ACL for a specific remote subnet IPv4 address
Note: the sample was done in Azure Cloud Service VMs, for Azure IaaS VMs, similar command should also work, feel free to have a try and make your cloud VMs only accessible to your limited IP clients.