Skip to content
Snippets Groups Projects
README.md 10.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Bolke de Bruin's avatar
    Bolke de Bruin committed
    GO Remote Desktop Gateway
    =========================
    
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    ![Go](https://github.com/bolkedebruin/rdpgw/workflows/Go/badge.svg)
    
    [![Docker Pulls](https://badgen.net/docker/pulls/bolkedebruin/rdpgw?icon=docker&label=pulls)](https://hub.docker.com/r/bolkedebruin/rdpgw/)
    [![Docker Stars](https://badgen.net/docker/stars/bolkedebruin/rdpgw?icon=docker&label=stars)](https://hub.docker.com/r/bolkedebruin/rdpgw/)
    [![Docker Image Size](https://badgen.net/docker/size/bolkedebruin/rdpgw?icon=docker&label=image%20size)](https://hub.docker.com/r/bolkedebruin/rdpgw/)
    
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    :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).
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    This allows you to connect with the official Microsoft clients to remote desktops over HTTPS. 
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    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.
    
    
    ## Security
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    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. 
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    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)
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    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.
    
    ## How to build & install
    
    bolkedebruin's avatar
    bolkedebruin committed
    
    __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.
    
    bolkedebruin's avatar
    bolkedebruin committed
    
    Then clone the repo and issues the following.
    
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    ```bash
    cd rdpgw
    
    make
    make install
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    ```
    
    ## Configuration
    By default the configuration is read from `rdpgw.yaml`. Below is a 
    template.
    
    ```yaml
    # web server configuration. 
    
    bolkedebruin's avatar
    bolkedebruin committed
     # 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:
    
    bolkedebruin's avatar
    bolkedebruin committed
     # 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
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
     # TLS certificate files
    
     CertFile: server.pem
     KeyFile: key.pem
    
     # gateway address advertised in the rdp files and browser
    
     GatewayAddress: localhost
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
     # port to listen on (change to 80 or equivalent if not using TLS)
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
     # list of acceptable desktop hosts to connect to
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
      - localhost:3389
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
      - my-{{ preferred_username }}-host:3389
    
     # 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.
    
     SessionStore: cookie
    
      # 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 
    
    # Open ID Connect specific settings
    
    OpenId:
     ProviderUrl: http://keycloak/auth/realms/test
     ClientId: rdpgw
     ClientSecret: your-secret
    
    bolkedebruin's avatar
    bolkedebruin committed
    Kerberos:
     Keytab: /etc/keytabs/rdpgw.keytab
     Krb5conf: /etc/krb5.conf
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    # enabled / disabled capabilities
    
    Caps:
     SmartCardAuth: false
     TokenAuth: true
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
     # connection timeout in minutes, 0 is limitless
    
     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
    
      # 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
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
      # Verifies if the ip used to connect to download the rdp file equals from where the
      # connection is opened.
      VerifyClientIp: true
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    ```
    
    bolkedebruin's avatar
    bolkedebruin committed
    ## 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
    ```
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    
    ## 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).
    
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    ## TODO
    * Integrate Open Policy Agent
    * Integrate uber-go/zap
    * Research: TLS defragmentation 
    
    * Improve Web Interface
    
    Bolke de Bruin's avatar
    Bolke de Bruin committed
    
    
    bolkedebruin's avatar
    bolkedebruin committed