2023-02-11 12:14:30 -06:00
|
|
|
|
# `r n s h` Shell over Reticulum
|
2023-02-12 01:26:57 -06:00
|
|
|
|
[![CI](https://github.com/acehoss/rnsh/actions/workflows/python-package.yml/badge.svg)](https://github.com/acehoss/rnsh/actions/workflows/python-package.yml)
|
|
|
|
|
[![Release](https://github.com/acehoss/rnsh/actions/workflows/python-publish.yml/badge.svg)](https://github.com/acehoss/rnsh/actions/workflows/python-publish.yml)
|
2023-02-13 14:32:38 -06:00
|
|
|
|
[![PyPI version](https://badge.fury.io/py/rnsh.svg)](https://badge.fury.io/py/rnsh)
|
2023-02-13 07:36:28 -06:00
|
|
|
|
![PyPI - Downloads](https://img.shields.io/pypi/dw/rnsh?color=informational&label=Installs&logo=pypi)
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-10 22:34:32 -06:00
|
|
|
|
`rnsh` is a utility written in Python that facilitates shell
|
2023-02-11 12:43:24 -06:00
|
|
|
|
sessions over [Reticulum](https://reticulum.network) networks.
|
2023-02-11 13:24:37 -06:00
|
|
|
|
It is based on the `rnx` utility that ships with Reticulum and
|
2023-02-12 23:15:51 -06:00
|
|
|
|
aims to provide a similar experience to SSH.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-14 15:42:55 -06:00
|
|
|
|
## Contents
|
|
|
|
|
|
|
|
|
|
- [Alpha Disclaimer](#reminder--alpha-software)
|
|
|
|
|
- [Recent Changes](#recent-changes)
|
|
|
|
|
- [Quickstart](#quickstart)
|
|
|
|
|
- [Options](#options)
|
|
|
|
|
- [How it works](#how-it-works)
|
|
|
|
|
- [Roadmap](#roadmap)
|
|
|
|
|
- [Active TODO](#todo)
|
|
|
|
|
|
|
|
|
|
### Reminder: Alpha Software
|
2023-02-14 05:31:11 -06:00
|
|
|
|
These early versions will be buggy. There will sometimes be major
|
|
|
|
|
breaking changes to the command line parameters between releases.
|
|
|
|
|
There will sometimes be breaking changes in the protocol between
|
|
|
|
|
releases. Use at your own peril!
|
|
|
|
|
|
|
|
|
|
## Recent Changes
|
2023-02-22 19:58:03 -06:00
|
|
|
|
### v0.0.12
|
|
|
|
|
- Remove service name from RNS destination aspects. Service name
|
|
|
|
|
now selects a suffix for the identity file and should only be
|
|
|
|
|
supplied on the listener. The initiator only needs the destination
|
|
|
|
|
hash of the listener to connect.
|
|
|
|
|
- Show a spinner during link establishment on tty sessions
|
|
|
|
|
- Attempt to catch and beautify exceptions on initiator
|
|
|
|
|
|
2023-02-19 06:51:01 -06:00
|
|
|
|
### v0.0.11
|
|
|
|
|
- Event loop bursting improves throughput and CPU utilization on
|
|
|
|
|
both listener and initiator.
|
|
|
|
|
- Packet retries use RNS resend feature to prevent duplicate
|
|
|
|
|
packets.
|
|
|
|
|
|
2023-02-18 18:40:44 -06:00
|
|
|
|
### v0.0.10
|
|
|
|
|
- Rate limit window change events to prevent saturation of transport
|
|
|
|
|
- Tweaked some loop timers to improve CPU utilization
|
|
|
|
|
|
|
|
|
|
### v0.0.9
|
|
|
|
|
- Switch to a new packet-based protocol
|
|
|
|
|
- Bug fixes and dependency updates
|
|
|
|
|
|
2023-02-10 18:32:07 -06:00
|
|
|
|
## Quickstart
|
|
|
|
|
|
2023-02-11 12:11:51 -06:00
|
|
|
|
Tested (thus far) on Python 3.11 macOS 13.1 ARM64. Should
|
|
|
|
|
run on Python 3.6+ on Linux or Unix. WSL probably works.
|
|
|
|
|
Cygwin might work, too.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-10 22:34:32 -06:00
|
|
|
|
- Activate a virtualenv
|
|
|
|
|
- `pip3 install rnsh`
|
|
|
|
|
- Or from a `whl` release, `pip3 install /path/to/rnsh-0.0.1-py3-none-any.whl`
|
|
|
|
|
- Configure Reticulum interfaces, check with `rnstatus`
|
|
|
|
|
- Ready to run `rnsh`. The options are shown below.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-11 12:43:24 -06:00
|
|
|
|
### Example: Shell server
|
|
|
|
|
#### Setup
|
|
|
|
|
Before running the listener or initiator, you'll need to get the
|
|
|
|
|
listener destination hash and the initiator identity hash.
|
|
|
|
|
```shell
|
|
|
|
|
# On listener
|
|
|
|
|
rnsh -l -p
|
|
|
|
|
|
|
|
|
|
# On initiator
|
|
|
|
|
rnsh -p
|
|
|
|
|
```
|
2023-02-22 19:58:03 -06:00
|
|
|
|
Note: service name no longer is supplied on initiator. The destination
|
|
|
|
|
hash encapsulates this information.
|
2023-02-11 12:43:24 -06:00
|
|
|
|
|
|
|
|
|
#### Listener
|
|
|
|
|
- Listening for default service name ("default").
|
|
|
|
|
- Using user's default Reticulum config dir (~/.reticulum).
|
|
|
|
|
- Using default identity ($RNSCONFIGDIR/storage/identities/rnsh).
|
|
|
|
|
- Allowing remote identity `6d47805065fa470852cf1b1ef417a1ac` to connect.
|
|
|
|
|
- Launching `/bin/zsh` on authorized connect.
|
|
|
|
|
```shell
|
|
|
|
|
rnsh -l -a 6d47805065fa470852cf1b1ef417a1ac -- /bin/zsh
|
|
|
|
|
```
|
|
|
|
|
#### Initiator
|
|
|
|
|
- Connecting to default service name ("default").
|
|
|
|
|
- Using user's default Reticulum config dir (~/.reticulum).
|
|
|
|
|
- Using default identity ($RNSCONFIGDIR/storage/identities/rnsh).
|
|
|
|
|
- Connecting to destination `a5f72aefc2cb3cdba648f73f77c4e887`
|
|
|
|
|
```shell
|
|
|
|
|
rnsh a5f72aefc2cb3cdba648f73f77c4e887
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Options
|
2023-02-10 18:32:07 -06:00
|
|
|
|
```
|
|
|
|
|
Usage:
|
2023-02-22 19:58:03 -06:00
|
|
|
|
rnsh -l [-c <configdir>] [-i <identityfile> | -s <service_name>] [-v... | -q...] -p
|
|
|
|
|
rnsh -l [-c <configdir>] [-i <identityfile> | -s <service_name>] [-v... | -q...]
|
|
|
|
|
[-b <period>] (-n | -a <identity_hash> [-a <identity_hash>] ...) [-A | -C]
|
|
|
|
|
[[--] <program> [<arg> ...]]
|
|
|
|
|
rnsh [-c <configdir>] [-i <identityfile>] [-v... | -q...] -p
|
|
|
|
|
rnsh [-c <configdir>] [-i <identityfile>] [-v... | -q...] [-N] [-m] [-w <timeout>]
|
|
|
|
|
<destination_hash> [[--] <program> [<arg> ...]]
|
2023-02-10 18:32:07 -06:00
|
|
|
|
rnsh -h
|
|
|
|
|
rnsh --version
|
|
|
|
|
|
|
|
|
|
Options:
|
2023-02-22 19:58:03 -06:00
|
|
|
|
-c DIR --config DIR Alternate Reticulum config directory to use
|
2023-02-14 06:18:22 -06:00
|
|
|
|
-i FILE --identity FILE Specific identity file to use
|
2023-02-22 19:58:03 -06:00
|
|
|
|
-s NAME --service NAME Service name for identity file if not default
|
2023-02-14 06:18:22 -06:00
|
|
|
|
-p --print-identity Print identity information and exit
|
|
|
|
|
-l --listen Listen (server) mode. If supplied, <program> <arg>...will
|
|
|
|
|
be used as the command line when the initiator does not
|
|
|
|
|
provide one or when remote command is disabled. If
|
|
|
|
|
<program> is not supplied, the default shell of the
|
|
|
|
|
user rnsh is running under will be used.
|
|
|
|
|
-b --announce PERIOD Announce on startup and every PERIOD seconds
|
|
|
|
|
Specify 0 for PERIOD to announce on startup only.
|
|
|
|
|
-a HASH --allowed HASH Specify identities allowed to connect
|
|
|
|
|
-n --no-auth Disable authentication
|
|
|
|
|
-N --no-id Disable identify on connect
|
|
|
|
|
-A --remote-command-as-args Concatenate remote command to argument list of <program>/shell
|
|
|
|
|
-C --no-remote-command Disable executing command line from remote
|
|
|
|
|
-m --mirror Client returns with code of remote process
|
|
|
|
|
-w TIME --timeout TIME Specify client connect and request timeout in seconds
|
|
|
|
|
-q --quiet Increase quietness (move level up), multiple increases effect
|
|
|
|
|
DEFAULT LOGGING LEVEL
|
|
|
|
|
CRITICAL (silent)
|
|
|
|
|
Initiator -> ERROR
|
|
|
|
|
WARNING
|
|
|
|
|
Listener -> INFO
|
|
|
|
|
DEBUG (insane)
|
|
|
|
|
-v --verbose Increase verbosity (move level down), multiple increases effect
|
|
|
|
|
--version Show version
|
|
|
|
|
-h --help Show this help
|
2023-02-10 18:32:07 -06:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## How it works
|
2023-02-10 22:34:32 -06:00
|
|
|
|
### Listeners
|
|
|
|
|
Listener instances are the servers. Each listener is configured
|
|
|
|
|
with an RNS identity, and a service name. Together, RNS makes
|
|
|
|
|
these into a destination hash that can be used to connect to
|
|
|
|
|
your listener.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-22 19:58:03 -06:00
|
|
|
|
Each listener must use a unique identity. The `-s` parameter
|
|
|
|
|
can be used to specify a service name, which creates a unique
|
|
|
|
|
identity file.
|
|
|
|
|
|
|
|
|
|
Listeners can be configured with a command line to run on
|
|
|
|
|
connect. Initiators can supply a command line as well. There
|
|
|
|
|
are several different options for the way the command line
|
|
|
|
|
is handled:
|
|
|
|
|
|
|
|
|
|
- `-C` no initiator command line is allowed; the connection will
|
|
|
|
|
be terminated if one is supplied.
|
|
|
|
|
- `-A` initiator-supplied command line is appended to listener-
|
|
|
|
|
configured command line
|
|
|
|
|
- With neither of these options, the listener will use the first
|
|
|
|
|
valid command line from this list:
|
|
|
|
|
1. Initiator-supplied command line
|
|
|
|
|
2. Listener command line argument
|
|
|
|
|
3. Default shell of user listener is running under
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If the `-n` option is not set on the listener, the initiator
|
|
|
|
|
is required to identify before starting a command. The program
|
|
|
|
|
will be started with the initiator's identity hash string is set
|
|
|
|
|
in the environment variable `RNS_REMOTE_IDENTITY`.
|
2023-02-10 22:34:32 -06:00
|
|
|
|
|
|
|
|
|
Listeners are set up using the `-l` flag.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-10 22:34:32 -06:00
|
|
|
|
### Initiators
|
|
|
|
|
Initiators are the clients. Each initiator has an identity
|
|
|
|
|
hash which is used as an authentication mechanism on Reticulum.
|
|
|
|
|
You'll need this value to configure the listener to allow
|
|
|
|
|
your connection. It is possible to run the server without
|
|
|
|
|
authentication, but hopefully it's obvious that this is an
|
|
|
|
|
advanced use case.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-10 22:34:32 -06:00
|
|
|
|
To get the identity hash, use the `-p` flag.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-10 22:34:32 -06:00
|
|
|
|
With the initiator identity set up in the listener command
|
|
|
|
|
line, and with the listener identity copied (you'll need to
|
|
|
|
|
do `-p` on the listener side, too), you can run the
|
|
|
|
|
initiator.
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
2023-02-10 22:34:32 -06:00
|
|
|
|
I recommend staying pretty vanilla to start with and
|
|
|
|
|
trying `/bin/zsh` or whatever your favorite shell is these
|
|
|
|
|
days. The shell should start in login mode. Ideally it
|
|
|
|
|
works just like an `ssh` shell session.
|
2023-02-11 12:43:24 -06:00
|
|
|
|
|
2023-02-23 11:05:17 -06:00
|
|
|
|
## Protocol
|
|
|
|
|
The protocol is build on top of the Reticulum `Packet` API.
|
|
|
|
|
Application software sends and receives `Message` objects,
|
|
|
|
|
which are encapsulated by `Packet` objects. Messages are
|
|
|
|
|
(currently) sent one per packet, and only one packet is
|
|
|
|
|
sent at a time (per link). The next packet is not sent until
|
|
|
|
|
the receiver proves the outstanding packet.
|
|
|
|
|
|
|
|
|
|
A future update will work to allow a sliding window of
|
|
|
|
|
outstanding packets to improve channel utilization.
|
|
|
|
|
|
|
|
|
|
### Session Establishment
|
|
|
|
|
1. Initiator establishes link. Listener session enters state
|
|
|
|
|
`LSSTATE_WAIT_IDENT`, or `LSSTATE_WAIT_VERS` if running
|
|
|
|
|
with `--no-auth` option.
|
|
|
|
|
|
|
|
|
|
2. Initiator identifies on link if not using `--no-id`.
|
|
|
|
|
- If using `--allowed-hash`, listener validates identity
|
|
|
|
|
against configuration and if no match, sends a
|
|
|
|
|
protocol error message and tears down link after prune
|
|
|
|
|
timer.
|
|
|
|
|
3. Initiator transmits a `VersionInformationMessage`, which
|
|
|
|
|
is evaluated by the server for compatibility. If
|
|
|
|
|
incompatible, a protocol error is sent.
|
|
|
|
|
4. Listener responds with a `VersionInfoMessage` and enters
|
|
|
|
|
state `LSSTATE_WAIT_CMD`
|
|
|
|
|
5. Initiator evaluates the listener's version information
|
|
|
|
|
for compatibility and if incompatible, tears down link.
|
|
|
|
|
6. Initiator sends an `ExecuteCommandMessage` (which could
|
|
|
|
|
be an empty command) and enters the session event loop.
|
|
|
|
|
7. Listener evaluates the command message against the
|
|
|
|
|
configured options such as `-A` or `-C` and responds
|
|
|
|
|
with a protocol error if not allowed.
|
|
|
|
|
8. Listener starts the program. If success, the listener
|
|
|
|
|
enters the session event loop. If failure, responds
|
|
|
|
|
with a `CommandExitedMessage`.
|
|
|
|
|
|
|
|
|
|
### Session Event Loop
|
|
|
|
|
##### Listener state `LSSTATE_RUNNING`
|
|
|
|
|
Process messages received from initiator.
|
|
|
|
|
- `WindowSizeMessage`: set window size on child tty if appropriate
|
|
|
|
|
- `StreamDataMessage`: binary data stream for child process;
|
|
|
|
|
streams ids 0, 1, 2 = stdin, stdout, stderr
|
|
|
|
|
- `NoopMessage`: no operation - listener replies with `NoopMessage`
|
|
|
|
|
- When link is torn down, child process is terminated if running and
|
|
|
|
|
session destroyed
|
|
|
|
|
- If command terminates, a `CommandExitedMessage` is sent and session
|
|
|
|
|
is pruned after an idle timeout.
|
|
|
|
|
##### Initiator state `ISSTATE_RUNNING`
|
|
|
|
|
Process messages received from listener.
|
|
|
|
|
- `ErrorMessage`: print error, terminate link, and exit
|
|
|
|
|
- `StreamDataMessage`: binary stream information;
|
|
|
|
|
streams ids 0, 1, 2 = stdin, stdout, stderr
|
|
|
|
|
- `CommandExitedMessage`: remote command exited
|
|
|
|
|
- If link is torn down unexpectedly, print message and exit
|
|
|
|
|
|
2023-02-10 18:32:07 -06:00
|
|
|
|
|
|
|
|
|
## Roadmap
|
|
|
|
|
1. Plan a better roadmap
|
|
|
|
|
2. ?
|
|
|
|
|
3. Keep my day job
|
|
|
|
|
|
|
|
|
|
## TODO
|
|
|
|
|
- [X] ~~Initial version~~
|
2023-02-10 22:35:50 -06:00
|
|
|
|
- [X] ~~Pip package with command-line utility support~~
|
2023-02-11 12:11:51 -06:00
|
|
|
|
- [X] ~~Publish to PyPI~~
|
2023-02-12 23:15:51 -06:00
|
|
|
|
- [X] ~~Improve signal handling~~
|
2023-02-13 14:32:38 -06:00
|
|
|
|
- [X] ~~Make it scriptable (currently requires a tty)~~
|
2023-02-14 15:42:55 -06:00
|
|
|
|
- [X] ~~Protocol improvements (throughput!)~~
|
|
|
|
|
- [X] ~~Documentation improvements~~
|
2023-02-23 11:05:17 -06:00
|
|
|
|
- [X] ~~Fix issues with running `rnsh` in a binary pipeline, i.e.
|
|
|
|
|
piping the output of `tar` over `rsh`.~~
|
2023-02-14 15:42:55 -06:00
|
|
|
|
- [ ] Test on several platforms
|
|
|
|
|
- [ ] Fix issues that come up with testing
|
2023-02-23 11:05:17 -06:00
|
|
|
|
- [ ] v0.1.0 beta
|
2023-02-14 15:42:55 -06:00
|
|
|
|
- [ ] Test and fix more issues
|
2023-02-23 11:05:17 -06:00
|
|
|
|
- [ ] More betas
|
2023-02-14 15:42:55 -06:00
|
|
|
|
- [ ] Enhancement Ideas
|
2023-02-23 11:05:17 -06:00
|
|
|
|
- [ ] `authorized_keys` mode similar to SSH to allow one listener
|
|
|
|
|
process to serve multiple users
|
2023-02-14 15:42:55 -06:00
|
|
|
|
- [ ] Git over `rnsh` (git remote helper)
|
|
|
|
|
- [ ] Sliding window acknowledgements for improved throughput
|
2023-02-23 11:05:17 -06:00
|
|
|
|
- [ ] v1.0 someday probably maybe
|
2023-02-14 15:42:55 -06:00
|
|
|
|
|
|
|
|
|
## Miscellaneous
|
|
|
|
|
|
2023-02-23 11:05:17 -06:00
|
|
|
|
By piping into/out of `rnsh`, it is possible to transfer
|
2023-02-14 15:42:55 -06:00
|
|
|
|
files using the same method discussed in
|
|
|
|
|
[this article](https://cromwell-intl.com/open-source/tar-and-ssh.html).
|
2023-02-23 11:05:17 -06:00
|
|
|
|
It's not terribly fast currently, due to the round-trip rule
|
|
|
|
|
enforced by the protocol. Sliding window acknowledgements will
|
|
|
|
|
speed this up significantly.
|