gameveloster
gameveloster

Reputation: 1503

Docker container that creates a SSH tunnel on docker-compose up

My docker-compose.yml file creates a service api listening on port 9000 and a remote server 123.1.2.3 needs to access it. An SSH tunnel is required because there is a firewall preventing direct access to the Docker host port 9000.

version: "3.9"

services:
  api:
    image: my/api-service:latest
    ports:
    - "9000:9000"

I'm currently manually creating this SSH tunnel by running this command on the Docker host

ssh -fN -R 9000:127.0.0.1:9000 [email protected]

Is it possible to create another service in this docker-compose.yml file to create this SSH tunnel on running docker-compose up, using a SSH private key in the same directory as the docker-compose.yml file?

Upvotes: 0

Views: 2128

Answers (1)

David Maze
David Maze

Reputation: 158647

This should be possible. Looking at a copy of the ssh(1) man page, the ssh -R option sets up a port forward from the remote machine back to the local machine

ssh -R port:host:hostport

where port on the remote host is forwarded through the ssh tunnel, making outbound connections to host:hostport from the local system.

In your case, if the ssh tunnel was launched from a container, you could use normal Docker networking, and connect to api:9000, using the standard port number of the container.

The first thing you'll need is an image with the ssh client. This is easy enough to build from source, so we'll do that

FROM ubuntu:22.04
RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive \
    apt-get install --no-install-recommends --assume-yes openssh-client

Do not copy the ssh keys into the image. Anything that's in an image can be trivially extracted later, and you don't want to compromise your ssh keys this way.

Instead, we'll bind mount our ssh keys into the container when it runs. ssh is extremely particular about the permissions of the ssh keys, so you need to make sure the container runs as the same numeric user ID as on your host system. Run

id -u

and remember that number (on an Ubuntu system, it might be 1000).

Now, in the Compose file, in addition to the original server, we need to

  1. Build the image;
  2. Specify the user ID;
  3. Inject the ssh keys;
  4. Create some concept of a "home directory"; and
  5. Specify the actual ssh command to run.
version: '3.8'
services:
  api:
    image: my/api-service:latest
    # ports: ['9000:9000']  # optional
  tunnel:
    build:
      context: .
      dockerfile: Dockerfile.ssh
    user: 1000
    volumes:
      - /home/yourname/.ssh:/home/.ssh
    environment:
      HOME: /home
    command: ssh -N -R 9000:api:9000 [email protected]

In the last line the first 9000 is the port number on the remote system, and api:9000 is the container name and standard port number for the target container. ports: would also publish the port on the local system and aren't required (or considered) for connections between containers. I've omitted the ssh -f option so that the ssh tunnel runs as a foreground process, as the only process in its container.

Upvotes: 2

Related Questions