Code Monkey home page Code Monkey logo

sshd-cloudflared's Introduction

Dockerised SSHd Tunnelled through cloudflared

This project aims at providing access to the current directory on your work machine through an SSH tunnel at the CloudFlare edge, all this inside a Docker container for clean separation of resources. Containers are launched in the background and compatible with the vscode remote extension. Traffic is fully encrypted end-to-end. Provided that you have downloaded the wrapper cf-sshd.sh and made it available under your $PATH, running it will create a background container and print out instructions for how to connect to it from another machine using ssh:

emmanuel@localhost:~/dev> cf-sshd.sh -v
[cf-sshd.sh] [NFO] [20220916-120406] Pulling latest image ghcr.io/efrecon/sshd-cloudflared:latest
[cf-sshd.sh] [WRN] [20220916-120413] Could not match '[email protected]' to user at GitHub
[cf-sshd.sh] [NFO] [20220916-120414] Matched 'Emmanuel Frecon' to 'efrecon' at GitHub
[cf-sshd.sh] [NFO] [20220916-120414] Will get SSH keys for GitHub user efrecon
[cf-sshd.sh] [WRN] [20220916-120414] Removing group write permissions from current directory for proper SSH access!
[cf-sshd.sh] [NFO] [20220916-120415] Waiting for tunnel establishment...
[cf-sshd.sh] [NFO] [20220916-120420] Running in container f105c67100d58a5351818bda3b2468e9902e94fb2642456178027e4f3add4deb


Run the following command to connect:
    ssh-keygen -R dev && echo 'dev ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGz7HHHQv7TKXEs12NzjpclrhDveqzI5QOmifO97NWBgugymyC75TlyCZ2DCi30jp56ykazoyf+MxffmXuAH5NQSvogmW4mXqzWZzF2Q+pxC5l2qv8+Ag+/0j/bThSMqqCsfNdFEkU61sxR5PPF5feoKTSCLA+7YCCAVtYAgWzce1AoIxCQF8v2f49tZReufDWdpFIpd7OcV/QYaj5qyWVTWe/nu0ztYUiuJlRQwS02yIhcPk/TrEUxE0ImmwvzCI3iAZTp9ORnDcgjKfoeI6xjcqXbCLMw5mt20GchC9AgkzKu2rgG+gOHPC6cjpogJnIxPwPHxdB3se13dLY/mXYqHepY2hicwzkoX3MdrGjC22ti0r+yB+38W6mnRHl7QUhKhtqB04pqooOPwA2ytt9vnj0apVGl89s9XNAM6IRp5NmDJV0YaD4mYMy7cyBr9qNGdfhSmyWHVpgUWlqhBNR0QITV2avit0nuKt0uHY2jBRkqXlY3FvvlFd8n2VV7DdgdsXt3j00yl8zlIUTrEcXc8p2l30etLQ+dHeqZ/sBjgUtotgVoI9dny7qZioc7d5Q/BG5KkB6sfoR7533Y6FQUNbxv5LtEZ4rVNW3qUeMgGhicGSHOpzdKSzBL8+1sZfCksdVD/iXzrv4p+ha5/JogQx807c31NExjhMGJ+jHaw==' >> ~/.ssh/known_hosts && ssh -o ProxyCommand='cloudflared access tcp --hostname https://dispatch-hopkins-pmc-limitations.trycloudflare.com' emmanuel@dev

Run the following command to connect without verification (DANGER!):
    ssh -o ProxyCommand='cloudflared access tcp --hostname https://dispatch-hopkins-pmc-limitations.trycloudflare.com' -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=accept-new emmanuel@dev

You can then run one of the ssh commands printed out on the console to access your directory. Remember that establishing a tunnel might take a few seconds, seconds under which ssh access will not work. Once logged in, your prompt should reflect a hostname that is called after the name of the directory where you created the container. This is to make it easier to differentiate between several such environments. Inside the container, you will have the same username as outside.

The entrypoint of the Dockerfile configures and creates a userspace SSH daemon, and establishes a guest tunnel using cloudflared. The SSH daemon automatically picks authorised keys from any user (yours!) at github, thus restricting access to that user only.

Tunnelled SSHd

If you do not wish to download or run the wrapper, you can instead run the following command.

docker run \
  -d \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd):$(pwd)" \
  -w "$(pwd)" \
  -v /etc/passwd:/etc/passwd:ro \
  -v /etc/group:/etc/group:ro \
  --group-add "$(getent group docker|cut -d: -f 3)" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(command -v docker)":/usr/bin/docker:ro \
  ghcr.io/efrecon/sshd-cloudflared \
  -g xxxx

This command works as follows:

  • It passes your current directory for access to the container, ensuring that you will be able to access the files at that location, and nothing else.
  • It passes your current user details (user and group identifier) to the container so that the entrypoint will be able to setup the ssh server with proper credentials.
  • It passes the group and password details (in read-only mode!) from the current machine, so that the ssh server will be able to refer to your username properly and impersonate you. This is because the configuration cannot have identifiers, only names.
  • It passes the Docker socket and even docker binary client, together with arranging for your user to be a member of the docker group inside the container. This enables operations on the local machine's Docker daemon from within the container running this image.
  • xxxx should be replaced by your handle at GitHub, e.g. efrecon

As the command is started in the background, you will have to pick up login details from the logs. If you have full trust, you should be able to run a command similar to the following one from another machine (provided it has a copy of cloudflared accessible under the $PATH). The command will be output in the logs, albeit with another access URL and another username:

ssh \
  -o ProxyCommand='cloudflared access tcp --hostname https://owen-go-exciting-glasgow.trycloudflare.com' \
  -o UserKnownHostsFile=/dev/null \
  -o StrictHostKeyChecking=accept-new \
  emmanuel@sshd-cloudflared

VS Code

The Dockerfile adds just enough packages to make development environments created using this image to be used with the remote extension of VS code. You will have to create a Docker volume that will be used to store the code for the vscode-server in the container. The content of this volume needs to be owned by your user.

To create and initialise the volume, run the following (and possibly adapt):

docker volume create vscode-server
docker run --rm -v vscode-server:/vscode-server busybox \
  /bin/sh -c "touch /vscode-server/.initialised && chown -R $(id -u):$(id -g) /vscode-server"

Once you have created the volume, you can pass it to overload the .vscode-server directory whenever you want an environment, as in the following command. The first time you start the remote extension against the SSH daemon in that container, it will install and automatically run the server inside the ${HOME}/.vscode-server.

docker run \
  -d \
  --user "$(id -u):$(id -g)" \
  -v "$(pwd):$(pwd)" \
  -w "$(pwd)" \
  -v /etc/passwd:/etc/passwd:ro \
  -v /etc/group:/etc/group:ro \
  --group-add "$(getent group docker|cut -d: -f 3)" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(command -v docker)":/usr/bin/docker:ro \
  -v "vscode-server:${HOME}/.vscode-server" \
  ghcr.io/efrecon/sshd-cloudflared \
  -g xxxx

This command can be automated using the wrapper.

Wrapper

As the raw docker commands are bit complex, this projects comes with a wrapper. This is a standalone shell script, tuned for installation somewhere in the $PATH. The wrapper will:

  1. Automatically pull the latest image of this project at the GHCR.
  2. Arrange for the current directory to only be writable by your user, this is a mandatory requirement for being able to setup and run a userspace SSH daemon in the current directory.
  3. Setup a Docker volume unique for your local user, in order to facilitate using that container as a VS Code remote.
  4. When called without arguments, the wrapper will collect your git email and name and try using them to look for your GitHub handle using the search API.
  5. Start a Docker container in the background with either the GitHub handle discovered above, or the remaining arguments, as is.
    • Unless specified otherwise, the hostname of the SSH daemon will be the name of the directory where the wrapper was started from. This is to better identify various development environments.
    • Unless impossible or configured otherwise the wrapper will arrange for the docker client to be accessible and fully working from within the container, thus providing access to the local docker daemon.
    • The wrapper should carry on your current shell into the container, as long as the container has its binary, e.g. /bin/bash.
    • Unless configured otherwise, the wrapper will arrange for the SSH server to be compatible with the VS Code Remote extension.
  6. Wait for the container and tunnel to be ready and extract tunnel information from the Docker logs.

Provided you have the XDG directory $HOME/.local/bin in your account, run the following to install the wrapper and make it available as cf-sshd.sh under the $PATH.

curl \
  --location \
  --silent \
  --output "$HOME/.local/bin/cf-sshd.sh" \
  https://raw.githubusercontent.com/efrecon/sshd-cloudflared/main/cf-sshd.sh && \
  chmod u+x "$HOME/.local/bin/cf-sshd.sh"

When searching for user details at GitHub fails, you will have to provide this information at the command-line. The wrapper uses a -- to separate options from arguments and all arguments are blindly passed to the entrypoint of the Docker container. As an example, the following would kickstart a container for the efrecon GitHub user.

cf-sshd.sh -- -g efrecon

The wrapper is designed to minimise input and should "just work". Options and flags exist to tweak its behaviour to your needs if necessary. Calling it with the -h flag will print help over the dash-led options and flags that it supports, as well as the environment variables that it recognises.

Manual Cleanup

By default, the Docker entrypoint, will create a hidden temporary directory under the current directory. This directory starts with the .cf-sshd_ prefix, followed by a random string. This directory will contain files for the configuration of the SSH daemon, which need to be accessible with proper permissions. If you kill the container abruptly (i.e. using rm -fv rather than first running stop), the directory will not be removed and you will have to cleanup manually.

Similar Work

Part of this code is inspired by this project.

Development Notes

To work on modifying this implementation, you will have to iteratively (re)build the images and use the local image for your tests.

Build Docker Images

To build the docker images locally, run the following commands from this directory.

docker build -t ghcr.io/efrecon/sshd-cloudflared-base:latest -f Dockerfile.base .
docker build -t ghcr.io/efrecon/sshd-cloudflared:latest -f Dockerfile .

Take a note of the identifier of the last image that was built, e.g. 552379793d65

Run a Container

Use the identifier of the last image built to run a container using the wrapper, e.g. as in the following command. The command also increases verbosity with the -v option, and exports the port of the SSH daemon locally to the host, at 2222.

./cf-sshd.sh -i 552379793d65 -v -p 2222

Login

You should now be able to login into the container either through the instructions printed at the terminal, or directly via the exported port, e.g. with the following command, and provided that your local ssh client is setup with one of the keys at GitHub.

ssh localhost -p 2222

Problems?

When looking for problems, look for the Docker container that was created in the background, its logs will contain the logs from the entrypoint and also from all underlying services.

Files that were automatically generated are generated in the directory which name is prefixed with .cf-sshd_ under the current directory.

Dismantling

Once done, remove the container in two steps, using the name that was automatically assigned to it by Docker, e.g. as with the following command. This gives the container a chance to remove the local directory that it created under the directory that was shared.

docker stop practical_agnesi && docker rm -v practical_agnesi

sshd-cloudflared's People

Contributors

efrecon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

sshd-cloudflared's Issues

Add git

Add git to the image so that it fits better to the Vs code use case. Maybe create two images? A minimal one and another one with docker and vscode capabilities (called devenv or similar)

Log rotation

Add log rotation to avoid for the log files to grow uncontrolled over time.

Mount more

The wrapper is currently restricted to mounting the current directory into the container, together with the volume necessary for setting up VS code. Many tools enjoy being able to store user-related settings directory under the $HOME directory, e.g. the AWS CLI, npm, the CDK, and all other tools storing stuff in the XDG directories. The wrapper could:

  1. Have support for user-specified mounting from the hidden directories under the $HOME
  2. Have automatic support for a number of tools, including for XDG stuff.

This would help the destination container to provide a sufficiently emulated user environment. These improvements need to be optional (but probably turned on by default).

Add sftp server

The current configuration file does not turn on the sftp server, it probably should so that it gets possible to mount the container from the outside using sshfs.

# override default of no subsystems
Subsystem    sftp    /usr/lib/ssh/sftp-server

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.