Remote command append mode (-A) for listener

This commit is contained in:
Aaron Heise 2023-02-14 06:18:22 -06:00
parent 21b80819c6
commit d4cb31e220
4 changed files with 88 additions and 66 deletions

View file

@ -24,6 +24,12 @@ There will sometimes be breaking changes in the protocol between
releases. Use at your own peril! releases. Use at your own peril!
## Recent Changes ## Recent Changes
### v0.0.7
Added `-A` command line option. This listener option causes the
remote command line to be appended to the arguments list of the
launched program. This allows the listener to jail connections
to a particular executable while still allowing parameters.
### v0.0.6 ### v0.0.6
Minor improvements in transport efficiency Minor improvements in transport efficiency
@ -92,7 +98,7 @@ Usage:
rnsh [--config <configdir>] [-i <identityfile>] [-s <service_name>] [-l] -p rnsh [--config <configdir>] [-i <identityfile>] [-s <service_name>] [-l] -p
rnsh -l [--config <configfile>] [-i <identityfile>] [-s <service_name>] rnsh -l [--config <configfile>] [-i <identityfile>] [-s <service_name>]
[-v... | -q...] [-b <period>] (-n | -a <identity_hash> [-a <identity_hash>] ...) [-v... | -q...] [-b <period>] (-n | -a <identity_hash> [-a <identity_hash>] ...)
[-C] [[--] <program> [<arg> ...]] [-A | -C] [[--] <program> [<arg> ...]]
rnsh [--config <configfile>] [-i <identityfile>] [-s <service_name>] rnsh [--config <configfile>] [-i <identityfile>] [-s <service_name>]
[-v... | -q...] [-N] [-m] [-w <timeout>] <destination_hash> [-v... | -q...] [-N] [-m] [-w <timeout>] <destination_hash>
[[--] <program> [<arg> ...]] [[--] <program> [<arg> ...]]
@ -100,33 +106,34 @@ Usage:
rnsh --version rnsh --version
Options: Options:
--config DIR Alternate Reticulum config directory to use --config DIR Alternate Reticulum config directory to use
-i FILE --identity FILE Specific identity file to use -i FILE --identity FILE Specific identity file to use
-s NAME --service NAME Listen on/connect to specific service name if not default -s NAME --service NAME Listen on/connect to specific service name if not default
-p --print-identity Print identity information and exit -p --print-identity Print identity information and exit
-l --listen Listen (server) mode. If supplied, <program> <arg>...will -l --listen Listen (server) mode. If supplied, <program> <arg>...will
be used as the command line when the initiator does not be used as the command line when the initiator does not
provide one or when remote command is disabled. If provide one or when remote command is disabled. If
<program> is not supplied, the default shell of the <program> is not supplied, the default shell of the
user rnsh is running under will be used. user rnsh is running under will be used.
-b --announce PERIOD Announce on startup and every PERIOD seconds -b --announce PERIOD Announce on startup and every PERIOD seconds
Specify 0 for PERIOD to announce on startup only. Specify 0 for PERIOD to announce on startup only.
-a HASH --allowed HASH Specify identities allowed to connect -a HASH --allowed HASH Specify identities allowed to connect
-n --no-auth Disable authentication -n --no-auth Disable authentication
-N --no-id Disable identify on connect -N --no-id Disable identify on connect
-C --no-remote-command Disable executing command line from remote -A --remote-command-as-args Concatenate remote command to argument list of <program>/shell
-m --mirror Client returns with code of remote process -C --no-remote-command Disable executing command line from remote
-w TIME --timeout TIME Specify client connect and request timeout in seconds -m --mirror Client returns with code of remote process
-q --quiet Increase quietness (move level up), multiple increases effect -w TIME --timeout TIME Specify client connect and request timeout in seconds
DEFAULT LOGGING LEVEL -q --quiet Increase quietness (move level up), multiple increases effect
CRITICAL (silent) DEFAULT LOGGING LEVEL
Initiator -> ERROR CRITICAL (silent)
WARNING Initiator -> ERROR
Listener -> INFO WARNING
DEBUG (insane) Listener -> INFO
-v --verbose Increase verbosity (move level down), multiple increases effect DEBUG (insane)
--version Show version -v --verbose Increase verbosity (move level down), multiple increases effect
-h --help Show this help --version Show version
-h --help Show this help
``` ```
## How it works ## How it works

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "rnsh" name = "rnsh"
version = "0.0.6" version = "0.0.7"
description = "Shell over Reticulum" description = "Shell over Reticulum"
authors = ["acehoss <acehoss@acehoss.net>"] authors = ["acehoss <acehoss@acehoss.net>"]
license = "MIT" license = "MIT"

View file

@ -21,7 +21,7 @@ Usage:
rnsh [--config <configdir>] [-i <identityfile>] [-s <service_name>] [-l] -p rnsh [--config <configdir>] [-i <identityfile>] [-s <service_name>] [-l] -p
rnsh -l [--config <configfile>] [-i <identityfile>] [-s <service_name>] rnsh -l [--config <configfile>] [-i <identityfile>] [-s <service_name>]
[-v... | -q...] [-b <period>] (-n | -a <identity_hash> [-a <identity_hash>] ...) [-v... | -q...] [-b <period>] (-n | -a <identity_hash> [-a <identity_hash>] ...)
[-C] [[--] <program> [<arg> ...]] [-A | -C] [[--] <program> [<arg> ...]]
rnsh [--config <configfile>] [-i <identityfile>] [-s <service_name>] rnsh [--config <configfile>] [-i <identityfile>] [-s <service_name>]
[-v... | -q...] [-N] [-m] [-w <timeout>] <destination_hash> [-v... | -q...] [-N] [-m] [-w <timeout>] <destination_hash>
[[--] <program> [<arg> ...]] [[--] <program> [<arg> ...]]
@ -29,33 +29,34 @@ Usage:
rnsh --version rnsh --version
Options: Options:
--config DIR Alternate Reticulum config directory to use --config DIR Alternate Reticulum config directory to use
-i FILE --identity FILE Specific identity file to use -i FILE --identity FILE Specific identity file to use
-s NAME --service NAME Listen on/connect to specific service name if not default -s NAME --service NAME Listen on/connect to specific service name if not default
-p --print-identity Print identity information and exit -p --print-identity Print identity information and exit
-l --listen Listen (server) mode. If supplied, <program> <arg>...will -l --listen Listen (server) mode. If supplied, <program> <arg>...will
be used as the command line when the initiator does not be used as the command line when the initiator does not
provide one or when remote command is disabled. If provide one or when remote command is disabled. If
<program> is not supplied, the default shell of the <program> is not supplied, the default shell of the
user rnsh is running under will be used. user rnsh is running under will be used.
-b --announce PERIOD Announce on startup and every PERIOD seconds -b --announce PERIOD Announce on startup and every PERIOD seconds
Specify 0 for PERIOD to announce on startup only. Specify 0 for PERIOD to announce on startup only.
-a HASH --allowed HASH Specify identities allowed to connect -a HASH --allowed HASH Specify identities allowed to connect
-n --no-auth Disable authentication -n --no-auth Disable authentication
-N --no-id Disable identify on connect -N --no-id Disable identify on connect
-C --no-remote-command Disable executing command line from remote -A --remote-command-as-args Concatenate remote command to argument list of <program>/shell
-m --mirror Client returns with code of remote process -C --no-remote-command Disable executing command line from remote
-w TIME --timeout TIME Specify client connect and request timeout in seconds -m --mirror Client returns with code of remote process
-q --quiet Increase quietness (move level up), multiple increases effect -w TIME --timeout TIME Specify client connect and request timeout in seconds
DEFAULT LOGGING LEVEL -q --quiet Increase quietness (move level up), multiple increases effect
CRITICAL (silent) DEFAULT LOGGING LEVEL
Initiator -> ERROR CRITICAL (silent)
WARNING Initiator -> ERROR
Listener -> INFO WARNING
DEBUG (insane) Listener -> INFO
-v --verbose Increase verbosity (move level down), multiple increases effect DEBUG (insane)
--version Show version -v --verbose Increase verbosity (move level down), multiple increases effect
-h --help Show this help --version Show version
-h --help Show this help
''' '''
@ -64,7 +65,8 @@ class Args:
global usage global usage
try: try:
argv, program_args = _split_array_at(argv, "--") argv, program_args = _split_array_at(argv, "--")
if len(program_args) > 0: # need to add first arg after -- back onto argv for docopts, but only for listener
if len(program_args) > 0 and next(filter(lambda a: a == "-l" or a == "--listen", argv), None) is not None:
argv.append(program_args[0]) argv.append(program_args[0])
self.program_args = program_args[1:] self.program_args = program_args[1:]
@ -88,6 +90,7 @@ class Args:
self.valid = False self.valid = False
self.no_auth = args.get("--no-auth", None) or False self.no_auth = args.get("--no-auth", None) or False
self.allowed = args.get("--allowed", None) or [] self.allowed = args.get("--allowed", None) or []
self.remote_cmd_as_args = args.get("--remote-command-as-args", None) or False
self.no_remote_cmd = args.get("--no-remote-command", None) or False self.no_remote_cmd = args.get("--no-remote-command", None) or False
self.program = args.get("<program>", None) self.program = args.get("<program>", None)
self.program_args = args.get("<arg>", None) or [] self.program_args = args.get("<arg>", None) or []

View file

@ -69,6 +69,7 @@ _retry_timer: retry.RetryThread | None = None
_destination: RNS.Destination | None = None _destination: RNS.Destination | None = None
_loop: asyncio.AbstractEventLoop | None = None _loop: asyncio.AbstractEventLoop | None = None
_no_remote_command = True _no_remote_command = True
_remote_cmd_as_args = False
async def _check_finished(timeout: float = 0): async def _check_finished(timeout: float = 0):
@ -115,8 +116,9 @@ def _print_identity(configdir, identitypath, service_name, include_destination:
async def _listen(configdir, command, identitypath=None, service_name="default", verbosity=0, quietness=0, allowed=None, async def _listen(configdir, command, identitypath=None, service_name="default", verbosity=0, quietness=0, allowed=None,
disable_auth=None, announce_period=900, no_remote_command=True): disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False):
global _identity, _allow_all, _allowed_identity_hashes, _reticulum, _cmd, _destination, _no_remote_command global _identity, _allow_all, _allowed_identity_hashes, _reticulum, _cmd, _destination, _no_remote_command
global _remote_cmd_as_args
log = _get_logger("_listen") log = _get_logger("_listen")
@ -128,15 +130,21 @@ async def _listen(configdir, command, identitypath=None, service_name="default",
_cmd = command _cmd = command
if _cmd is None or len(_cmd) == 0: if _cmd is None or len(_cmd) == 0:
shell = pwd.getpwuid(os.getuid()).pw_shell shell = None
try:
shell = pwd.getpwuid(os.getuid()).pw_shell
except Exception as e:
log.error(f"Error looking up shell: {e}")
log.info(f"Using {shell} for default command.") log.info(f"Using {shell} for default command.")
_cmd = [shell] _cmd = [shell] if shell else None
else: else:
log.info(f"Using command {shlex.join(_cmd)}") log.info(f"Using command {shlex.join(_cmd)}")
_no_remote_command = no_remote_command _no_remote_command = no_remote_command
if _cmd is None and _no_remote_command: _remote_cmd_as_args = remote_cmd_as_args
raise Exception(f"Unable to look up shell for {os.getlogin}, cannot proceed with -C and no <program>.") if (_cmd is None or len(_cmd) == 0 or _cmd[0] is None or len(_cmd[0]) == 0) \
and (_no_remote_command or _remote_cmd_as_args):
raise Exception(f"Unable to look up shell for {os.getlogin}, cannot proceed with -A or -C and no <program>.")
if disable_auth: if disable_auth:
_allow_all = True _allow_all = True
@ -603,13 +611,16 @@ def _listen_request(path, data, request_id, link_id, remote_identity, requested_
if not remote_version <= _PROTOCOL_VERSION_DEFAULT: if not remote_version <= _PROTOCOL_VERSION_DEFAULT:
return Session.error_response("Listener<->initiator version mismatch") return Session.error_response("Listener<->initiator version mismatch")
cmd = _cmd cmd = _cmd.copy()
if remote_version >= _PROTOCOL_VERSION_1: if remote_version >= _PROTOCOL_VERSION_1:
remote_command = data[Session.REQUEST_IDX_CMD] remote_command = data[Session.REQUEST_IDX_CMD]
if remote_command is not None and len(remote_command) > 0: if remote_command is not None and len(remote_command) > 0:
if _no_remote_command: if _no_remote_command:
return Session.error_response("Listener does not permit initiator to provide command.") return Session.error_response("Listener does not permit initiator to provide command.")
cmd = remote_command elif _remote_cmd_as_args:
cmd.extend(remote_command)
else:
cmd = remote_command
if not _no_remote_command and (cmd is None or len(cmd) == 0): if not _no_remote_command and (cmd is None or len(cmd) == 0):
return Session.error_response("No command supplied and no default command available.") return Session.error_response("No command supplied and no default command available.")
@ -939,7 +950,8 @@ async def _rnsh_cli_main():
allowed=args.allowed, allowed=args.allowed,
disable_auth=args.no_auth, disable_auth=args.no_auth,
announce_period=args.announce, announce_period=args.announce,
no_remote_command=args.no_remote_cmd) no_remote_command=args.no_remote_cmd,
remote_cmd_as_args=args.remote_cmd_as_args)
return 0 return 0
if args.destination is not None and args.service_name is not None: if args.destination is not None and args.service_name is not None: