Getting Started
Prerequisites
Install the DevolutionsGateway
PowerShell module:
Install-Module -Name DevolutionsGateway
Import-Module DevolutionsGateway
Configuration
Create the Devolutions Gateway configuration directory if it doesn't already exists:
PS > Import-Module DevolutionsGateway
PS > Get-DGatewayPath
C:\ProgramData\Devolutions\Gateway
PS > New-Item -Path $(Get-DGatewayPath) -ItemType 'Directory' -Force
Move into the configuration directory manually, or using the Enter-DGatewayConfig command:
PS > Enter-DGatewayConfig -ChangeDirectory
PS C:\ProgramData\Devolutions\Gateway>
The Enter-DGatewayConfig
command sets the DGATEWAY_CONFIG_PATH
environment variable that affects all commands accepting the -ConfigPath
parameter. For this guide, we will be using the standard configuration directory.
Gateway Hostname
By default, the Devolutions Gateway will use the default computer name ($Env:ComputerName
) as its hostname when responding to queries. For testing on the local network, this is probably not an issue, but for external access you will need to change it to the external DNS name you intend to use:
Set-DGatewayHostname 'jet.buzzword.marketing'
Gateway Listeners
Before launching Devolutions Gateway for the first time, it is important to configure its protocol listeners with the Set-DGatewayListeners
command:
Set-DGatewayListeners @(
$(New-DGatewayListener 'http://*:7171' 'http://*:7171'),
$(New-DGatewayListener 'tcp://*:8181' 'tcp://*:8181')
)
Run Get-DGatewayListeners
to see the list of configured listeners:
PS > Get-DGatewayListeners
InternalUrl ExternalUrl
----------- -----------
http://*:7171 http://*:7171
tcp://*:8181 tcp://*:8181
Each listener object is composed of an InternalUrl
and ExternalUrl
member. The internal URL is the local protocol and port on which to listen, the external URL is the external protocol and port used to expose the service externally.
Use *
as the "host" in both URLs to use an appropriate default value, unless it needs to be set explicitly to something else. The default host for the external URL is the configured gateway hostname.
When using Devolutions Gateway on the local network, both URLs will generally be identical, so don't bother about it too much for now, we'll reconfigure them later for proper external access.
Initial Launch
Once the initial configuration is completed, the gateway can be started with the Start-DGateway
command. Use the -Verbose
parameter to print the full docker commands and use them for reference if you wish to launch the container directly without the DevolutionsGateway
module.
PS > Start-DGateway
docker pull devolutions/devolutions-gateway:0.13.0-servercore-ltsc2019
Starting devolutions-gateway
devolutions-gateway successfully started
Make sure that the HTTP listener health check responds "200 OK":
PS > $DGatewayUrl = 'http://localhost:7171'
PS > Invoke-WebRequest "$DGatewayUrl/health"
StatusCode : 200
StatusDescription : OK
Content : {74, 101, 116, 32...}
RawContent : HTTP/1.1 200 OK
Content-Length: 59
Date: Thu, 22 Oct 2020 19:02:07 GMT
Jet instance "jet.buzzword.marketing" is alive and healthy.
Headers : {[Content-Length, 59], [Date, Thu, 22 Oct 2020 19:02:07 GMT]}
RawContentLength : 59
Certificate Configuration
Now that Devolutions Gateway was launched once with success, we can reconfigure it for proper HTTPS access. Before going any further, stop Devolutions Gateway if it is currently running:
Stop-DGateway
A valid certificate from a known authority like letsencrypt can be obtained in various ways. The Posh-ACME PowerShell module is an excellent module with plugins for most DNS providers.
For the purpose of this guide, we use a wildcard certificate for "*.buzzword.marketing" as opposed to "jet.buzzword.marketing". Using a wildcard certificate has many practical advantages, but it only becomes mandatory if you intend to deploy multiple instances of Devolutions Gateway for high availability.
$CertPath = "~\certs\!.buzzword.marketing"
Import-DGatewayCertificate -CertificateFile $(Join-Path $CertPath 'fullchain.pfx') -Password 'poshacme'
Let's reconfigure our protocol listeners, using 'https' rather than 'http' this time:
Set-DGatewayListeners @(
$(New-DGatewayListener 'https://*:7171' 'https://*:7171'),
$(New-DGatewayListener 'tcp://*:8181' 'tcp://*:8181')
)
the 'tcp' listener remains unchanged for now, but it may use the configured certificate for certain protocols. Do you recall the Set-DGatewayHostname
command? Let's confirm that the configured hostname matches the imported certificate:
PS > Get-DGatewayHostname
jet.buzzword.marketing
Our certificate valid for "*.buzzword.marketing" matches the "jet.buzzword.marketing" hostname, so there won't be a certificate name mismatch.
External Exposure
The next step is to expose Devolutions Gateway externally on that domain name. If you have an Azure VM directly exposed to the internet, add the required inbound firewall exception. In other environments, this is done with a simple port forward in your router. If you use a reverse proxy with TLS offloading, we will cover the differences later.
For this guide, I have configured an Azure VM directly exposed on the internet with a public IP. The "buzzword.marketing" domain was configured with an A record for 'jet' pointing to the Azure VM public IP.
With this in mind, let's explain a little more the internal and external URLs:
InternalUrl: 'https://*:7171' expands to 'https://0.0.0.0:7171', which means "listen locally in HTTPS on all network interfaces, on port 7171".
ExternalUrl: 'https://*:7171' expands to "https://jet.buzzword.marketing:7171" which means "accessed externally in HTTPS, on 'jet.buzzword.marketing', port 7171"
In the end, this means traffic will flow in through "https://jet.buzzword.marketing:7171" and reach "https://internal-hostname:7171" on the internal network.
Validate the configuration, making sure that everything matches, then start Devolutions Gateway again:
Start-DGateway
Cross your fingers, then try the Devolutions Gateway health check:
Invoke-WebRequest "https://$(Get-DGatewayHostname):7171/health"
StatusCode : 200
StatusDescription : OK
Content : {74, 101, 116, 32...}
RawContent : HTTP/1.1 200 OK
Content-Length: 59
Date: Thu, 22 Oct 2020 20:39:58 GMT
Jet instance "jet.buzzword.marketing" is alive and healthy.
Headers : {[Content-Length, 59], [Date, Thu, 22 Oct 2020 20:39:58 GMT]}
RawContentLength : 59
If the above command worked, then you have successfully configured Devolutions Gateway for external access with a valid certificate, congratulations! If not, review the previous steps to find what you missed.
Reverse Proxy
Instead of configuring Devolutions Gateway to handle HTTPS with a certificate, a reverse proxy can be put in front to handle and offload TLS entirely. This approach is preferred if multiple web applications need to be exposed through the same entry point. Reverse proxies like IIS (with ARR), traefik, nginx and haproxy also come with advanced configuration options and better ways to handle certificate management.
When using a wildcard certificate, it becomes easy to create an HTTPS listener bound to a specific domain and forward all traffic coming through this domain to Devolutions Gateway specifically. This technique is often used to make "virtual hosts" on the same TCP port (especially 443, the standard one) using TLS Server Name Indication (SNI).
Assuming we have configured our reverse proxy to forward traffic coming in through "https://jet.buzzword.marketing" to our internal host where Devolutions Gateway is listening in HTTP on port 7171, the new configuration should look like this:
Set-DGatewayListeners @(
$(New-DGatewayListener 'http://*:7171' 'https://*'),
$(New-DGatewayListener 'tcp://*:8181' 'tcp://*:8181')
)
Notice that in this case, the internal URL is set to 'http', because it receives traffic in HTTP from the reverse proxy. The reverse proxy handles HTTPS, offloads TLS, then forwards the traffic in HTTP to Devolutions Gateway. Because of this, the network path taken between the reverse proxy and the target host on the local network should be restricted.
You can also choose to offload TLS, yet forward traffic over HTTPS on the internal network with a second TLS connection, but this is definitely not a requirement. This type of configuration is generally more difficult to implement and comes with an additional performance cost. Before going that route, consider not doing TLS offloading in the first place, and letting Devolutions Gateway handle it instead with better performance.
Last but not least, the TCP listener also needs a corresponding port forwarding configuration. In this case, TCP/8181 on jet.buzzword.marketing needs to be forward to TCP/8181 on the internal host where Devolutions Gateway is running. Don't forget to double check that the firewall has the proper inbound TCP port exception.
Wayk Bastion
If you use Wayk Bastion, you can now reconfigure it to point to your Devolutions Gateway:
Set-WaykBastionConfig -JetExternal $true -JetRelayUrl "https://jet.buzzword.marketing:7171"
Restart-WaykBastion
Automatic Startup
If you launched Devolutions Gateway in a container, you don't need to do anything, as the default Docker restart policy is set to 'always'. This can be changed with the Set-DGatewayConfig
command:
Set-DGatewayConfig -DockerRestartPolicy "on-failure"
Unlike "always", the "on-failure" restart policy only restarts the container if it fails with a non-zero exit code.