Windows Remote Desktop Protocol supports TLS security, which is one defensive layer to consider when hardening your Windows Server. This guide explains how to install a free Let's Encrypt TLS certificate, and configure it for Windows Remote Desktop.
This guide assumes you do not have a web server running on port 80.
Add a DNS A record for your Windows Server instance. This tutorial uses myserver.example.com.
To manage the TLS certificates, you'll need to install several software packages. Several of these installation steps modify your path and environment variables, and you'll need to log out and back in to the server a few times during this process.
Open an administrative PowerShell.
Click Start, type PowerShell, right-click Windows PowerShell, and then click Run as administrator.
Install the Chocolatey Package Manager, which automates many of the following installation steps.
> Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Log out, and log back in to the server to set the new environment.
Install OpenSSL and Python3 with Chocolatey
> choco install openssl python3 -y
Log out, and log back in to the server to set the new environment.
Install Certbot with pip:
> pip install certbot
Log out, and log back in to the server to set the new environment.
Let's Encrypt needs to perform a handshake on port 80 to verify your domain name. With PowerShell, create a rule to allow inbound traffic on port 80.
> New-NetFirewallRule -DisplayName "Let's Encrypt (HTTP-In)" -Direction inbound -LocalPort 80 -Protocol TCP -Action Allow
If you use the Vultr Firewall, verify port 80 is open there as well.
Request a certificate with Certbot. Replace myserver.example.com
with your domain name.
> certbot certonly -d myserver.example.com
You'll be prompted to either spin up a temporary webserver or place files in the webroot. Enter option 1 to use a temporary webserver.
When prompted, enter your email address.
Agree to the terms of service.
When finished, your Let's Encrypt keys are stored in C:\Certbot\live\myserver.example.com
You have created a .pem format certificate, but Windows requires .pfx format.
Convert the certificate with OpenSSL.
Choose a strong password to replace YOUR_PASSWORD
Replace myserver.example.com
with your domain name
> openssl pkcs12 -export -out C:\Certbot\keys\winrdp.pfx -inkey C:\Certbot\live\myserver.example.com\privkey.pem -in C:\Certbot\live\myserver.example.com\cert.pem -certfile C:\Certbot\live\myserver.example.com\chain.pem -password pass:YOUR_PASSWORD
Import your certificate. Replace YOUR_PASSWORD
with the strong password you chose earlier.
> certutil -p YOUR_PASSWORD -importPFX C:\Certbot\keys\winrdp.pfx noExport
Run these commands to make Remote Desktop use the certificate.
Retreive the certificate thumbprint and set it to the variable $thumbprint
.
> $thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\my | WHERE {$_.Subject -match "myserver.example.com" } | Sort-Object -Descending NotBefore | Select -First 1).Thumbprint
Apply the certificate thumbprint to RDP.
> wmic /namespace:\\root\CIMV2\TerminalServices PATH Win32_TSGeneralSetting Set SSLCertificateSHA1Hash="$thumbprint"
Connect to your instance with RDP using the instance IP address. You should receive a certificate validation warning.
Next, connect to the instance using the domain name instead of the IP address. You should connect without certificate security warnings.
This demonstrates your certificate is working properly.
Let's Encrypt certificates expires after three months. To keep your certificates valid, create a scheduled task to renew them automatically.
Create a new PowerShell script named C:\Certbot\renew-certificates.ps1
.
> notepad C:\Certbot\renew-certificates.ps1
Paste the following into the PowerShell script.
Make sure to change myserver.example.com
to your domain name, and use the strong password you chose earlier.
certbot renew
openssl pkcs12 -export -out C:\Certbot\keys\winrdp.pfx -inkey C:\Certbot\live\myserver.example.com\privkey.pem -in C:\Certbot\live\myserver.example.com\cert.pem -certfile C:\Certbot\live\myserver.example.com\chain.pem -password pass:YOUR_PASSWORD
certutil -p YOUR_PASSWORD -importPFX C:\Certbot\keys\winrdp.pfx noExport
$thumbprint = (Get-ChildItem -Path Cert:\LocalMachine\my | WHERE {$_.Subject -match "myserver.example.com" } | Sort-Object -Descending NotBefore | Select -First 1).Thumbprint
wmic /namespace:\\root\CIMV2\TerminalServices PATH Win32_TSGeneralSetting Set SSLCertificateSHA1Hash="$thumbprint"
Create the scheduled task to check and renew certificates every Sunday at 1 a.m. every four weeks.
> $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden C:\Certbot\renew-certificates.ps1"
> $trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -WeeksInterval 4 -At 1am
> Register-ScheduledTask "Renew Certificates" -Action $action -Trigger $trigger -User "System"