rdpgw: a Remote Desktop Gateway in Go, for XRDP desktops on Kubernetes
Remote Desktop Gateway in Go for deploying on Linux/BSD/Kubernetes
At a glance
- What is it?
- RDPGW implements the Remote Desktop Gateway protocol so the official Microsoft clients can reach remote desktops over HTTPS, with OpenID Connect, Kerberos, PAM, NTLM and header-based authentication, and it ships as two binaries, the gateway and a privileged auth helper.
- Who is it for?
- RDPGW fits a platform team that runs XRDP desktops in containers and wants the official Microsoft remote desktop clients to reach them over HTTPS without deploying the Windows Remote Desktop Gateway service.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 50 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the protocol gives you and what it asks of you
RDPGW is an implementation of the Remote Desktop Gateway protocol, and what it buys is that the official Microsoft clients can connect to remote desktops over HTTPS. The desktops in question could be, for example, XRDP desktops running in containers on Kubernetes.
The stated aim is to provide a full open source replacement for the Microsoft Remote Desktop Gateway, including access policies. That last phrase is the important one. A gateway is not a proxy, it is a policy enforcement point, and replacing the Microsoft product means replacing the part that decides who may reach which host.
The security requirements are stipulated by the client that connects, and some are enforced by the gateway. The client requires that the server's TLS certificate is valid and signed by a trusted authority, and the common name in the certificate has to match the DNS hostname of the gateway. If those requirements are not met, the client will refuse to connect.
That last constraint has a practical consequence that catches people out. The gateway is not an internal service you can reach by IP address; the client checks the hostname against the certificate, so you need a real name and a real certificate for it. The TLS section of the documentation allows either supplying a certificate and key or letting the gateway obtain one from Let's Encrypt, and if you use Let's Encrypt the host has to be reachable on port 80 from the Let's Encrypt servers.
You can disable TLS on the gateway, but then you have to run a proxy in front of it that does TLS termination.
The project is Apache-2.0 licensed, written in Go, and the last push was on 2026-09-02. The releases are v2.2.0, Eager Eland, from 2025-09-18, v2.1.0, Dunking Donuts, from 2025-09-05 and v2.0.2 from 2024-03-30. The naming convention of the release tags is a small sign of how the project is run.
Five authentication mechanisms, and the ones that can be mixed
The gateway has several security phases. In the authentication phase the client's credentials are verified, and depending on the mechanism used, they are verified against an OpenID Connect provider, Kerberos, a local PAM service, a local database, or extracted from HTTP headers provided by upstream proxy services.
Each of those has a different shape.
With OpenID Connect, the user connects to a webpage provided by the gateway, which redirects to the provider, and on success the browser downloads an RDP file with temporary credentials that let the user connect to the gateway with a remote desktop client. With Kerberos, the client needs a valid ticket granting ticket, the gateway proxies the TGT request to the KDC, so the gateway must be able to reach the KDC and a krb5.conf file must be provided. The proxy works without the need for an RDP file, so the client can connect directly to the gateway. With local authentication the client provides a username and password verified against PAM, and to ensure privilege separation the rdpgw-auth binary also has to be running with a valid PAM configuration. With NTLM, the allowed user credentials for the gateway are configured in the configuration file of rdpgw-auth.
Mixing is possible but restricted. Technically it is possible to mix authentication mechanisms, and currently you can mix local with Kerberos or NTLM, but if you enable OpenID Connect it is not possible to mix it with local or Kerberos at the moment.
Multi-factor authentication comes out of the box with the OpenID Connect integration, which means you can integrate your remote desktops with Keycloak, Okta, Google, Azure, Apple or Facebook.
The header-based option is the one for teams already running a proxy that authenticates upstream, with Azure App Proxy, Google IAP and AWS ALB named as the services it integrates with, where the proxy handles authentication and passes the user identity in HTTP headers.
How the token and the session are protected
The cryptographic details are short and worth knowing, because they explain the shape of the trust model.
Technically, cookies are encrypted and signed on the client side, relying on Gorilla Sessions. PAA tokens, the gateway access tokens, are generated and signed according to the JWT spec by using jwt-go, signed with a 256 bit HMAC.
So the cookie carries the session state and the gateway access token carries the authorisation decision, and both are signed with a key the gateway holds. That is the piece that makes the multi-host policy model work, since the client presents a signed token that carries the authorisation decision.
And the policy check has one more layer. RDP hosts that the client wants to connect to are verified against what was provided by or 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.
That sentence is the one to notice. The IP binding is a defence against a stolen token being replayed from a different address, which is a real protection against a token that leaks out of a browser download. The documentation tells you plainly that turning it off is a security risk, and that it may need to be turned off anyway because NAT and proxies routinely change the address between the token issue and the RDP connection. An operator has to make that trade deliberately.
The Go dependency list backs up the picture. There is a Kerberos library under bolkedebruin/gokrb5, an OpenID Connect client at coreos/go-oidc, a JWT library at go-jose/go-jose, an NTLM implementation, a PAM binding at msteinert/pam, and gorillatoolkit components for mux, sessions and websockets. The configuration is parsed with koanf, so YAML, environment variables and files can all feed the same config, and Prometheus client_golang is there for metrics.
The configuration file, and the two binaries
Configuration is a YAML file, read from rdpgw.yaml by default, and the documentation walks through the sections that matter.
For TLS there are two shapes. To let the gateway obtain a certificate itself:
Tls: autoOr to supply your own:
Tls: enable
CertFile: server.pem
KeyFile: key.pemThe example configuration file for OpenID Connect shows the server section, and the comment there is the one that tells you the deployment constraints:
# web server configuration.
Server:
# can be set to openid, kerberos, local and ntlm. 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.Two things in that comment are operational requirements rather than documentation. The tokenauth capability has to be enabled when OpenID Connect is used, and rdpgw-auth has to be run as root or setuid, which is the privilege separation the PAM section referred to.
The build reflects the two-binary design. The Makefile builds both, the gateway from ./cmd/rdpgw and the auth helper from ./cmd/auth:
go build $(GOFLAGS) -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/rdpgw
go build $(GOFLAGS) -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME2) ./cmd/authwith BINNAME defaulting to rdpgw and BINNAME2 to rdpgw-auth, and an install target that copies the gateway to /usr/local/bin by default.
The versioning is derived from git. GIT_COMMIT, GIT_SHA, GIT_TAG and a dirty flag are captured, BINARY_VERSION falls back to the git tag, and VERSION_METADATA is set to unreleased unless there is an exact tag, so a build from a non-tagged commit says so.
The module targets Go 1.25.0, and the mod target runs go mod tidy with a compat flag of 1.22.
Where the boundary sits
The first limitation is that this is a gateway and not a remote desktop server. It terminates the Remote Desktop Gateway protocol and then hands the connection to a host such as XRDP. The benefit of the indirection is the access policy layer and the fact that clients only ever need to know one hostname, and the cost is that you now have two things to run and to keep patched.
The second limitation is the authentication floor. Every mechanism available assumes some notion of a directory: an OpenID Connect provider, a Kerberos KDC, a PAM stack, or NTLM credentials. A user base with none of those is not served well by this project, and the PAM path in particular asks you to run a privileged helper.
The third limitation is the IP binding, already discussed, which is a security control that the deployment reality often forces you to disable. The documentation says so itself, which is unusual candour and worth crediting.
The fourth is the client constraint. The Microsoft clients are strict about the certificate, and the README says the common name has to match the DNS hostname of the gateway. That rules out the common shortcut of pointing a client at an IP address, and it means the gateway needs a name in DNS and a certificate that matches it, which in an internal environment means either a real internal CA in the client trust store or Let's Encrypt with the host reachable on port 80.
The repository also has an UPGRADING.md at the root, which is worth reading if you are running an older version, since the project has had a 2.0 release and the release tags suggest the 2.x line has been stable for a while. There is a docs/ directory with a separate document per authentication mechanism, a cmd/ directory holding the two commands, a proto/ directory, a shared/ directory and a dev/ directory for local development.
Editorial conclusion
RDPGW fits a platform team that runs XRDP desktops in containers and wants the official Microsoft remote desktop clients to reach them over HTTPS without deploying the Windows Remote Desktop Gateway service. It is a poor fit if you only need RDP to one host, since the whole point of a gateway is the access policy layer in front of many hosts, and it is a poor fit for a user base without directory services, since the authentication mechanisms assume OpenID Connect, Kerberos, PAM or NTLM. Before deploying, sort out the TLS certificate, since the client refuses to connect without a valid one whose common name matches the gateway hostname, and read the header authentication documentation if you plan to put a reverse proxy in front, because the client IP check can be disabled and the documentation says that is a security risk.
Frequently asked questions
What does RDPGW do and what can it connect to?
RDPGW is an implementation of the Remote Desktop Gateway protocol in Go, which lets the official Microsoft remote desktop clients connect to remote desktops over HTTPS. The intended use is desktops such as XRDP running in containers on Kubernetes, and the aim is a full open source replacement for the Microsoft Remote Desktop Gateway including access policies.
Which authentication methods does RDPGW support?
OpenID Connect, Kerberos, PAM-based local authentication, NTLM, and header-based authentication for reverse proxy integration such as Azure App Proxy, Google IAP or AWS ALB. Local can be mixed with Kerberos or NTLM, but OpenID Connect cannot currently be mixed with local or Kerberos.
What TLS setup does RDPGW need?
The client requires a valid certificate signed by a trusted authority whose common name matches the gateway DNS hostname, or the client refuses to connect. You can set Tls: auto to let the gateway obtain a certificate from Let's Encrypt, in which case the host must be reachable on port 80, or Tls: enable with CertFile and KeyFile to supply your own.
Why does RDPGW ship a separate rdpgw-auth binary?
It exists to enforce privilege separation for the PAM authentication path, where the gateway connects to rdpgw-auth over a socket to verify usernames and passwords. The configuration note states that rdpgw-auth needs to be run as root or setuid in order to work, which is why the privileged process is split out rather than run inside the gateway.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bolkedebruin-rdpgw)