Tunshell is a simple and secure method to remote shell into ephemeral environments such as deployment pipelines or serverless functions. The project is predominately written in Rust.
Why would I use this over my well-established SSH client?
Good question, you wouldn't! The use case for tunshell is predominantly quick, ad-hoc remote access to hosts which you may not have SSH access to, or even the ability to install an SSH daemon at all. The beauty of tunshell is that its client is a statically-linked, pre-compiled binary which can be installed by downloading it with a one-liner script. This makes it ideal to debug environments you normally wouldn't have shell access to, some examples:
Tunshell allows you to remote shell into GitHub Actions, BitBucket Pipelines etc by inserting a one-liner into your build scripts. If you've ever spent hours trying to track down an issue on a deployment pipeline that you couldn't replicate locally because of subtle environmental differences, this could come in handy.
Tunshell even supports extremely limited environments such as AWS Lambda or Google Cloud Functions. As these platforms often only allow for execution of code in a configured language, a variety of install scripts among popular languages are provided. This could be helpful to diagnose networking or connectivity issues which are specific to these environments.
Tunshell could also be used as an exploitation tool to gain unauthorized access to remote hosts. Personally, I hope that this tool is not misused for nefarious purposes. If it becomes apparent that tunshell is helping malicious actors go about their activities, the free service will be discontinued.
Tunshell is comprised of 3 main components:
- Relay Server: a server which is able to coordinate with clients to establish connectivity
- Client Binary: a portable binary acting as a shell server or client.
- Website: The user interface for configuring a remote shell session with the relay server and providing install scripts for the client.
The process is kicked off using tunshell.com. One can generate a "session" which represents a remote shell connection from one client to another.
For each session the website generates one install script for each side of the connection. Below is a diagram illustrating the noteworthy components embedded in each script.
Installer script URL: A url pointing to a script which will install the client binary on the executing machine. These scripts detect the host's OS and CPU architecture to download the correct pre-compiled executable.
Mode argument: can be target mode (T) or local mode (L). These instruct the client to operate as a shell server or client respectively.
Session keys: a pair of random strings generated by the relay server corresponding to a session. Upon initialisation, these keys are passed back to the relay server. When a pair of clients have sent a corresponding keys, the relay server will begin establishing connectivity between the clients.
Encryption secret: a random secret which is generated locally using javascript on the website. This secret is used to generate a unique encryption key to secure data transmission between the two clients.
After the install scripts have executed and the two clients have validated their session keys with the relay server, the following process of attempting to establish a network connection between the two begins.
There are three networking models supported that are attempted and used in the following priority order:
- TCP: The clients will attempt to connect to the peer over TCP directly. If both clients are behind a firewall or NAT device, this will likely fail.
- UDP: The implementation also contains thin TCP-like protocol built on UDP. In some cases this can help establish a direct connection if at least of the clients are behind a more permissive NAT device.
- Relayed: In the case where no direct connection succeeds, the clients will fallback to proxying data through the relay server. The relay server will traffic packets between the clients using the existing TLS connections initiated by each client.
The relayed connection is also used for connections where one of the clients is running in a web browser. In which case a Web Socket is used between the client and the relay server on top of TLS.
In some restricted environments the client will not have permission to allocate a PTY. This means that running the native shell in an interactive session is not going to be possible. The client has a bare-bones (read: incomplete) implementation of a VT100-style shell which does not require a PTY and is used as a fallback in such cases. This is still WIP.
Before using tunshell is important to understand inherent risks. The nature of the application and installation method should trigger alarm bells in any developer's head given we are exposing shell access over a network. Although a lot of thought has gone into the limiting the attack surface there are is still a lot of room for improvement.
First and foremost, one must always be wary when running scripts from remote sources. The installation method of the tunshell client relies on the execution of a 3rd party script and binary on the host machine. If these were to be compromised so would your host. So it's critical that these are produced and delivered in a secure and transparent process. In summary, the artifacts are generated directly from the source in this repo, stored in AWS S3 and served via CloudFront CDN.
The next consideration is the operation of the client binary, which exposes shell access over a network channel. It is important to state that, although the traffic between clients can be passed through the relay server, effort has gone into ensuring that the relay server is not able to inspect, modify or forge traffic between any two clients. This is currently achieved by generating an encryption secret independently of the relay server which is then known to each of the clients.
In addition to the secret, during the connection establishment phase, the relay server will generate a unique nonce for each connection pair and send this nonce to each client. The clients use the encryption secret and nonce to derive an encryption key using PBKDF2-SHA256. The resulting key is unique to this connection and only known to the both clients. The traffic between the clients is then end-to-end encrypted and authenticated using AES-GCM-256.
It is important that the session and encryption keys remain secret. Exposing these parameters could allow attackers who obtain these keys to takeover hosts which have an active tunshell client.
The following is list of the supported platforms and architectures for the tunshell client:
Target | Builds | Tested |
---|---|---|
x86_64-unknown-linux-musl | Y | Y |
armv7-unknown-linux-musleabihf | Y | N |
arm-unknown-linux-musleabi | Y | Y |
aarch64-unknown-linux-musl | Y | Y |
arm-linux-androideabi | Y | N |
i686-unknown-linux-musl | Y | Y |
i586-unknown-linux-musl | Y | Y |
x86_64-apple-darwin | Y | Y |
x86_64-apple-ios | Y | N |
x86_64-pc-windows-msvc | Y | Y |
i686-pc-windows-msvc | Y | N |
- Builds: indicates whether the client successfully built for the target
- Tested: indicates whether the test suites succeeds for the target, clients with N listed may be inoperable
These targets are managed by the build pipeline.
For additional security or customisation it is possible to self-host the relay server on your own infrastructure. The details of doing so can be found here.
- Add fallback shell built-in to install https://github.com/uutils/coreutils / busybox
- Socket forwarding / file copying
- Replacing AES encryption scheme with TLS using 256 bit ECDH key pairs (public keys can be dual purposed as session keys)