|
1 | 1 | # oauth2-forwarder
|
| 2 | + |
| 3 | +This utility allows forwarding an oauth2 interactive request that is initiated inside a docker container to the browser on the docker host and then forwarding the resulting redirect back into the docker container. The net effect is that a command line tool inside the docker container can perform interactive oauth2 authentication using the browser flow on its host. |
| 4 | + |
| 5 | +## Background |
| 6 | + |
| 7 | +Imagine you have a command line tool that performs oauth2 interactive login. The typical flow of that application is this: |
| 8 | + |
| 9 | +1. CLI tool sends an interactive login url to the browser and, simultaneously, opens up an http service to listen for the redirect response. |
| 10 | +2. The browser opens the login url and the user performs interactive authentication. |
| 11 | +3. The successful interactive authentication results in a redirect GET request being made with the new authentication code. |
| 12 | +4. The http service setup by the CLI tool receives the redirect requests, extracts the code, and uses it. |
| 13 | + |
| 14 | +This, for example, is exactly how the @azure/msal-node library works if you use the `.acquireTokenInteractive()` method on a public application. |
| 15 | + |
| 16 | +This flow breaks down if you try to use it inside a docker container (at least one without a GUI) because step 2 fails, i.e., there is no way for the CLI to launch the host's browser for interactive login. You could get around this by using a device flow, but (a) that's a little more cumbersome, and (b) often conditional access policies will block that flow if they are checking for device compliance. |
| 17 | + |
| 18 | +If you ever tried to do this from inside a docker container managed by VS Code, you'd have seen that it magically works. This utility gives you similar functionality without needing to use VS Code. |
| 19 | + |
| 20 | +This utility works by changing the flow above as follows using a client-server pair provided by the utility--called, respectively, `o2f-server` and `o2f-client`--to proxy sending the request and redirect urls back and forth between the container and host. The flow then becomes: |
| 21 | + |
| 22 | +1. [container] CLI tool sends an interactive login url to the browser and, simultaneously, opens up an http service to listen for the redirect response. |
| 23 | +2. [container] `o2f-client` intercepts the request and forwards the url to `o2f-server` over a custom tcp port |
| 24 | +3. [host] `o2f-server` receives the request sends the the login url to the browser and simultaneously opens an http service to listen for the redirect response |
| 25 | +4. [host] The browser opens the login url and the user performs interactive authentication. |
| 26 | +5. [host] The successful interactive authentication results in a redirect GET request being made with the new authentication code. |
| 27 | +6. [host] The http service setup by `o2f-server` receives the redirect requests, extracts the code, and sends it back to `o2f-client` using the tcp channel opened in step 2. |
| 28 | +7. [container] `o2f-client` makes a GET requested for the redirect url it just received. |
| 29 | +8. [container] The http service setup by the CLI tool receives the redirect requests, extracts the code, and uses it. |
| 30 | + |
| 31 | +## Installation and Usage |
| 32 | + |
| 33 | +This helper is written in Typescript and compiles down to two Javascript scripts, one for the server and one for the client. |
| 34 | + |
| 35 | +### Download |
| 36 | + |
| 37 | +Download the latest release from this repo. The release consists of a filed named `oauth2-forwarder.zip` which contains two Javascript scripts: `o2f-server.js` and `o2f-client.js` plus a helper `browser.sh` script, all in a directory called `o2f`. These can be placed wherever you want, but these instructions assume they are placed in the home directories of the host and container. |
| 38 | + |
| 39 | +### On the host |
| 40 | + |
| 41 | +Run `node ~/of2-server.js`. This will launch the server and it will listen for TCP connections on localhost at a random port which will be displayed in the console. You will need to keep this console/terminal open. |
| 42 | + |
| 43 | +Notes: |
| 44 | + |
| 45 | +- You can tell it to use a specific port by setting the environmental variable `OAUTH2_FORWARDER_PORT` |
| 46 | + |
| 47 | +### In the container |
| 48 | + |
| 49 | +Run `export OAUTH2_FORWARDER_SERVER="host.docker.internal:PORT` where PORT is replaced with the port displayed when you ran the server. |
| 50 | + |
| 51 | +Run `export BROWSER=~/o2f/browser.sh` to set an environmental variable that will intercept attemps to open the system browser. If you've moved the `o2f` directory you'll need to change this variable setting appropriately. |
| 52 | + |
| 53 | +NB: This works for a linux container that uses the BROWSER variable to determine how to handle requests to open things in a browser, for example, like those created by the npm `open` library. If you have a different setup your mileage my vary. Feel free to raise an issue here to get help. |
| 54 | + |
| 55 | +Run your oauth2 CLI app as normal. |
| 56 | + |
| 57 | +Notes: |
| 58 | + |
| 59 | +- You can turn on more verbose debugging information by setting the environmental variable `OAUTH2_FORWARDER_DEBUG` to `true`. The logging on the client side is saved in `/tmp/oauth2-forwarder.log`. On the server side it is output to the console. |
| 60 | + |
| 61 | +### Using a Dockerfile |
| 62 | + |
| 63 | +Here's a strategy to make this fairly easy to use with a Docker container built with a Dockerfile. |
| 64 | + |
| 65 | +On the host, set a specific port that you will listen on by configuring the env variable `OAUTH2_FORWARDER_PORT`. |
| 66 | + |
| 67 | +Add these lines in the Dockerfile |
| 68 | + |
| 69 | +``` |
| 70 | +RUN curl -LO https://github.com/sam-mfb/oauth2-forwarder/releases/download/v[VERSION]/oauth2-forwarder.zip |
| 71 | +RUN unzip oauth2-forwarder.zip |
| 72 | +ENV OAUTH2_FORWARDER_SERVER host.docker.internal:[PORT] |
| 73 | +ENV BROWSER ~/o2f/browser.sh |
| 74 | +``` |
| 75 | + |
| 76 | +Of course, replace `[VERSION]` and `[PORT]` with the actual version number and port number (or use Docker's `ARG` command). |
| 77 | + |
| 78 | +## Debugging |
| 79 | + |
| 80 | +You can enable debugging on either the server or the client by setting the environmental variable `OAUTH2_FORWARDER_DEBUG` to `true`. |
| 81 | + |
| 82 | +## Security |
| 83 | + |
| 84 | +Since this is using all localhost tcp communications the security model is the same using this tool as it is in the non-containerized solution. In other words, in both cases the received auth code is transmitted over plaintext tcp on the localhost only. NB: you could modify this tool to send the client<-->server traffic across the network, but that would not be a good idea. |
| 85 | + |
| 86 | +NB: If you believe there is a security issue with the app, please reach out to me directly via email, which is just `sam` at my company's domain. |
0 commit comments