bolkedebruin / rdpgw

Remote Desktop Gateway in Go for deploying on Linux/BSD/Kubernetes
Apache License 2.0
693 stars 117 forks source link

Using NTLM with a container #118

Open ency98 opened 3 weeks ago

ency98 commented 3 weeks ago

Using NTLM with a container.

Before I start I just want to say thank you to bolkedebruin. I may get a little snarky in this post but I do appreciate the work you have done creating this project and providing the documentation you have provided. I know it's impossibly hard to help everyone or write documentation that will cover the majority of "normal" cases let alone the edge cases that can be thrown at you. I also know it can be difficult helping people that don't know enough to ask the correct questions or have any idea how things function on the back end. You have done some good work and thank you.

I’m just posting this here to help anyone else having issues trying to figure out where to map various files how to get rdpgw-auth going and get the rdpgw-auth.sock in /tmp/

I’m not a programmer my my scripting skills are rather primitive so this took me WAY too long to figure out. But I really wanted to get it going so I banged my head against it until I got it going. The lack of explanations and documentation was very frustrating. But home labers ticking about on the weekend are not the target audience and the people that would actually need this and actually decided to look for a non MS solution probably know more than me when it comes to reading go and editing Dockerbuild files. A simple list of directories that the yaml files were supposed to be mapped to while running in a container would have saved me a lot of time.

Again, I'm not a developer, programmer, or do much dev-ops work. I'm sure there are better ways to accomplish what I wanted and I'm sure I walked all over best practices.

I hope this can help save someone’s weekend.

I decided I wanted to use NTLM as it was a pain to do the whole OAuth thing while I was on my LAN or connected to my VPN. I do have an external facing instance using oauth with Authentik as the backend just to be cool and win a nerd contest with a co-worker. But that's a whole other story. The OAuth implimentation is a lot easier and has much better documentation.

I'm also not using the provided docker-compose file as I had no need for the xrdp container for testing or for regular use.

Step 1: editing the run.sh script

Use git to clone the project.

cd into the docker directory "cd ./rdpgw/dev/docker"

Open the run.sh script. This is the "entry point for the rdpgw container and launches the rdpgw server and starts rdpgw-auth for local and NTLM authentication.

Delete everything between

USER=rdpgw

And

cd /opt/rdpgw || exit 1

Then delete everything after

cd /opt/rdpgw || exit 1

All the way to

# drop privileges and run the application

Now we need to add back the part that starts rdpgw-auth with some bits that will create rdpgw-auth.sock

Between

cd /opt/rdpgw || exit 1

And

# drop privileges and run the application

Add this line

/opt/rdpgw/rdpgw-auth -n rdpgw -s /tmp/rdpgw-auth.sock &

The entire run.sh script should look something like this.

#!/bin/sh

USER=rdpgw

cd /opt/rdpgw || exit 1
/opt/rdpgw/rdpgw-auth -n rdpgw -s /tmp/rdpgw-auth.sock &

# drop privileges and run the application
su -c /opt/rdpgw/rdpgw "${USER}" -- "$@" &
wait
exit $?

That's pretty much all I had to change to get the container up and going with /tmp/rdpgw-auth.sock available for authentication.

Step 2: building the container.

This step want too bad and there are a bunch of good guides out there.

Make sure you are still in the directory that contains the run.sh script and Dockerfile.

Run the following command to build the container.

docker build -t rdpgw-ntlm:latest .

The container will start building. I had no issues on this step. You may or may not need some dependancies but I seemed to have everything the build required already installed on my dev machine.

If you are like me and are in a serious nerd pissing contest with some coworkers and host your own registry the command will look something like this.

docker build -t repo.domain.tld/rdpgw:latest . 
docker push repo.domain.tld/rdpgw:latest

We now have the container built and ready to do some NTLM authentication.

Step 3: docker-compose.yml

Now on to the docker compose file.

You will need to map the following directories and files from your host into the container.

    volumes:
      - ./appdata/opt/rdpgw/rdpgw.yaml:/opt/rdpgw/rdpgw.yaml
      - ./appdata/opt/rdpgw/rdpgw-auth.yaml:/opt/rdpgw/rdpgw-auth.yaml
      - ./appdata/opt/rdpgw/server.pem:/opt/rdpgw/server.pem
      - ./appdata/opt/rdpgw/key.pem:/opt/rdpgw/key.pem
      - ./appdata/ect/rdpgw/default.rdp:/etc/rdpgw/default.rdp

I tried just mapping

      - ./appdata/opt/rdpgw/:/opt/rdpgw

But that never worked.

The environmental section looks like this.

    environment:
      RDPGW_SERVER__CERT_FILE: /opt/rdpgw/server.pem
      RDPGW_SERVER__KEY_FILE: /opt/rdpgw/key.pem
      RDPGW_SERVER__SESSION_STORE: "file"
      RDPGW_SERVER__GATEWAY_ADDRESS: https://rdg.domain.tld
      RDPGW_SERVER__PORT: "9443"

I found that a lot of the information on the issues page and in the read me's had me putting duplicate information in the rdpgw.yaml and the compose file. I'm sure that was me just reading things wrong. One issue I was not able to sort out was the paatokenencryptionkey and usertokensigningkey. It does not matter if I provide my own keys in the compose file in the rdpgw.yaml file or both the logs always report

No valid `security.paatokenencryptionkey` specified (empty or not 32 characters). Setting to random
No valid `security.usertokensigningkey` specified (empty or not 32 characters). Setting to random

The rest of your compose file is going to be filled with fiddly bits relating to your specific setup and workflow. Here is a basic config that should work.

---
services:

  app:
    image: rdpgw-ntlm:latest # Assuming you are deploying this on the same host you built the container on.  

    ports:
      - 9443:9443

    restart: on-failure

    volumes:
      - ./appdata/opt/rdpgw/rdpgw.yaml:/opt/rdpgw/rdpgw.yaml
      - ./appdata/opt/rdpgw/rdpgw-auth.yaml:/opt/rdpgw/rdpgw-auth.yaml
      - ./appdata/opt/rdpgw/server.pem:/opt/rdpgw/server.pem
      - ./appdata/opt/rdpgw/key.pem:/opt/rdpgw/key.pem
      - ./appdata/ect/rdpgw/default.rdp:/etc/rdpgw/default.rdp

    environment:
      RDPGW_SERVER__CERT_FILE: /opt/rdpgw/server.pem
      RDPGW_SERVER__KEY_FILE: /opt/rdpgw/key.pem
      RDPGW_SERVER__SESSION_STORE: "file"
      RDPGW_SERVER__GATEWAY_ADDRESS: https://rdg.domain.tld
      RDPGW_SERVER__PORT: "9443"

Step 4: yaml config files

This was a real pain to sort out. There a lot of brief mentions or hinting about things in the issues pages and in the read me. It took a lot of making a change, spinning things up, test, spin down, cry, and try a different way. I'm not even going to attempt to walk through these settings as I have no clue what I'm talking about.

rdpgw.yaml

Would really have been nice to have all the options listed even if they were not described. I know nothing about programming or go apps and had to dig through every file in this repo to try references to settings and where they might need to be applied.

Server:
  Authentication:
    - ntlm

  BasicAuthTimeout: "5"

  Tls: "auto"

  Hosts:
    - "HOST_1_DNS/IP:3389" # Don't get cute and think you're smarter than the author who made the app. Your gonna need the port
    - "HOST_2_DNS/IP:3389" # Don't forget that this is linux. What you use in the rdp file or the default.rdp if you add one, has to match the case of what you enter here. 
    - "HOST_3_DNS/IP:3389"

  HostSelection: "unsigned" # somewhere on the issues page its listed what options are available. This was the only option that worked for me if I had multiple hosts
  SessionKey: "GENERATE A 32 CHAR KEY" # CHANNGE
  SessionEncryptionKey: "GENERATE A 32 CHAR KEY" # CHANNGE

AuthSocket: /tmp/rdpgw-auth.sock # this MF thing... Remember the run.sh script... yeah...

Caps:
  TokenAuth: "false"
  IdleTimeout: "120"
  EnableClipboard: "true" # If you do not add this you will not be able to copy/paste no matter what setting you put into your RDP configs
  EnableDrive: "true" # If you do not add this you will not be able to copy/paste no matter what setting you put into your RDP configs

Client:
  defaults: "/etc/rdpgw/default.rdp"
  UsernameTemplate: "{{ username }}@DN.domain.tld" # Change the domain or remove I did not notice a difference either way
  SplitUserDomain: "false"

Security:
  PAATokenSigningKey: "GENERATE A 32 CHAR KEY" # CHANNGE
  UserTokenEncryptionKey: "GENERATE A 32 CHAR KEY" # CHANNGE
  EnableUserToken: "true"
  VerifyClientIp: "true"

rdpgw-auth.yaml

No problems here. Simple as.

Users:
 - {Username: "USERNAME", Password: "PASSWORD"} 

default.rdp

Nothing to fancy here. Just some basic settings for the network, disabling audio, and a failed attempt to get the clipboard working before I figured out it had to be enabled in rdpgw.yaml file. My coworker and I spent a whole day digging through our GPO's at work to see if it was being blocked by gpo, image local policy, mdm, or any of the handful of other systems we had in place that could have been blocking it.

connection type:i:7
bandwidthautodetect:i:1
networkautodetect:i:1
audiomode:i:2
autoreconnect max retries:i:5
autoreconnection enabled:i:1
session bpp:i:16
smart sizing:i:1
redirectclipboard:i:1

Step 5: Certs

Take a look at certstrap if you want to use your own and dont feel like googling how to use openssl. Your you are bound and determined to run your own CA but refuse to do it the right way.... Dont judge me!!!

certstrap

The container will also generate its own certs. No matter what you do it is important that your client computer trusts the certs.

I run mine behind a proxy using this project for certs and tcp pass through.

tlsproxy

You can use NGINX but I was already frustrated and did not feel like learning anything new last weekend. NGINX proxy manager should have been able to do it but I wasn't able to get the tcp streams working.

That's pretty much it.

You have the container that will have rdpgw-auth running and the rdpgw-auth.sock in /tmp/ ready to authenticate.

You have a basic docker-compose file to spin up the container

You have the rdpgw.yaml to configure the application and the rdpgw-auth.ymal supplying the user used to authenticate against the application.

You have a basic default.rdp file if you don't configure anything by hand in the rdp client or carry around your own rdp files.

And hopefully you have a weekend or two you can spend with your family instead getting stuck in a rabbit hole. trying to figure out something that should be "fun" and "easy".

Bonus info. If you want to try your hand at deploying remote apps add these lines to your RDP file to specify the specific app

alternate shell:s:rdpinit.exe
remoteapplicationmode:i:1
remoteapplicationname:s:Firefox
remoteapplicationprogram:s:||Firefox

The specific name was a pain for me to figure out deploying apps using the MS console and powershell. I just did not know enough to ask the right questions. Kasm had a pretty cool page on deploying remote apps using their server and I was able to use a lot of info from that page to configure some remote apps and deploy the apps in a way that didn't cause more problems than it fixed.

kasmweb