{"id":13509673,"url":"https://github.com/bolkedebruin/rdpgw","last_synced_at":"2025-05-14T06:14:32.044Z","repository":{"id":41054044,"uuid":"276049487","full_name":"bolkedebruin/rdpgw","owner":"bolkedebruin","description":"Remote Desktop Gateway in Go for deploying on Linux/BSD/Kubernetes","archived":false,"fork":false,"pushed_at":"2025-05-06T15:39:55.000Z","size":335,"stargazers_count":894,"open_issues_count":38,"forks_count":133,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-05-06T16:51:42.429Z","etag":null,"topics":["gateway","golang-application","rdp","rdp-gateway","remote-desktop","remote-desktops","xrdp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bolkedebruin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-06-30T09:02:31.000Z","updated_at":"2025-05-06T15:38:58.000Z","dependencies_parsed_at":"2023-12-19T15:02:33.118Z","dependency_job_id":"1fe02aa3-ec54-4326-8fb6-81b1921bff3d","html_url":"https://github.com/bolkedebruin/rdpgw","commit_stats":{"total_commits":261,"total_committers":16,"mean_commits":16.3125,"dds":"0.10344827586206895","last_synced_commit":"372dc43ef215fb98bb48ff46c584366f248047ef"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolkedebruin%2Frdpgw","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolkedebruin%2Frdpgw/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolkedebruin%2Frdpgw/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bolkedebruin%2Frdpgw/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bolkedebruin","download_url":"https://codeload.github.com/bolkedebruin/rdpgw/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254083933,"owners_count":22011905,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gateway","golang-application","rdp","rdp-gateway","remote-desktop","remote-desktops","xrdp"],"created_at":"2024-08-01T02:01:11.290Z","updated_at":"2025-05-14T06:14:32.018Z","avatar_url":"https://github.com/bolkedebruin.png","language":"Go","funding_links":[],"categories":["Go","others"],"sub_categories":[],"readme":"GO Remote Desktop Gateway\n=========================\n\n![Go](https://github.com/bolkedebruin/rdpgw/workflows/Go/badge.svg)\n[![Docker Pulls](https://badgen.net/docker/pulls/bolkedebruin/rdpgw?icon=docker\u0026label=pulls)](https://hub.docker.com/r/bolkedebruin/rdpgw/)\n[![Docker Stars](https://badgen.net/docker/stars/bolkedebruin/rdpgw?icon=docker\u0026label=stars)](https://hub.docker.com/r/bolkedebruin/rdpgw/)\n[![Docker Image Size](https://badgen.net/docker/size/bolkedebruin/rdpgw?icon=docker\u0026label=image%20size)](https://hub.docker.com/r/bolkedebruin/rdpgw/)\n\n\n:star: Star us on GitHub — it helps!\n\nRDPGW 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).\nThis allows you to connect with the official Microsoft clients to remote desktops over HTTPS. \nThese desktops could be, for example, [XRDP](http://www.xrdp.org) desktops running in containers\non Kubernetes.\n\n# AIM\nRDPGW aims to provide a full open source replacement for MS Remote Desktop Gateway, \nincluding access policies.\n\n# Security requirements\n\nSeveral security requirements are stipulated by the client that is connecting to it and some are\nenforced by the gateway. The client requires that the server's TLS certificate is valid and that\nit is signed by a trusted authority. In addition, the common name in the certificate needs to\nmatch the DNS hostname of the gateway. If these requirements are not met the client will refuse\nto connect.\n\nThe gateway has several security phases. In the authentication phase the client's credentials are\nverified. Depending the authentication mechanism used, the client's credentials are verified against\nan OpenID Connect provider, Kerberos, a local PAM service or a local database.\n\nIf OpenID Connect is used the user will\nneed to connect to a webpage provided by the gateway to authenticate, which in turn will redirect\nthe user to the OpenID Connect provider. If the authentication is successful the browser will download\na RDP file with temporary credentials that allow the user to connect to the gateway by using a remote\ndesktop client.\n\nIf Kerberos is used the client will need to have a valid ticket granting ticket (TGT). The gateway\nwill proxy the TGT request to the KDC. Therefore, the gateway needs to be able to connect to the KDC\nand a krb5.conf file needs to be provided. The proxy works without the need for an RDP file and thus\nthe client can connect directly to the gateway.\n\nIf local authentication is used the client will need to provide a username and password that is verified\nagainst PAM. This requires, to ensure privilege separation, that ```rdpgw-auth``` is also running and a\nvalid PAM configuration is provided per typical configuration.\n\nIf NTLM authentication is used, the allowed user credentials for the gateway should be configured in the \nconfiguration file of `rdpgw-auth`.\n\nFinally, RDP hosts that the client wants to connect to are verified against what was provided by / allowed by\nthe server. Next to that the client's ip address needs to match the one it obtained the gateway token with if\nusing OpenID Connect. Due to proxies and NAT this is not always possible and thus can be disabled. However, this\nis a security risk.\n\n# Configuration\nThe configuration is done through a YAML file. The configuration file is read from `rdpgw.yaml` by default.\nAt the bottom of this README is an example configuration file. In these sections you will find the most important\nsettings.\n\n## Authentication\n\nRDPGW wants to be secure when you set it up from the start. It supports several authentication\nmechanisms such as OpenID Connect, Kerberos, PAM or NTLM.\n\nTechnically, cookies are encrypted and signed on the client side relying\non [Gorilla Sessions](https://www.gorillatoolkit.org/pkg/sessions). PAA tokens (gateway access tokens)\nare generated and signed according to the JWT spec by using [jwt-go](https://github.com/dgrijalva/jwt-go)\nsigned with a 256 bit HMAC. \n\n### Multi Factor Authentication (MFA)\nRDPGW provides multi-factor authentication out of the box with OpenID Connect integration. Thus\nyou can integrate your remote desktops with Keycloak, Okta, Google, Azure, Apple or Facebook\nif you want.\n\n### Mixing authentication mechanisms\n\nIt is technically possible to mix authentication mechanisms. Currently, you can mix local with Kerberos or NTLM. If you enable \nOpenID Connect it is not possible to mix it with local or Kerberos at the moment.\n\n### Open ID Connect\n![OpenID Connect](docs/images/flow-openid.svg)\n\nTo use OpenID Connect make sure you have properly configured your OpenID Connect provider, and you have a client id\nand secret. The client id and secret are used to authenticate the gateway to the OpenID Connect provider. The provider\nwill then authenticate the user and provide the gateway with a token. The gateway will then use this token to generate\na PAA token that is used to connect to the RDP host.\n\nTo enable OpenID Connect make sure to set the following variables in the configuration file.\n\n```yaml\nServer:\n  Authentication: \n    - openid\nOpenId:\n    ProviderUrl: http://\u003cprovider_url\u003e\n    ClientId: \u003cyour client id\u003e\n    ClientSecret: \u003cyour-secret\u003e\nCaps:\n  TokenAuth: true\n```\n\nAs you can see in the flow diagram when using OpenID Connect the user will use a browser to connect to the gateway first at\nhttps://your-gateway/connect. If authentication is successful the browser will download a RDP file with temporary credentials\nthat allow the user to connect to the gateway by using a remote desktop client.\n\n### Kerberos\n![Kerberos](docs/images/flow-kerberos.svg)\n\n__NOTE__: Kerberos is heavily reliant on DNS (forward and reverse). Make sure that your DNS is properly configured. \nNext to that, its errors  are not always very descriptive. It is beyond the scope of this project to provide a full \nKerberos tutorial.\n\nTo use Kerberos make sure you have a keytab and krb5.conf file. The keytab is used to authenticate the gateway to the KDC\nand the krb5.conf file is used to configure the KDC. The keytab needs to contain a valid principal for the gateway. \n\nUse `ktutil` or a similar tool provided by your Kerberos server to create a keytab file for the newly created service principal.\nPlace this keytab file in a secure location on the server and make sure that the file is only readable by the user that runs\nthe gateway.\n\n```plaintext\nktutil\naddent -password -p HTTP/rdpgw.example.com@YOUR.REALM -k 1 -e aes256-cts-hmac-sha1-96\nwkt rdpgw.keytab\n```\n\nThen set the following in the configuration file.\n\n```yaml\nServer:\n  Authentication:\n    - kerberos\nKerberos:\n    Keytab: /etc/keytabs/rdpgw.keytab\n    Krb5conf: /etc/krb5.conf\nCaps:\n  TokenAuth: false\n```\n\nThe client can then connect directly to the gateway without the need for a RDP file.\n\n\n### PAM / Local (Basic Auth)\n![PAM](docs/images/flow-pam.svg)\n\nThe gateway can also support authentication against PAM. Sometimes this is referred to as local or passwd authentication,\nbut it also supports LDAP authentication or even Active Directory if you have the correct modules installed. Typically \n(for passwd), PAM requires that it is accessed as root. Therefore, the gateway comes with a small helper program called \n`rdpgw-auth` that is used to authenticate the user. This program needs to be run as root or setuid.\n\n__NOTE__: The default windows client ``mstsc`` does not support basic auth. You will need to use a different client or\nswitch to OpenID Connect, Kerberos or NTLM authentication.\n\n__NOTE__: Using PAM for passwd (i.e. LDAP is fine) within a container is not recommended. It is better to use OpenID \nConnect or Kerberos. If you do want to use it within a container you can choose to run the helper program outside the \ncontainer and have the socket available within. Alternatively, you can mount all what is needed into the container but \nPAM is quite sensitive to the environment.\n\nEnsure you have a PAM service file for the gateway, `/etc/pam.d/rdpgw`. For authentication against local accounts on the\nhost located in `/etc/passwd` and `/etc/shadow` you can use the following.\n\n```plaintext\nauth required pam_unix.so\naccount required pam_unix.so\n```\n\nThen set the following in the configuration file.\n\n```yaml\nServer:\n  Authentication:\n    - local\nAuthSocket: /tmp/rdpgw-auth.sock\nCaps:\n  TokenAuth: false\n```\n\nMake sure to run both the gateway and `rdpgw-auth`. The gateway will connect to the socket to authenticate the user.\n\n```bash\n# ./rdpgw-auth -n rdpgw -s /tmp/rdpgw-auth.sock\n```\n\nThe client can then connect to the gateway directly by using a remote desktop client.\n\n### NTLM\n\nThe gateway can also support NTLM authentication. \nCurrently, only the configuration file is supported as a database for credential lookup. \nIn the future, support for real databases (e.g. sqlite) may be added.\n\nNTLM authentication has the advantage that it is easy to setup, especially in case the gateway is used for a limited number of users.\nUnlike PAM / local, NTLM authentication supports the default windows client ``mstsc``.\n\n__WARNING__: The password is currently saved in plain text. So, you should keep the config file as secure as possible and avoid \nreusing the same password for other applications. The password is stored in plain text to support the NTLM authentication protocol.\n\nTo enable NTLM authentication make sure to set the following variables in the configuration file.\n\nConfiguration file for `rdpgw`: \n```yaml\nServer:\n  Authentication:\n    - ntlm\nCaps:\n  TokenAuth: false\n```\n\nConfiguration file for `rdpgw-auth`:\n````yaml\nUsers:\n - {Username: \"my_username\", Password: \"my_secure_password\"} # Modify this password!\n````\n\nThe client can then connect to the gateway directly by using a remote desktop client using the gateway credentials \nconfigured in the YAML configuration file.\n\n## TLS\n\nThe gateway requires a valid TLS certificate. This means a certificate that is signed by a valid CA that is in the store \nof your clients. If this is not the case particularly Windows clients will fail to connect. You can either provide a \ncertificate and key file or let the gateway obtain a certificate from letsencrypt. If you want to use letsencrypt make \nsure that the host is reachable on port 80 from the letsencrypt servers.\n\nFor letsencrypt:\n\n```yaml\nTls: auto\n```\n\nfor your own certificate:\n```yaml\nTls: enable\nCertFile: server.pem \nKeyFile: key.pem\n```\n\n__NOTE__: You can disable TLS on the gateway, but you will then need to make sure a proxy is run in front of it that does\nTLS termination. \n\n\n## Example configuration file for Open ID Connect\n\n```yaml\n# web server configuration. \nServer:\n # can be set to openid, kerberos, local and ntlm. If openid is used rdpgw expects\n # a configured openid provider, make sure to set caps.tokenauth to true. If local\n # rdpgw connects to rdpgw-auth over a socket to verify users and password. Note:\n # rdpgw-auth needs to be run as root or setuid in order to work. If kerberos is\n # used a keytab and krb5conf need to be supplied. local can be stacked with \n # kerberos or ntlm authentication, so that the clients selects what it wants.\n Authentication:\n  # - kerberos\n  # - local\n  - openid\n  # - ntlm\n # The socket to connect to if using local auth. Ensure rdpgw auth is configured to\n # use the same socket.\n # AuthSocket: /tmp/rdpgw-auth.sock\n # Basic auth timeout (in seconds). Useful if you're planning on waiting for MFA\n BasicAuthTimeout: 5\n # The default option 'auto' uses a certificate file if provided and found otherwise\n # it uses letsencrypt to obtain a certificate, the latter requires that the host is reachable\n # from letsencrypt servers. If TLS termination happens somewhere else (e.g. a load balancer)\n # set this option to 'disable'. This is mutually exclusive with 'authentication: local'\n # Note: rdp connections over a gateway require TLS\n Tls: auto\n # gateway address advertised in the rdp files and browser\n GatewayAddress: localhost\n # port to listen on (change to 80 or equivalent if not using TLS)\n Port: 443\n # list of acceptable desktop hosts to connect to\n Hosts:\n  - localhost:3389\n  - my-{{ preferred_username }}-host:3389\n # if true the server randomly selects a host to connect to\n # valid options are: \n #  - roundrobin, which selects a random host from the list (default)\n #  - signed, a listed host specified in the signed query parameter\n #  - unsigned, a listed host specified in the query parameter\n #  - any, insecurely allow any host specified in the query parameter\n HostSelection: roundrobin \n # a random strings of at least 32 characters to secure cookies on the client\n # make sure to share this across the different pods\n SessionKey: thisisasessionkeyreplacethisjetzt\n SessionEncryptionKey: thisisasessionkeyreplacethisnunu!\n  # where to store session details. This can be either file or cookie (default: cookie)\n  # if a file store is chosen, it is required to have clients 'keep state' to the rdpgw\n  # instance they are connected to.\n SessionStore: cookie\n  # tries to set the receive / send buffer of the connections to the client\n # in case of high latency high bandwidth the defaults set by the OS might\n # be to low for a good experience\n # ReceiveBuf: 12582912\n # SendBuf: 12582912 \n# Open ID Connect specific settings\nOpenId:\n ProviderUrl: http://keycloak/auth/realms/test\n ClientId: rdpgw\n ClientSecret: your-secret\n# Kerberos:\n#  Keytab: /etc/keytabs/rdpgw.keytab\n#  Krb5conf: /etc/krb5.conf\n#  enabled / disabled capabilities\nCaps:\n SmartCardAuth: false\n # required for openid connect\n TokenAuth: true\n # connection timeout in minutes, 0 is limitless\n IdleTimeout: 10\n EnablePrinter: true\n EnablePort: true\n EnablePnp: true\n EnableDrive: true\n EnableClipboard: true\nClient:\n  # template rdp file to use for clients\n  # rdp file settings and their defaults see here: \n  # https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/rdp-files\n  defaults: /etc/rdpgw/default.rdp\n  # this is a go string templated with {{ username }} and {{ token }}\n  # the example below uses the ASCII field separator to distinguish\n  # between user and token \n  UsernameTemplate: \"{{ username }}@bla.com\\x1f{{ token }}\"\n  # If true puts splits \"user@domain.com\" into the user and domain component so that\n  # domain gets set in the rdp file and the domain name is stripped from the username\n  SplitUserDomain: false\n  # If true, removes \"username\" (and \"domain\" if SplitUserDomain is true) from RDP file.\n  # NoUsername: true\nSecurity:\n  # a random string of 32 characters to secure cookies on the client\n  # make sure to share this amongst different pods\n  PAATokenSigningKey: thisisasessionkeyreplacethisjetzt\n  # PAATokenEncryptionKey: thisisasessionkeyreplacethisjetzt\n  # a random string of 32 characters to secure cookies on the client\n  UserTokenEncryptionKey: thisisasessionkeyreplacethisjetzt\n  # Signing makes the token bigger and we are limited to 511 characters\n  # UserTokenSigningKey: thisisasessionkeyreplacethisjetzt\n  # if you want to enable token generation for the user\n  # if true the username will be set to a jwt with the username embedded into it\n  EnableUserToken: true\n  # Verifies if the ip used to connect to download the rdp file equals from where the\n  # connection is opened.\n  VerifyClientIp: true\n```\n\n## How to build \u0026 install\n\n__NOTE__: a [docker image](https://hub.docker.com/r/bolkedebruin/rdpgw/) is available on docker hub, which removes the need for building and installing go.\n\nEnsure 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.\n\nThen clone the repo and issues the following.\n\n```bash\ncd rdpgw\nmake\nmake install\n```\n\n## Testing locally\nA convenience docker-compose allows you to test the RDPGW locally. It uses [Keycloak](http://www.keycloak.org) \nand [xrdp](http://www.xrdp.org) and exposes it services on port 9443. You will need to allow your browser\nto connect to localhost with and self signed security certificate. For chrome set `chrome://flags/#allow-insecure-localhost`.\nThe username to login to both Keycloak and xrdp is `admin` as is the password.\n\n__NOTE__: The redirecting relies on DNS. Make sure to add ``127.0.0.1\tkeycloak`` to your `/etc/hosts` file to ensure\nthat the redirect works.\n\n__NOTE__: The local testing environment uses a self signed certificate. This works for MAC clients, but not for Windows.\nIf you want to test it on Windows you will need to provide a valid certificate.\n\n```bash\n# with open id\ncd dev/docker\ndocker-compose -f docker-compose.yml up\n\n# or for arm64 with open id\ndocker-compose -f docker-compose-arm64.yml up\n\n# or for local or pam\ndocker-compose -f docker-compose-local.yml up\n```\n    \nYou can then connect to the gateway at `https://localhost:9443/connect` for the OpenID connect flavors which will start \nthe authentication flow. Or you can connect directly with the gateway set and the host set to ``xrdp`` if using the ``local`` \nflavor. You can login with 'admin/admin'. The RDP file will download and you can open it with a remote \ndesktop client. Also for logging in 'admin/admin' will work.\n\n## Use\nPoint your browser to `https://your-gateway/connect`. After authentication\nand RDP file will download to your desktop. This file can be opened by one\nof the remote desktop clients and it will try to connect to the gateway and\ndesktop host behind it.\n\n## Integration\nThe gateway exposes an endpoint for the verification of user tokens at\nhttps://yourserver/tokeninfo . The query parameter is 'access_token' so\nyou can just do a GET to https://yourserver/tokeninfo?access_token=\u003ctoken\u003e .\nIt will return 200 OK with the decrypted token.\n\nIn this way you can integrate, for example, it with [pam-jwt](https://github.com/bolkedebruin/pam-jwt).\n\n## Client Caveats\nThe several clients that Microsoft provides come with their own caveats. \nThe most important one is that the default client on Windows ``mstsc`` does \nnot support basic authentication. This means you need to use either OpenID Connect,\nKerberos or ntlm authentication.\n\nIn addition to that, ``mstsc``, when configuring a gateway directly in the client requires\nyou to either:\n * \"save the credentials\" for the gateway\n * or specify a (random) domain name in the username field (e.g. ``.\\username``) when prompted for the gateway credentials,\n \notherwise the client will not connect at all (it won't send any packages to the gateway) and it will keep on asking for new credentials.\n\nFinally, ``mstsc`` requires a valid certificate on the gateway.\n\nThe Microsoft Remote Desktop Client from the Microsoft Store does not have these issues,\nbut it requires that the username and password used for authentication are the same for\nboth the gateway and the RDP host.\n\nThe Microsoft Remote Desktop Client for Mac does not have these issues and is the most flexible.\nIt supports basic authentication, OpenID Connect and Kerberos and can use different credentials\n\nThe official Microsoft IOS and Android clients seem also more flexible.\n\nThird party clients like [FreeRDP](https://www.freerdp.com) might also provide more\nflexibility.\n\n## TODO\n* Improve Web Interface\n\n# Acknowledgements\n* This product includes software developed by the Thomson Reuters Global Resources. ([go-ntlm](https://github.com/m7913d/go-ntlm) - BSD-4 License)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbolkedebruin%2Frdpgw","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbolkedebruin%2Frdpgw","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbolkedebruin%2Frdpgw/lists"}