Add project files
This commit is contained in:
parent
8fbe47ced9
commit
d63830423c
4 changed files with 119 additions and 1 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
101
asserver.py
Executable file
101
asserver.py
Executable file
|
@ -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()
|
15
example_config.yml
Normal file
15
example_config.yml
Normal file
|
@ -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-----
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
asyncssh
|
||||||
|
PyYAML
|
Reference in a new issue