From d63830423c1723bb60909d3ba961ee30b96e5199 Mon Sep 17 00:00:00 2001 From: ChaoticByte Date: Sat, 15 Jun 2024 19:10:58 +0200 Subject: [PATCH] Add project files --- LICENSE | 2 +- asserver.py | 101 +++++++++++++++++++++++++++++++++++++++++++++ example_config.yml | 15 +++++++ requirements.txt | 2 + 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100755 asserver.py create mode 100644 example_config.yml create mode 100644 requirements.txt diff --git a/LICENSE b/LICENSE index c06137c..920dc4f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Julian Müller +Copyright (c) 2024 Julian Müller (ChaoticByte) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/asserver.py b/asserver.py new file mode 100755 index 0000000..dfd1e07 --- /dev/null +++ b/asserver.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2024 Julian Müller (ChaoticByte) +# License: MIT + +import asyncio + +from argparse import ArgumentParser +from pathlib import Path +from sys import stdout +from sys import stderr + +import asyncssh +import yaml + + +config_host = "" +config_port = 8022 +connected_clients = [] +config_clients = { + # username: asyncssh.SSHAuthorizedKeys +} + + +class SSHServer(asyncssh.SSHServer): + def host_based_auth_supported(self): return False + def kbdint_auth_supported(self): return False + def password_auth_supported(self): return False + def public_key_auth_supported(self): return True + def begin_auth(self, username: str) -> bool: return True # we wanna handle auth + + def validate_public_key(self, username: str, key: asyncssh.SSHKey) -> bool: + try: + return config_clients[username].validate(key, "", "") is not None + except: + return False + + +def broadcast(msg: str): + assert type(msg) == str + for c in connected_clients: + c.stdout.write(msg) + +async def handle_connection(process: asyncssh.SSHServerProcess): + connected_clients.append(process) + username = process.get_extra_info("username") + try: + connected_msg = f"[connected] {username}\n" + stdout.write(connected_msg) + broadcast(connected_msg) + while True: + try: + async for line in process.stdin: + if line == "": raise asyncssh.BreakReceived(0) + line = line.strip('\n\r') + msg = f"{username}: {line}\n" + stdout.write(msg) + broadcast(msg) + except asyncssh.TerminalSizeChanged: + continue + finally: + break + except asyncssh.BreakReceived: + pass + except Exception as e: + stderr.write(f"An error occured: {type(e).__name__} {e}\n") + stderr.flush() + finally: + process.exit(0) + connected_clients.remove(process) + disconnected_msg = f"[disconnected] {username}\n" + stdout.write(disconnected_msg) + broadcast(disconnected_msg) + + +if __name__ == "__main__": + # commandline arguments + argp = ArgumentParser() + argp.add_argument("config", type=Path, help="The path to the config file") + args = argp.parse_args() + # read config + config = yaml.safe_load(args.config.read_text()) + config_host = str(config["host"]) + config_port = int(config["port"]) + config_private_key = asyncssh.import_private_key(config["private_key"]) + server_public_key = config_private_key.export_public_key("openssh").decode().strip("\n\r") + stderr.write(f"Server public key is \"{server_public_key}\"\n") + stderr.flush() + for c in config["clients"]: + config_clients[str(c)] = asyncssh.import_authorized_keys(str(config["clients"][c])) + # start server + loop = asyncio.get_event_loop() + loop.run_until_complete( + asyncssh.create_server( + SSHServer, + config_host, + config_port, + server_host_keys=[config_private_key], + process_factory=handle_connection + )) + loop.run_forever() diff --git a/example_config.yml b/example_config.yml new file mode 100644 index 0000000..ebd00a5 --- /dev/null +++ b/example_config.yml @@ -0,0 +1,15 @@ +# Example server config file +host: '' +port: 8022 +clients: + # This are just examples. Please generate your own keypair for each client you want to add + example1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFY33xLzFiLk5GnNu2Bm8Ns3TMSILtU6p1WItgy6sPMP + example2: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMDV1fTeb7PiYVnqzGTQzdw5r3VPSaDFgfoPm1GD6HaR +private_key: | # Change this!!! + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACDtzTutk/iImRHKDY2CBwoQab6BfE0ppM3/0wNVbvJEzgAAAJCANLL6gDSy + +gAAAAtzc2gtZWQyNTUxOQAAACDtzTutk/iImRHKDY2CBwoQab6BfE0ppM3/0wNVbvJEzg + AAAECpsT0qdquxr8MUe1zltF1ttE3Ean52CpYROv0WkOcmle3NO62T+IiZEcoNjYIHChBp + voF8TSmkzf/TA1Vu8kTOAAAACmp1bGlhbkBwYzEBAgM= + -----END OPENSSH PRIVATE KEY----- diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..901a685 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +asyncssh +PyYAML