Search code examples
mongodbgithubgithub-codespaces

How do I connect to MongoDB, running in Github codespaces, using MongoDB Compass?


I'm trying out Github codespaces, specifically the "Node.js & Mongo DB" default settings.

The port is forwarded, and my objective is to connect with MongoDB Compass running on my local machine.

The address forwarded to 27017 is something like https://<long-address>.githubpreview.dev/

My attempt

I attempted to use the following connection string, but it did not work in MongoDB compass. It failed with No addresses found at host. I'm actually unsure about how I even determine if MongoDB is actually running in the Github codespace?

mongodb+srv://root:example@<long-address>.githubpreview.dev/

.devcontainer files


docker-compose.yml

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        # Update 'VARIANT' to pick an LTS version of Node.js: 16, 14, 12.
        # Append -bullseye or -buster to pin to an OS version.
        # Use -bullseye variants on local arm64/Apple Silicon.
        VARIANT: "16"
    volumes:
      - ..:/workspace:cached
    init: true

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

    # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
    network_mode: service:db
    # Uncomment the next line to use a non-root user for all processes.
    # user: node

    # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. 
    # (Adding the "ports" property to this file will not forward from a Codespace.)

  db:
    image: mongo:latest
    restart: unless-stopped
    volumes:
      - mongodb-data:/data/db
    # Uncomment to change startup options
    environment:
     MONGO_INITDB_ROOT_USERNAME: root
     MONGO_INITDB_ROOT_PASSWORD: example
     MONGO_INITDB_DATABASE: foo

    # Add "forwardPorts": ["27017"] to **devcontainer.json** to forward MongoDB locally.
    # (Adding the "ports" property to this file will not forward from a Codespace.)

volumes:
  mongodb-data: null

And a devcontainer.json file

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.203.0/containers/javascript-node-mongo
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
{
    "name": "Node.js & Mongo DB",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/workspace",

    // Set *default* container specific settings.json values on container create.
    "settings": {},

    // Add the IDs of extensions you want installed when the container is created.
    "extensions": [
        "dbaeumer.vscode-eslint",
        "mongodb.mongodb-vscode" 
    ],

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    "forwardPorts": [3000, 27017],

    // Use 'postCreateCommand' to run commands after the container is created.
    // "postCreateCommand": "yarn install",

    // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
    "remoteUser": "node",
    "features": {
        "git": "os-provided"
    }
}

and finally a Docker file:

# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster
ARG VARIANT=16-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}

# Install MongoDB command line tools if on buster and x86_64 (arm64 not supported)
ARG MONGO_TOOLS_VERSION=5.0
RUN . /etc/os-release \
    && if [ "${VERSION_CODENAME}" = "buster" ] && [ "$(dpkg --print-architecture)" = "amd64" ]; then \
        curl -sSL "https://www.mongodb.org/static/pgp/server-${MONGO_TOOLS_VERSION}.asc" | gpg --dearmor > /usr/share/keyrings/mongodb-archive-keyring.gpg \
        && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mongodb-archive-keyring.gpg] http://repo.mongodb.org/apt/debian $(lsb_release -cs)/mongodb-org/${MONGO_TOOLS_VERSION} main" | tee /etc/apt/sources.list.d/mongodb-org-${MONGO_TOOLS_VERSION}.list \
        && apt-get update && export DEBIAN_FRONTEND=noninteractive \
        && apt-get install -y mongodb-database-tools mongodb-mongosh \
        && apt-get clean -y && rm -rf /var/lib/apt/lists/*; \
    fi

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"

# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>"

Update I also posted here in the MongoDB community, but no help...


Solution

  • As @iravinandan said you need to set up a tunnel.

    Publishing a port alone won't help as all incoming requests are going through an http proxy.

    If you dig CNAME <long-address>.githubpreview.dev you will see it's github-codespaces.app.online.visualstudio.com. You can put anything in the githubpreview.dev subdomain and it will still be resolved on the DNS level.

    The proxy relies on HTTP Host header to route the request to correct upstream so it will work for HTTP protocols only.

    To use any other protocol (MongoDb wire protocol in your case) you need to set up a TCP tunnel from codespaces to your machine.

    Simplest set up - direct connection

    At the time of writing the default Node + Mongo codespace uses Debian buster, so ssh port forwarding would be the obvious choice. In the codespace/VSCode terminal:

    ssh -R 27017:localhost:27017 your_public_ip
    

    Then in your compas connect to

    mongodb://localhost:27017
    

    enter image description here

    It will require your local machine to run sshd of course, have a white IP (or at least your router should forward incoming ssh traffic to your computer) and allow it in the firewall. You can pick any port if 27017 is already being used locally.

    It's the simplest set up but it exposes your laptop to the internet, and it's just a matter of time to get it infected.

    A bit more secure - jumpbox in the middle

    To keep your local system behind DMZ you can set up a jumpbox instead - a minimalistic disposable linux box somewhere in the internet, which will be used to chain 2 tunnels:

    • Remote port forwarding from codespace to the jumpbox
    • Local port forwarding from your laptop to the jumpbox

    The same

    mongodb://localhost:27017
    

    on mongo compas.

    The jumpbox have to expose sshd to the internet, but you can minimise risks by hardening its security. After all it doesn't do anything but proxy traffic. EC2 nano will be more than enough, just keep in mind large data transfers might be expensive.

    Hassle-free tunnel-as-a-service

    Something you can try in 5 min. ngrok has been around for more than a decade and it does exactly this - it sells tunnels (with some free tier sufficient for the demo).

    In your codespace/VScode terminal:

    npm i ngrok --save-dev
    

    To avoid installing every time but ensure you don't ship with production code. You will need to register an account on ngrok (SSO with github will do) to get an authentication code and pass it to the codespaces/VSCode terminal:

    ./node_modules/.bin/ngrok authtoken <the token>
    

    Please remember it saves the token to the home directory which will be wiped after rebuild. Once authorised you can open the tunnel in the codespaces/VSCode terminal:

    ./node_modules/.bin/ngrok tcp 27017
    

    Codespace will automatically forward the port:

    enter image description here

    And the terminal will show you some stats (mind the free tier limit) and connection string:

    enter image description here

    The subdomain and port will change every time you open the tunnel. From the image above the connection parameters for mongodb compas will be:

    mongodb://0.tcp.ngrok.io:18862
    

    with authorization parameters on mongodb level as needed.

    Again, keep in mind you leave your mongodb exposed to the internet (0.tcp.ngrok.io:18862), and mongo accepts unauthenticated connections.

    I wouldn't leave it open for longer than necessary.

    Use built-in mongodb client

    The node + mongo environment comes with handy VScode plugins pre-installed:

    enter image description here

    Of course it lacks many of compas analytical tools but it works out of the box and is sufficient for development.

    Just open the plugin and connect to localhost:

    enter image description here

    Compass D.I.Y

    The best option to get compass functionality without compromising security and achieve zero-config objective is to host compass yourself. It's an electron application and works perfectly in a browser in Mongodb Atlas.

    The source code is available at https://github.com/mongodb-js/compass. With a bit of effort you can craft a docker image to host compass, include this image into docker-compose, and forward the port in devcontainer.json

    Github codespaces will take care of authentication (keep the forwarded port private so only owner of the space will have access to it). All communication from desktop to compass will be over https, and compass to mongodb will be local to the docker network. Security-wise it will be on par with VSCode mongodb plugin