Newer
Older

[](https://hub.docker.com/r/bolkedebruin/rdpgw/)
[](https://hub.docker.com/r/bolkedebruin/rdpgw/)
[](https://hub.docker.com/r/bolkedebruin/rdpgw/)
:star: Star us on GitHub — it helps!
RDPGW is an implementation of the [Remote Desktop Gateway protocol](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsgu/0007d661-a86d-4e8f-89f7-7f77f8824188).
This allows you to connect with the official Microsoft clients to remote desktops over HTTPS.
These desktops could be, for example, [XRDP](http://www.xrdp.org) desktops running in containers
on Kubernetes.
## AIM
RDPGW aims to provide a full open source replacement for MS Remote Desktop Gateway,
including access policies.
RDPGW wants to be secure when you set it up from the start. It supports several authentication
mechanisms such as OpenID Connect, Kerberos and PAM.
Technically, cookies are encrypted and signed on the client side relying
on [Gorilla Sessions](https://www.gorillatoolkit.org/pkg/sessions). PAA tokens (gateway access tokens)
are generated and signed according to the JWT spec by using [jwt-go](https://github.com/dgrijalva/jwt-go)
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
signed with a 256 bit HMAC.
### Multi Factor Authentication (MFA)
RDPGW provides multi-factor authentication out of the box with OpenID Connect integration. Thus
you can integrate your remote desktops with Keycloak, Okta, Google, Azure, Apple or Facebook
if you want.
### Security requirements
Several security requirements are stipulated by the client that is connecting to it and some are
enforced by the gateway. The client requires that the server's TLS certificate is valid and that
it is signed by a trusted authority. In addition, the common name in the certificate needs to
match the DNS hostname of the gateway. If these requirements are not met the client will refuse
to connect.
The gateway has several security phases. In the authentication phase the client's credentials are
verified. Depending the authentication mechanism used, the client's credentials are verified against
an OpenID Connect provider, Kerberos or a local PAM service.
If OpenID Connect is used the user will
need to connect to a webpage provided by the gateway to authenticate, which in turn will redirect
the user to the OpenID Connect provider. If the authentication is successful the browser will download
a RDP file with temporary credentials that allow the user to connect to the gateway by using a remote
desktop client.
If Kerberos is used the client will need to have a valid ticket granting ticket (TGT). The gateway
will proxy the TGT request to the KDC. Therefore, the gateway needs to be able to connect to the KDC
and a krb5.conf file needs to be provided. The proxy works without the need for an RDP file and thus
the client can connect directly to the gateway.
If local authentication is used the client will need to provide a username and password that is verified
against PAM. This requires, to ensure privilege separation, that ```rdpgw-auth``` is also running and a
valid PAM configuration is provided per typical configuration.
Finally, RDP hosts that the client wants to connect to are verified against what was provided by / allowed by
the server. Next to that the client's ip address needs to match the one it obtained the gateway token with if
using OpenID Connect. Due to proxies and NAT this is not always possible and thus can be disabled. However, this
is a security risk.
### Mixing authentication mechanisms
RDPGW allows you to mix authentication mechanisms in case functionally possible. PAM and Kerberos can be used
together, but OpenID Connect can only be used by itself.
__NOTE__: a docker image is available on docker hub, which removes the need for building and installing go.
Ensure that you have `make` (comes with standard build tools, like `build-essential` on Debian), `go` (version 1.19 or above), and development files for PAM (`libpam0g-dev` on Debian) installed.
```
## Configuration
By default the configuration is read from `rdpgw.yaml`. Below is a
template.
```yaml
# web server configuration.
# can be set to openid, kerberos and local. If openid is used rdpgw expects
# a configured openid provider, make sure to set caps.tokenauth to true. If local
# rdpgw connects to rdpgw-auth over a socket to verify users and password. Note:
# rdpgw-auth needs to be run as root or setuid in order to work. If kerberos is
# used a keytab and krb5conf need to be supplied. local and kerberos authentication
# can be stacked, so that the clients selects what it wants.
Authentication:
- openid
# The socket to connect to if using local auth. Ensure rdpgw auth is configured to
# use the same socket.
AuthSocket: /tmp/rdpgw-auth.sock
# Basic auth timeout (in seconds). Useful if you're planning on waiting for MFA
BasicAuthTimeout: 5
# The default option 'auto' uses a certificate file if provided and found otherwise
# it uses letsencrypt to obtain a certificate, the latter requires that the host is reachable
# from letsencrypt servers. If TLS termination happens somewhere else (e.g. a load balancer)
# set this option to 'disable'. This is mutually exclusive with 'authentication: local'
# Note: rdp connections over a gateway require TLS
CertFile: server.pem
KeyFile: key.pem
# gateway address advertised in the rdp files and browser
# port to listen on (change to 80 or equivalent if not using TLS)
# list of acceptable desktop hosts to connect to
# if true the server randomly selects a host to connect to
# valid options are:
# - roundrobin, which selects a random host from the list (default)
# - signed, a listed host specified in the signed query parameter
# - unsigned, a listed host specified in the query parameter
# - any, insecurely allow any host specified in the query parameter
HostSelection: roundrobin
# a random strings of at least 32 characters to secure cookies on the client
# make sure to share this across the different pods
SessionKey: thisisasessionkeyreplacethisjetzt
SessionEncryptionKey: thisisasessionkeyreplacethisnunu!
# where to store session details. This can be either file or cookie (default: cookie)
# if a file store is chosen, it is required to have clients 'keep state' to the rdpgw
# instance they are connected to.
# tries to set the receive / send buffer of the connections to the client
# in case of high latency high bandwidth the defaults set by the OS might
# be to low for a good experience
# ReceiveBuf: 12582912
# SendBuf: 12582912
OpenId:
ProviderUrl: http://keycloak/auth/realms/test
ClientId: rdpgw
ClientSecret: your-secret
Kerberos:
Keytab: /etc/keytabs/rdpgw.keytab
Krb5conf: /etc/krb5.conf
Caps:
SmartCardAuth: false
TokenAuth: true
IdleTimeout: 10
EnablePrinter: true
EnablePort: true
EnablePnp: true
EnableDrive: true
EnableClipboard: true
Client:
# this is a go string templated with {{ username }} and {{ token }}
# the example below uses the ASCII field separator to distinguish
# between user and token
UsernameTemplate: "{{ username }}@bla.com\x1f{{ token }}"
# rdp file settings see:
# https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/rdp-files
NetworkAutoDetect: 0
BandwidthAutoDetect: 1
ConnectionType: 6
Bolke de Bruin
committed
# If true puts splits "user@domain.com" into the user and domain component so that
# domain gets set in the rdp file and the domain name is stripped from the username
SplitUserDomain: false
# If true, removes "username" (and "domain" if SplitUserDomain is true) from RDP file.
# NoUsername: true
# a random string of 32 characters to secure cookies on the client
# make sure to share this amongst different pods
PAATokenSigningKey: thisisasessionkeyreplacethisjetzt
# PAATokenEncryptionKey: thisisasessionkeyreplacethisjetzt
# a random string of 32 characters to secure cookies on the client
UserTokenEncryptionKey: thisisasessionkeyreplacethisjetzt
# if you want to enable token generation for the user
# if true the username will be set to a jwt with the username embedded into it
EnableUserToken: true
# Verifies if the ip used to connect to download the rdp file equals from where the
# connection is opened.
VerifyClientIp: true
## Testing locally
A convenience docker-compose allows you to test the RDPGW locally. It uses [Keycloak](http://www.keycloak.org)
and [xrdp](http://www.xrdp.org) and exposes it services on port 443. You will need to allow your browser
to connect to localhost with and self signed security certificate. For chrome set `chrome://flags/#allow-insecure-localhost`.
The username to login to both Keycloak and xrdp is `admin` as is the password.
```bash
cd dev/docker
docker-compose build
docker-compose up
```
## Use
Point your browser to `https://your-gateway/connect`. After authentication
and RDP file will download to your desktop. This file can be opened by one
of the remote desktop clients and it will try to connect to the gateway and
desktop host behind it.
## Integration
The gateway exposes an endpoint for the verification of user tokens at
https://yourserver/tokeninfo . The query parameter is 'access_token' so
you can just do a GET to https://yourserver/tokeninfo?access_token=<token> .
It will return 200 OK with the decrypted token.
In this way you can integrate, for example, it with [pam-jwt](https://github.com/bolkedebruin/pam-jwt).
## TODO
* Integrate Open Policy Agent
* Integrate uber-go/zap
* Research: TLS defragmentation