|
3 years ago | |
---|---|---|
wsfwd | 3 years ago | |
.gitignore | 4 years ago | |
Makefile | 4 years ago | |
README.md | 3 years ago | |
requirements.txt | 4 years ago | |
setup.py | 3 years ago |
WSFWD is a protocol for authentication (optional) and forwarding
command data over a WebSocket. This originally is for simple stdin
and stdout forwarding, for running a program like sshd -i
to bypass
port blocking, or allow more custom routing and execution.
It is designed so that in the future, it could support forwarding stderr separately, but also out of band messages, such as window change information, so that a full tty could be forwarded over the connection. As transporting a protocol like ssh does all of this for you, it is unlikely to be expanded to support it.
Included is a sample program that can be used to forward stream connections to one of the specified hosts and port.
First install the dependencies. Note that often it’s best to install it into a virtual environmentvenv, so, create one:
python -m venv dirname
Where dirname will be a directory that will be created w/ the virtual environment. Start the virtual environment (if you’re using bash/zsh, other shells are documented in the link above):
source ./dirname/bin/activate
Install wsfwd:
pip install git+https://www.funkthat.com/gitea/jmg/wsfwd
Run the server (for example to forward to the local machine’s sshd):
wsfwd serve 127.0.0.1:22
Multiple ip:port
maybe specified on the command to allow forwarding
to multiple hosts.
This will start up a webserver. It uses hypercorn, so, all the
hypercorn command line arguments may be specified via the --hypercorn
argument. For example, to bind only to 127.0.0.1, you can run:
wsfwd serve --hypercorn '--bind 127.0.0.1' 127.0.0.1:22
Once that has been setup, the client can be used to connect to the specified server:
wsfwd connect http://127.0.0.1/connect 127.0.0.1:22
This is most useful to be specified to ProxyCommand in your .ssh/config file:
Host localhost
ProxyCommand /path/to/dirname/bin/wsfwd connect http://127.0.0.1:8000/connect 127.0.0.1:22
There are many ways to use it. Another option could also be to launch it via inetd to transparently pass normal ssh sessions to remote hosts.
All WebSocket messages much be treated as binary. This is for simplicity and speed when doing large binary transfers. The format of the messages are:
The chan byte
value determines the meaning of payload.
If chan byte
is zero (aka 00), then the payload is a UTF-8 encoded
JSON message that contains a command, for example, an authentication
request or command to execute.
Other values for chan byte
are dynamically allocated based upon the
commands sent.
For commands, the JSON object MUST have a key of cmd, and the value is the command to execute. Each cmd that is sent MUST be acknowledged with an object that has a key of resp, and a value that is the same as the command. If the command fails, the response object MUST have a key of error, and the value MUST be a string containing an error message.
If the response to a command could be confused, it is expected that parts of command request be included in the response. In other cases, such as auth, it is expected that the auth message NOT be included, and that the response to a successful auth message is simply: { ‘resp’: ‘auth’ }
The following is a typical command flow. Failure messages are included in-line for additional context.
Auth message (optional):
{ 'cmd': 'auth', 'auth': { 'bearer': <token> } }
Error message in case of invalid auth:
{ 'resp': 'auth', 'error': 'Invalid auth' }
Command to execute a program:
{ 'cmd': 'exec', 'args': [ ... ], 'stdin': <chan>, 'stdout': <chan> }
Error if unable to exec the requested program:
{ 'resp': 'exec', 'error': 'Unable to exec', ... }, 'args': [...], [...] }
Success:
{ 'resp': 'exec', 'args': [...], [...] }
Here, messages on the stdin/stdout channels are exchanged.
Close stdin or stdout or other channel:
{ 'cmd': 'chanclose', 'chan': <chan> }
Success:
{ 'resp': 'chanclose', 'chan': <chan> }
Once stdin is closed, the server side will wait for stdout to be closed by the program, send the proper chanclose command for it, and then wait for the program to exit, and return the exit code.
{ 'cmd': 'exit', 'code': <code> }
Currently not implemented, but if stdin is closed by the program, the server may send a chanclose preemptively. The server will discard any additional data, and it is an error to send additional data on the channel after the client responds w/ a resp: chanclose acknowledgment.
These are the target client and server implementations that WSFWD is designed to work with. It should easily be able to be made to work with other implementations.
FastAPI uses starlette: https://www.starlette.io/websockets/