From d0b4c7d4d87817c1de786ab7d0df5378b61a7344 Mon Sep 17 00:00:00 2001 From: mentate Date: Tue, 17 Oct 2023 13:22:00 -0500 Subject: [PATCH] Added a setting/parameter for an access key. If set, key must be present as a url parameter when requesting index, otherwise access is decied --- webssh/handler.py | 23 +++++++++++++++++++---- webssh/main.py | 8 ++++++-- webssh/settings.py | 5 ++++- webssh/templates/access_denied.html | 8 ++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 webssh/templates/access_denied.html diff --git a/webssh/handler.py b/webssh/handler.py index 6cfc822..2bb72be 100644 --- a/webssh/handler.py +++ b/webssh/handler.py @@ -186,7 +186,7 @@ class PrivateKey(object): class MixinHandler(object): custom_headers = { - 'Server': 'TornadoServer' + 'You Should': 'Stay Out' } html = ('{code} {reason}{code} ' @@ -316,10 +316,11 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler): executor = ThreadPoolExecutor(max_workers=cpu_count()*5) - def initialize(self, loop, policy, host_keys_settings): + def initialize(self, loop, policy, host_keys_settings, access_key): super(IndexHandler, self).initialize(loop) self.policy = policy self.host_keys_settings = host_keys_settings + self.access_key = access_key self.ssh_client = self.get_ssh_client() self.debug = self.settings.get('debug', False) self.font = self.settings.get('font', '') @@ -395,6 +396,10 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler): privatekey, filename = self.get_privatekey() passphrase = self.get_argument('passphrase', u'') totp = self.get_argument('totp', u'') + supplied_access_key = self.get_argument('access_key', u'') + + + if isinstance(self.policy, paramiko.RejectPolicy): self.lookup_hostname(hostname, port) @@ -405,7 +410,7 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler): pkey = None self.ssh_client.totp = totp - args = (hostname, port, username, password, pkey) + args = (hostname, port, username, password, pkey, supplied_access_key) logging.debug(args) return args @@ -488,7 +493,17 @@ class IndexHandler(MixinHandler, tornado.web.RequestHandler): pass def get(self): - self.render('index.html', debug=self.debug, font=self.font) + # if path matches options.path + supplied_access_key = self.get_argument('accesskey', u'') + + if self.debug: + logging.debug(f"Provided access key: {supplied_access_key}") + logging.debug(f"actual access key: {self.access_key}") + + if supplied_access_key != self.access_key: + self.render('access_denied.html') + else: + self.render('index.html', debug=self.debug, font=self.font) @tornado.gen.coroutine def post(self): diff --git a/webssh/main.py b/webssh/main.py index 5faad10..0602cd4 100644 --- a/webssh/main.py +++ b/webssh/main.py @@ -7,17 +7,21 @@ from webssh import handler from webssh.handler import IndexHandler, WsockHandler, NotFoundHandler from webssh.settings import ( get_app_settings, get_host_keys_settings, get_policy_setting, - get_ssl_context, get_server_settings, check_encoding_setting + get_ssl_context, get_server_settings, check_encoding_setting, + get_access_key ) def make_handlers(loop, options): + logging.info(options) host_keys_settings = get_host_keys_settings(options) policy = get_policy_setting(options, host_keys_settings) + access_key = get_access_key(options) handlers = [ (r'/', IndexHandler, dict(loop=loop, policy=policy, - host_keys_settings=host_keys_settings)), + host_keys_settings=host_keys_settings, + access_key=access_key)), (r'/ws', WsockHandler, dict(loop=loop)) ] return handlers diff --git a/webssh/settings.py b/webssh/settings.py index b02b79e..6477753 100644 --- a/webssh/settings.py +++ b/webssh/settings.py @@ -20,6 +20,7 @@ def print_version(flag): define('address', default='', help='Listen address') +define('accesskey', default='', help='If provided, requests must have a matching url parameter') define('port', type=int, default=8888, help='Listen port') define('ssladdress', default='', help='SSL listen address') define('sslport', type=int, default=4433, help='SSL listen port') @@ -76,7 +77,7 @@ class Font(object): def get_app_settings(options): settings = dict( template_path=os.path.join(base_dir, 'webssh', 'templates'), - static_path=os.path.join(base_dir, 'webssh', 'static'), + static_path=os.path.join(base_dir,'webssh', 'static'), websocket_ping_interval=options.wpintvl, debug=options.debug, xsrf_cookies=options.xsrf, @@ -126,6 +127,8 @@ def get_policy_setting(options, host_keys_settings): check_policy_setting(policy_class, host_keys_settings) return policy_class() +def get_access_key(options): + return options.accesskey def get_ssl_context(options): if not options.certfile and not options.keyfile: diff --git a/webssh/templates/access_denied.html b/webssh/templates/access_denied.html new file mode 100644 index 0000000..63a853f --- /dev/null +++ b/webssh/templates/access_denied.html @@ -0,0 +1,8 @@ + + + +

Access Denied

+

Access key is not present or is invalid

+ + +