Merge pull request #25 from markqvist/identities_file

Allowed Identities File
This commit is contained in:
acehoss 2023-09-24 11:36:42 -07:00 committed by GitHub
commit 13352ee4e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 8 deletions

View File

@ -20,7 +20,7 @@ usage = \
Usage: Usage:
rnsh -l [-c <configdir>] [-i <identityfile> | -s <service_name>] [-v... | -q...] -p rnsh -l [-c <configdir>] [-i <identityfile> | -s <service_name>] [-v... | -q...] -p
rnsh -l [-c <configdir>] [-i <identityfile> | -s <service_name>] [-v... | -q...] rnsh -l [-c <configdir>] [-i <identityfile> | -s <service_name>] [-v... | -q...]
[-b <period>] (-n | -a <identity_hash> [-a <identity_hash>] ...) [-A | -C] [-b <period>] [-n] [-a <identity_hash>] ([-a <identity_hash>] ...) [-A | -C]
[[--] <program> [<arg> ...]] [[--] <program> [<arg> ...]]
rnsh [-c <configdir>] [-i <identityfile>] [-v... | -q...] -p rnsh [-c <configdir>] [-i <identityfile>] [-v... | -q...] -p
rnsh [-c <configdir>] [-i <identityfile>] [-v... | -q...] [-N] [-m] [-w <timeout>] rnsh [-c <configdir>] [-i <identityfile>] [-v... | -q...] [-N] [-m] [-w <timeout>]
@ -40,7 +40,9 @@ Options:
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. Allowed identities
can also be specified in ~/.rnsh/allowed_identities or
~/.config/rnsh/allowed_identities, one hash per line.
-n --no-auth Disable authentication -n --no-auth Disable authentication
-N --no-id Disable identify on connect -N --no-id Disable identify on connect
-A --remote-command-as-args Concatenate remote command to argument list of <program>/shell -A --remote-command-as-args Concatenate remote command to argument list of <program>/shell

View File

@ -64,7 +64,9 @@ def _get_logger(name: str):
_identity = None _identity = None
_reticulum = None _reticulum = None
_allow_all = False _allow_all = False
_allowed_file = None
_allowed_identity_hashes = [] _allowed_identity_hashes = []
_allowed_file_identity_hashes = []
_cmd: [str] | None = None _cmd: [str] | None = None
DATA_AVAIL_MSG = "data available" DATA_AVAIL_MSG = "data available"
_finished: asyncio.Event = None _finished: asyncio.Event = None
@ -88,12 +90,37 @@ def _sigint_handler(sig, loop):
else: else:
raise KeyboardInterrupt() raise KeyboardInterrupt()
def _reload_allowed_file():
global _allowed_file, _allowed_file_identity_hashes
log = _get_logger("_listen")
if _allowed_file != None:
try:
with open(_allowed_file, "r") as file:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH // 8) * 2
added = 0
line = 0
_allowed_file_identity_hashes = []
for allow in file.read().replace("\r", "").split("\n"):
line += 1
if len(allow) == dest_len:
try:
destination_hash = bytes.fromhex(allow)
_allowed_file_identity_hashes.append(destination_hash)
added += 1
except Exception:
log.debug(f"Discarded invalid Identity hash in {_allowed_file} at line {line}")
ms = "y" if added == 1 else "ies"
log.debug(f"Loaded {added} allowed identit{ms} from "+str(_allowed_file))
except Exception as e:
log.error(f"Error while reloading allowed indetities file: {e}")
async def listen(configdir, command, identitypath=None, service_name=None, verbosity=0, quietness=0, allowed=None, async def listen(configdir, command, identitypath=None, service_name=None, verbosity=0, quietness=0, allowed=None,
disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False, allowed_file=None, disable_auth=None, announce_period=900, no_remote_command=True, remote_cmd_as_args=False,
loop: asyncio.AbstractEventLoop = None): loop: asyncio.AbstractEventLoop = None):
global _identity, _allow_all, _allowed_identity_hashes, _reticulum, _cmd, _destination, _no_remote_command global _identity, _allow_all, _allowed_identity_hashes, _allowed_file, _allowed_file_identity_hashes
global _remote_cmd_as_args, _finished global _reticulum, _cmd, _destination, _no_remote_command, _remote_cmd_as_args, _finished
log = _get_logger("_listen") log = _get_logger("_listen")
if not loop: if not loop:
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
@ -135,6 +162,10 @@ async def listen(configdir, command, identitypath=None, service_name=None, verbo
_allow_all = True _allow_all = True
session.ListenerSession.allow_all = True session.ListenerSession.allow_all = True
else: else:
if allowed_file is not None:
_allowed_file = allowed_file
_reload_allowed_file()
if allowed is not None: if allowed is not None:
for a in allowed: for a in allowed:
try: try:
@ -154,10 +185,13 @@ async def listen(configdir, command, identitypath=None, service_name=None, verbo
log.error(str(e)) log.error(str(e))
exit(1) exit(1)
if len(_allowed_identity_hashes) < 1 and not disable_auth: if (len(_allowed_identity_hashes) < 1 and len(_allowed_file_identity_hashes) < 1) and not disable_auth:
log.warning("Warning: No allowed identities configured, rnsh will not accept any connections!") log.warning("Warning: No allowed identities configured, rnsh will not accept any connections!")
def link_established(lnk: RNS.Link): def link_established(lnk: RNS.Link):
_reload_allowed_file()
session.ListenerSession.allowed_file_identity_hashes = _allowed_file_identity_hashes
print(str(_allowed_file_identity_hashes))
session.ListenerSession(session.RNSOutlet.get_outlet(lnk), lnk.get_channel(), loop) session.ListenerSession(session.RNSOutlet.get_outlet(lnk), lnk.get_channel(), loop)
_destination.set_link_established_callback(link_established) _destination.set_link_established_callback(link_established)

View File

@ -117,7 +117,13 @@ async def _rnsh_cli_main():
return 0 return 0
if args.listen: if args.listen:
# log.info("command " + args.command) allowed_file = None
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if os.path.isfile(os.path.expanduser("~/.config/rnsh/allowed_identities")):
allowed_file = os.path.expanduser("~/.config/rnsh/allowed_identities")
elif os.path.isfile(os.path.expanduser("~/.rnsh/allowed_identities")):
allowed_file = os.path.expanduser("~/.rnsh/allowed_identities")
await listener.listen(configdir=args.config, await listener.listen(configdir=args.config,
command=args.command_line, command=args.command_line,
identitypath=args.identity, identitypath=args.identity,
@ -125,6 +131,7 @@ async def _rnsh_cli_main():
verbosity=args.verbose, verbosity=args.verbose,
quietness=args.quiet, quietness=args.quiet,
allowed=args.allowed, allowed=args.allowed,
allowed_file=allowed_file,
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,

View File

@ -69,6 +69,7 @@ class LSOutletBase(ABC):
class ListenerSession: class ListenerSession:
sessions: List[ListenerSession] = [] sessions: List[ListenerSession] = []
allowed_identity_hashes: [any] = [] allowed_identity_hashes: [any] = []
allowed_file_identity_hashes: [any] = []
allow_all: bool = False allow_all: bool = False
allow_remote_command: bool = False allow_remote_command: bool = False
default_command: [str] = [] default_command: [str] = []
@ -183,7 +184,7 @@ class ListenerSession:
if self.state not in [LSState.LSSTATE_WAIT_IDENT, LSState.LSSTATE_WAIT_VERS]: if self.state not in [LSState.LSSTATE_WAIT_IDENT, LSState.LSSTATE_WAIT_VERS]:
self._protocol_error(LSState.LSSTATE_WAIT_IDENT.name) self._protocol_error(LSState.LSSTATE_WAIT_IDENT.name)
if not self.allow_all and identity.hash not in self.allowed_identity_hashes: if not self.allow_all and identity.hash not in self.allowed_identity_hashes and identity.hash not in self.allowed_file_identity_hashes:
self.terminate("Identity is not allowed.") self.terminate("Identity is not allowed.")
self.remote_identity = identity self.remote_identity = identity