SSHX - Web-based Secure Collaborative Open Source Command Line Tool

Introduction⌗
Before learning about this open-source project, I had encountered similar needs myself:
- Entering a Gitlab Runner for debugging during CI/CD execution (the project includes a Github Action example)
- Assisting team members with terminal operations (this need may resonate particularly with IT Administrators)
Overview⌗
SSHX is a command-line collaboration tool developed in Rust that supports multiple people collaborating online simultaneously. It’s very simple to use, and SSHX’s main features include:
- Cross-platform
- Simple and lightweight
- End-to-end encryption
- Support for multiple terminals
- Globally optimized network
- Instant messaging support
- Real-time display of user names and cursor positions
Installation⌗
# Using the installation script
curl -sSf https://sshx.io/get | sh
# Or install from source code
cargo install sshx
Usage⌗
Share your terminal with others:
sshx
sshx v0.2.1
➜ Link: https://sshx.io/s/dStBgdqp7V#GvmVDg46h40BRz
➜ Shell: /bin/zsh
After starting, share the generated link with others, and they can interact in your terminal via the web.
First-time users need to enter their name:
After members join:
The toolbar at the top of the Web UI includes, in order: Logo, New Terminal, Chat, Settings, and Network Status.
Self-hosted⌗
Although the official service experience is decent, network fluctuations can still be significant. During my testing, network latency was around ~350ms
.
So I had the idea of self-hosting the server. Looking at the project structure, there’s no docker-compose.yaml but there is a Dockerfile, which makes things easier!
For those who want to use it on an internal network or want to build their own server node, you can clone the source code and build your own Docker image.
gh repo clone ekzhang/sshx
cd sshx
docker build -t sshx:latest .
After building, I found there were two more issues waiting for me.
The first problem is that end-to-end encryption uses the browser’s crypto.subtle
interface, which will cause JS execution errors if conditions aren’t met:
crypto.subtle.importKey TypeError: Cannot read properties of undefined
After some searching, I found that to use the crypto.subtle
interface, one of the following conditions must be met:
- Run in a secure context, meaning the service must be provided over HTTPS
- Use HTTP to provide the service, but the domain name can only be localhost
Once I identified the problem, I used mkcert
to generate a self-signed certificate for the sshx.test
domain and used Traefik for reverse proxying. The specific docker-compose.yaml configuration is as follows:
services:
sshx:
image: betterde/sshx:latest
labels:
- traefik.enable=true
- traefik.http.services.sshx.loadbalancer.server.port=8051
- traefik.http.routers.sshx.tls=true
- traefik.http.routers.sshx.rule=Host(`sshx.test`)
- traefik.http.routers.sshx.service=sshx
- traefik.http.routers.sshx.entrypoints=http,https
restart: always
hostname: sshx
networks:
- traefik
command: ["./sshx-server", "--port", "8051", "--listen", "::", "--override-origin", "https://sshx.test"]
environment:
- SSHX_SECRET=mylOHBgeQYBnEGkMiY25Q4wEr7Fa7i33
- SSHX_REDIS_URL=redis://redis:6379
container_name: sshx
networks:
traefik:
external: true
Starting the server:
docker compose up -d
[+] Building 0.0s (0/0) docker:orbstack
[+] Running 1/0
✔ Container sshx Running
Starting the client:
sshx --server https://sshx.test
2023-11-30T07:16:31.704375Z ERROR sshx: transport error
Caused by:
0: error trying to connect: invalid peer certificate: UnknownIssuer
1: invalid peer certificate: UnknownIssuer
From the error message, it’s clear there’s a CA certificate issue. So I searched through the project repository’s Issues and found this Issue. But the author indicated there are no plans for this at the moment, so we’ll replace the CA ourselves and manually compile the client.
Use the following command to replace tls-webpki-roots
with tls-roots
in Cargo.toml
:
# linux
sed -i 's/tls-webpki-roots/tls-roots/g' ./Cargo.toml
# macOS is slightly different, requiring a backup file name after -i, we don't need a backup here, so it's ''
sed -i '' 's/tls-webpki-roots/tls-roots/g' ./Cargo.toml
Compiling the SSHX client:
cargo build --release --bin sshx
Updating crates.io index
Downloaded http-range-header v0.3.1
Downloaded itoa v1.0.9
Downloaded mime_guess v2.0.4
Downloaded matchit v0.7.2
Downloaded async-trait v0.1.73
Downloaded anstyle v1.0.2
Downloaded clap v4.4.2
Downloaded password-hash v0.5.0
Downloaded jobserver v0.1.26
Downloaded bitflags v2.4.0
Downloaded ciborium-io v0.2.1
Downloaded petgraph v0.6.4
Downloaded async-channel v1.9.0
Downloaded rustls-webpki v0.101.4
Downloaded tokio-tungstenite v0.20.0
Downloaded clap_builder v4.4.2
Downloaded pin-project v1.1.3
Downloaded cipher v0.4.4
Downloaded sha1_smol v1.0.0
Downloaded ctr v0.9.2
Downloaded axum-core v0.3.4
Downloaded blake2 v0.10.6
Downloaded proc-macro2 v1.0.66
Downloaded axum v0.6.20
Downloaded h2 v0.3.21
Downloaded syn v2.0.31
Downloaded zstd v0.12.4
Downloaded regex-automata v0.3.8
Downloaded encoding_rs v0.8.33
Downloaded which v4.4.2
Downloaded url v2.4.1
Downloaded zstd-sys v2.0.8+zstd.1.5.5
Downloaded unicase v2.7.0
Downloaded slab v0.4.9
Downloaded serde_path_to_error v0.1.14
Downloaded scopeguard v1.2.0
Downloaded quote v1.0.33
Downloaded pin-project-internal v1.1.3
Downloaded tokio v1.32.0
Downloaded nix v0.27.1
Downloaded regex-syntax v0.7.5
Downloaded tower-http v0.4.4
Downloaded regex v1.9.5
Downloaded aho-corasick v1.0.5
Downloaded serde_json v1.0.106
Downloaded webpki-roots v0.23.1
Downloaded rustls-webpki v0.100.3
Downloaded bytes v1.5.0
Downloaded socket2 v0.5.3
Downloaded serde v1.0.188
Downloaded ryu v1.0.15
Downloaded rustls v0.21.7
Downloaded security-framework v2.9.2
Downloaded unicode-ident v1.0.11
Downloaded concurrent-queue v2.2.0
Downloaded tempfile v3.8.0
Downloaded utf-8 v0.7.6
Downloaded sha1 v0.10.5
Downloaded either v1.9.0
Downloaded tonic-reflection v0.10.0
Downloaded prost-derive v0.12.0
Downloaded prost v0.12.0
Downloaded tonic-build v0.10.0
Downloaded prost-types v0.12.0
Downloaded tonic v0.10.0
Downloaded errno v0.3.3
Downloaded fastrand v2.0.0
Downloaded pin-project-lite v0.2.13
Downloaded serde_derive v1.0.188
Downloaded deadpool-redis v0.13.0
Downloaded clap_lex v0.5.1
Downloaded zstd-safe v6.0.6
Downloaded smallvec v1.11.0
Downloaded redis v0.23.3
Downloaded thiserror-impl v1.0.48
Downloaded inout v0.1.3
Downloaded ciborium-ll v0.2.1
Downloaded equivalent v1.0.1
Downloaded anstream v0.5.0
Downloaded sync_wrapper v0.1.2
Downloaded rustversion v1.0.14
Downloaded pkg-config v0.3.27
Downloaded log v0.4.20
Downloaded prettyplease v0.2.15
Downloaded security-framework-sys v2.9.1
Downloaded deadpool-runtime v0.1.3
Downloaded dashmap v5.5.3
Downloaded itertools v0.11.0
Downloaded thiserror v1.0.48
Downloaded num_cpus v1.16.0
Downloaded deadpool v0.10.0
Downloaded close_fds v0.3.2
Downloaded rustls-pemfile v1.0.3
Downloaded base64ct v1.6.0
Downloaded cpufeatures v0.2.9
Downloaded clap_derive v4.4.2
Downloaded argon2 v0.5.2
Downloaded home v0.5.5
Downloaded hyper v0.14.27
Downloaded memchr v2.6.3
Downloaded tungstenite v0.20.0
Downloaded combine v4.6.6
Downloaded ciborium v0.2.1
Downloaded prost-build v0.12.0
Downloaded httpdate v1.0.3
Downloaded anyhow v1.0.75
Downloaded cc v1.0.83
Downloaded event-listener v2.5.3
Downloaded base64 v0.21.4
Downloaded aes v0.8.3
Downloaded rustix v0.38.12
Downloaded 111 crates (10.1 MB) in 3.06s (largest was `encoding_rs` at 1.4 MB)
Compiling libc v0.2.147
Compiling proc-macro2 v1.0.66
Compiling unicode-ident v1.0.11
Compiling cfg-if v1.0.0
Compiling autocfg v1.1.0
Compiling serde v1.0.188
Compiling pin-project-lite v0.2.13
Compiling version_check v0.9.4
Compiling typenum v1.16.0
Compiling parking_lot_core v0.9.8
Compiling log v0.4.20
Compiling smallvec v1.11.0
Compiling futures-core v0.3.28
Compiling generic-array v0.14.7
Compiling lock_api v0.4.10
Compiling scopeguard v1.2.0
Compiling itoa v1.0.9
Compiling once_cell v1.18.0
Compiling anyhow v1.0.75
Compiling slab v0.4.9
Compiling futures-sink v0.3.28
Compiling futures-task v0.3.28
Compiling either v1.9.0
Compiling tracing-core v0.1.31
Compiling quote v1.0.33
Compiling futures-util v0.3.28
Compiling fnv v1.0.7
Compiling syn v2.0.31
Compiling itertools v0.11.0
Compiling subtle v2.5.0
Compiling pin-utils v0.1.0
Compiling rustversion v1.0.14
Compiling indexmap v1.9.3
Compiling rustix v0.38.12
Compiling num_cpus v1.16.0
Compiling parking_lot v0.12.1
Compiling signal-hook-registry v1.4.1
Compiling mio v0.8.8
Compiling socket2 v0.5.3
Compiling getrandom v0.2.10
Compiling crypto-common v0.1.6
Compiling jobserver v0.1.26
Compiling rand_core v0.6.4
Compiling block-buffer v0.10.4
Compiling httparse v1.8.0
Compiling cc v1.0.83
Compiling tinyvec_macros v0.1.1
Compiling digest v0.10.7
Compiling tinyvec v1.6.0
Compiling errno v0.3.3
Compiling bitflags v2.4.0
Compiling hashbrown v0.12.3
Compiling ppv-lite86 v0.2.17
Compiling percent-encoding v2.3.0
Compiling form_urlencoded v1.2.0
Compiling rand_chacha v0.3.1
Compiling cpufeatures v0.2.9
Compiling unicode-bidi v0.3.13
Compiling regex-syntax v0.7.5
Compiling untrusted v0.7.1
Compiling unicode-normalization v0.1.22
Compiling thiserror v1.0.48
Compiling equivalent v1.0.1
Compiling tower-service v0.3.2
Compiling heck v0.4.1
Compiling hashbrown v0.14.0
Compiling ring v0.16.20
Compiling bytes v1.5.0
Compiling prettyplease v0.2.15
Compiling futures-channel v0.3.28
Compiling idna v0.4.0
Compiling indexmap v2.0.0
Compiling rand v0.8.5
Compiling fastrand v2.0.0
Compiling home v0.5.5
Compiling regex-automata v0.3.8
Compiling async-trait v0.1.73
Compiling core-foundation-sys v0.8.4
Compiling fixedbitset v0.4.2
Compiling try-lock v0.2.0
Compiling tempfile v3.8.0
Compiling petgraph v0.6.4
Compiling want v0.3.1
Compiling url v2.4.1
Compiling which v4.4.2
Compiling axum-core v0.3.4
Compiling sha1 v0.10.5
Compiling socket2 v0.4.9
Compiling base64 v0.21.4
Compiling utf-8 v0.7.6
Compiling regex v1.9.5
Compiling ryu v1.0.15
Compiling serde_json v1.0.106
Compiling rustls v0.21.7
Compiling serde_derive v1.0.188
Compiling tokio-macros v2.1.0
Compiling tracing-attributes v0.1.26
Compiling futures-macro v0.3.28
Compiling prost-derive v0.12.0
Compiling thiserror-impl v1.0.48
Compiling pin-project-internal v1.1.3
Compiling multimap v0.8.3
Compiling data-encoding v2.4.0
Compiling httpdate v1.0.3
Compiling bitflags v1.3.2
Compiling byteorder v1.4.3
Compiling tower-layer v0.3.2
Compiling pin-project v1.1.3
Compiling tracing v0.1.37
Compiling core-foundation v0.9.3
Compiling security-framework-sys v2.9.1
Compiling prost v0.12.0
Compiling axum v0.6.20
Compiling mime v0.3.17
Compiling prost-types v0.12.0
Compiling utf8parse v0.2.1
Compiling security-framework v2.9.2
Compiling anstyle-parse v0.2.1
Compiling async-stream-impl v0.3.5
Compiling rustls-pemfile v1.0.3
Compiling inout v0.1.3
Compiling anstyle v1.0.2
Compiling colorchoice v1.0.0
Compiling matchit v0.7.2
Compiling lazy_static v1.4.0
Compiling anstyle-query v1.0.0
Compiling memchr v2.6.3
Compiling regex-syntax v0.6.29
Compiling prost-build v0.12.0
Compiling sync_wrapper v0.1.2
Compiling rustls-webpki v0.101.4
Compiling sct v0.7.0
Compiling tonic-build v0.10.0
Compiling anstream v0.5.0
Compiling sshx-core v0.2.1 (/Users/George/Develop/Rust/sshx/crates/sshx-core)
Compiling regex-automata v0.1.10
Compiling async-stream v0.3.5
Compiling rustls-native-certs v0.6.3
Compiling cipher v0.4.4
Compiling base64ct v1.6.0
Compiling clap_lex v0.5.1
Compiling serde_path_to_error v0.1.14
Compiling serde_urlencoded v0.7.1
Compiling strsim v0.10.0
Compiling tokio v1.32.0
Compiling http v0.2.9
Compiling http-body v0.4.5
Compiling tungstenite v0.20.0
Compiling overload v0.1.1
Compiling nu-ansi-term v0.46.0
Compiling clap_builder v4.4.2
Compiling password-hash v0.5.0
Compiling tokio-util v0.7.8
Compiling h2 v0.3.21
Compiling tower v0.4.13
Compiling tokio-io-timeout v1.2.0
Compiling tokio-tungstenite v0.20.0
Compiling tokio-stream v0.1.14
Compiling tokio-rustls v0.24.1
Compiling matchers v0.1.0
Compiling tracing-log v0.1.3
Compiling sharded-slab v0.1.4
Compiling clap_derive v4.4.2
Compiling blake2 v0.10.6
Compiling thread_local v1.1.7
Compiling tracing-subscriber v0.3.17
Compiling nix v0.27.1
Compiling argon2 v0.5.2
Compiling aes v0.8.3
Compiling ctr v0.9.2
Compiling close_fds v0.3.2
Compiling encoding_rs v0.8.33
Compiling ansi_term v0.12.1
Compiling clap v4.4.2
Compiling hyper v0.14.27
Compiling hyper-timeout v0.4.1
Compiling tonic v0.10.0
Compiling sshx v0.2.1 (/Users/George/Develop/Rust/sshx/crates/sshx)
Finished release [optimized] target(s) in 35.27s
Place the compiled client executable in the /usr/local/bin
directory and start it:
sudo mv target/release/sshx /usr/local/bin/
sshx --server https://sshx.test
sshx v0.2.1
➜ Link: https://sshx.test/s/ynl7pSGppb#MYnh4yeL3LVdTn
➜ Shell: /bin/zsh
After opening the link in a browser, you can see the network latency is 4ms, which significantly improves the user experience:
For internal use, just have people who need remote assistance download your packaged client and execute the command!
Conclusion⌗
In the past, I’ve often helped others debug CLI errors, and everyone has different habits, such as the screens and keyboards they use. SSHX avoids these problems.
It also avoids some awkward situations, such as:
- Seeing content that the other person shouldn’t see
- Requiring the other person to input passwords multiple times during operations
With SSHX, communication costs are reduced and collaboration efficiency is improved!
I hope this is helpful, Happy hacking…