diff --git a/nginx_snippets/tes.conf b/nginx_snippets/tes.conf index f5b1d8bb37c439e04f4b07b2cdc74ca478e80bb5..e117cc051cd52735c8bcd3e69e99db4b2225651a 100644 --- a/nginx_snippets/tes.conf +++ b/nginx_snippets/tes.conf @@ -5,7 +5,7 @@ location /502.html { location /tes/ { - proxy_pass http://localhost:8080/; + proxy_pass http://127.0.0.1:8080/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; diff --git a/nginx_snippets/tws.conf b/nginx_snippets/tws.conf index f68f4962c30a14c728eec3e53020756ad061b01f..89c5bd8b09a89d239fea035030eed4a30c9ebf88 100644 --- a/nginx_snippets/tws.conf +++ b/nginx_snippets/tws.conf @@ -1,5 +1,5 @@ location / { - proxy_pass http://localhost:8090; + proxy_pass http://127.0.0.1:8090; proxy_set_header Origin ''; proxy_set_header Referer /; proxy_http_version 1.1; diff --git a/runscript b/runscript index 41a12c0a994c15fe16f771de3f80c942e379aeb6..284de4978688fd8bd60df9b0b9a16b72644ecf12 100755 --- a/runscript +++ b/runscript @@ -3,7 +3,7 @@ uwsgi \ --socket 0.0.0.0:8080 \ --buffer-size 65536 \ --workers 1 \ - --threads 8 \ + --threads 80 \ --protocol=http \ --uid=www-data \ --gid=www-data \ diff --git a/tes/apiendpoints.py b/tes/apiendpoints.py index 23c0f7496b59a03e3b9834703a5de2385fd8db78..7fffc1189a0e9bf681a2ac3b6c6da71c5c65707b 100644 --- a/tes/apiendpoints.py +++ b/tes/apiendpoints.py @@ -29,6 +29,8 @@ class GetCert(Resource): logger = logging.getLogger() try: data = request.get_json() + if type(data) is not dict: + return apiabort(400, message="necessary data not provided in post") response = {'cert':GetCert.get_cert(data['token'], data['pubkey'], data['signing_url'])} return response except: @@ -62,6 +64,8 @@ class SSHAgent(Resource): from .tunnelstat import SSHSession sshsess = SSHSession.get_sshsession() data = request.get_json() + if type(data) is not dict: + return apiabort(400, message="necessary data not provided in post") sshsess.add_keycert(key=data['key'],cert=data['cert']) sshsess.refresh() return "OK" @@ -89,6 +93,8 @@ class SSHAgent(Resource): def delete(self): + import logging + logger = logging.getLogger() from .tunnelstat import SSHSession try: sshsess = SSHSession.get_sshsession() @@ -109,29 +115,47 @@ def get_conn_params(): import logging logger = logging.getLogger() identitystr = request.args.get('identity') - identityparams = json.loads(identitystr) + if type(identitystr) is str: + identityparams = json.loads(identitystr) + else: + identityparams = {} try: interfacestr = request.args.get('interface') - interfaceparams = json.loads(interfacestr) + if type(interfacestr) is str: + interfaceparams = json.loads(interfacestr) + else: + interfaceparams = {} except: interfaceparams = {} pass try: pathstr = request.args.get('path') - pathparams = json.loads(pathstr) + if type(pathstr) is str: + pathparams = json.loads(pathstr) + else: + pathparams = None cdstr = request.args.get('cd') - cdparams = json.loads(cdstr) + if type(cdstr) is str: + cdparams = json.loads(cdstr) + else: + cdparams = None except: pathparams = None cdparams = None try: appstr = request.args.get('app') - appparams = json.loads(appstr) + if type(appstr) is str: + appparams = json.loads(appstr) + else: + appparams = {} except: appparams = {} try: appinstancestr = request.args.get('appinstance') - appinstanceparams = json.loads(appinstancestr) + if type(appinstancestr) is str: + appinstanceparams = json.loads(appinstancestr) + else: + appinstanceparams = {} except: appinstanceparams = {} params = {} @@ -165,9 +189,7 @@ class TunnelstatEP(Resource): import logging logger = logging.getLogger() from . import sshsessions - from flask import session port = None - try: for (sessid,sshsess) in sshsessions.items(): for (tok,port) in sshsess.port.items(): @@ -233,11 +255,13 @@ def wrap_execute(sess, host, user, cmd, bastion=None,stdin=None, sshport="22", b return apiabort(504, message="{}".format(e), sess=sess) except Exception as e: import traceback - logger.error('JobStat.get: Exception {}'.format(e), sess=sess) + logger.error('JobStat.get: Exception {}'.format(e) + ) logger.error(traceback.format_exc()) return apiabort(500,message="{}".format(e), sess=sess) class JobStat(Resource): + """ endpoints to return info on jobs on the backend compute Resource """ @@ -253,9 +277,21 @@ class JobStat(Resource): except Exception as e: return apiabort(500, message="{}".format(e)) try: - cmd = json.loads(request.args.get('statcmd')) - host = json.loads(request.args.get('host')) - user = json.loads(request.args.get('username')) + s = request.args.get('statcmd') + if type(s) is str: + cmd = json.loads(s) + else: + cmd = None + s = request.args.get('host') + if type(s) is str: + host = json.loads(s) + else: + host = None + s = request.args.get('username') + if type(s) is str: + user = json.loads(s) + else: + user = None except (TypeError, KeyError) as e: import traceback return apiabort(400, message="Missing required parameter {}\n{}".format(e,traceback.format_exc()), sess=sshsess) @@ -272,6 +308,8 @@ class MkDir(Resource): params = get_conn_params() sshsess = SSHSession.get_sshsession() site = params['identity']['site'] + if type(data) is not dict: + return apiabort(400, message="necessary parameters not provided in the post information") if 'dtnport' in site: sshport = site['dtnport'] else: @@ -352,9 +390,12 @@ class JobCancel(Resource): Terminate a job on the compute backend """ def delete(self, jobid): + import logging + logger = logging.getLogger() """ Terminate a job on the backend """ + sshsess = None try: params = get_conn_params() sshsess = SSHSession.get_sshsession() @@ -370,7 +411,45 @@ class JobCancel(Resource): #flask_restful.abort(500,message="jobcancel failed in some unexpected way") return apiabort(500,message="jobcancel failed in some unexpected way", sess=sshsess) - +class RemoteCommand(Resource): + # jobsubmit res = wrap_execute(sshsess, host=params['identity']['site']['host'], user=params['identity']['username'], cmd=params['interface']['submitcmd'], stdin=script) + # appinstance rv = wrap_execute(sshsess, host=batchhost, bastion=loginhost, user=username, cmd=paramscmd) ... part of path + # jobscancel res = wrap_execute(sshsess, host=params['identity']['site']['host'], user=params['identity']['username'], cmd=params['interface']['cancelcmd'].format(jobid=jobid)) + def post(self): + import logging + logger=logging.getLogger() + try: + sshsess = SSHSession.get_sshsession() + data = request.get_json() + cmd = None + bastion = None + if type(data) is dict: + user = data['user'] + host = data['host'] + if 'bastion' in data: + bastion = data['bastion'] + else: + bastion = None + cmd = data['cmd'] + else: + return apiabort(400, message="necessary data not provided") + try: + if 'input' in data: + input = data['input'].format(**data) + else: + input = None + res = wrap_execute(sshsess, host, user, cmd, bastion, stdin=input) + return res + except Exception as e: + import traceback + logger.error(e) + logger.error(traceback.format_exc()) + return apiabort(500,message="Remote Command failed in some unexpected way", sess=sshsess) + except Exception as e: + import traceback + logger.error(e) + logger.error(traceback.format_exc()) + return apiabort(500,message="Remote command failed in some unexpected way") class JobSubmit(Resource): """ @@ -380,10 +459,13 @@ class JobSubmit(Resource): """starting a job is a post, since it changes the state of the backend""" import logging logger=logging.getLogger() + sshsess = None try: params = get_conn_params() sshsess = SSHSession.get_sshsession() data = request.get_json() + if type(data) is not dict: + return apiabort(400, message="necessary data not provided in the post information") try: script = data['app']['startscript'].format(**data) except Exception as e: @@ -442,10 +524,18 @@ def apiabort(code, message=None, data = None, sess = None, logdata = None): class AppUrl(Resource): def get(self): import logging + logger = logging.getLogger() try: - logger = logging.getLogger() - appdef = json.loads(request.args.get('app')) - inst = json.loads(request.args.get('appinst')) + s = request.args.get('app') + if type(s) is str: + appdef = json.loads(s) + else: + return apiabort(400, message="Failed to provide information on the application") + s = request.args.get('appinst') + if type(s) is str: + inst = json.loads(s) + else: + return apiabort(400, message="failed to provide information in the application instance") inst['twsproxy']='{twsproxy}' inst['twsproxyauth'] = request.cookies.get('twsproxyauth') logger.debug('twsproxyauth {} {}'.format(inst['twsproxyauth'],request.cookies.get('twsproxyauth'))) @@ -464,7 +554,11 @@ class SetTWSProxyAuth(Resource): import urllib.parse import logging logger = logging.getLogger() - url = urllib.parse.unquote(request.args.get('redirect')) + s = request.args.get('redirect') + if type(s) is str: + url = urllib.parse.unquote(s) + else: + return apiabort(400, message="no redirect provided") logger.debug('SetTWSProxyAuth will redirect to {}'.format(url)) response = make_response(redirect(url)) response.set_cookie('twsproxyauth', authtok, secure=True) @@ -475,39 +569,6 @@ class Ping(Resource): def get(self): return None, 201 -class AppLaunch(Resource): - def get(self): - import logging - logger = logging.getLogger() - try: - appdef = json.loads(request.args.get('app')) - inst = json.loads(request.args.get('appinst')) - cmd = "{}".format(appdef['client']['cmd'].format(**inst)).split() - import subprocess - try: - logger.debug('launching subprocess') - p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE) - (stdout,stderr) = p.communicate() - logger.debug('subprocess completed') - if p.returncode != 0: - if stderr != "": - msg = stderr.decode() - else: - msg = "Unable to start the program {}".format(appdef['client']['cmd']) - return apiabort(500,message=msg) - except FileNotFoundError: - return apiabort(500,message="Unable to find the program {}".format(appdef['client']['cmd'])) - pass - - except Exception as e: - import traceback - logger.error(e) - logger.error(traceback.format_exc()) - #flask_restful.abort(500,message="AppUrl failed in some unexpected way") - return apiabort(500,message="AppLaunc failed in some unexpected way") - logger.debug('application launched, returning') - return None, 200 - class AppInstance(Resource): def get(self, username, loginhost, batchhost, jobid): """Run a command to get things like password and port number @@ -518,10 +579,11 @@ class AppInstance(Resource): events = logging.getLogger('connection_events') sshsess = SSHSession.get_sshsession() sessionId = hashlib.sha1(sshsess.sshsessid.encode()).hexdigest() - try: - paramscmd = json.loads(request.args.get('cmd')).format(jobid=jobid) - except json.decoder.JSONDecodeError: - return apiabort(400, message="No command was sent to the API server") + s = request.args.get('cmd') + if type(s) is str: + paramscmd = json.loads(s).format(jobid=jobid) + else: + return apiabort(400, message="no command provided to find information about the application instance") try: rv = wrap_execute(sshsess, host=batchhost, bastion=loginhost, user=username, cmd=paramscmd) events.info('connect {}'.format(json.dumps({'user':username, 'jobid':jobid, 'loginhost': loginhost, 'sessid':sessionId}))) @@ -558,6 +620,8 @@ class CreateTunnel(Resource): logger = logging.getLogger() try: data = request.get_json() + if type(data) is not dict: + return apiabort(400, message="necessary data not provided in the post information") try: port = data['port'] except KeyError as missingdata: @@ -584,7 +648,7 @@ class CreateTunnel(Resource): internalfirewall=firewall, localbind=localbind, authtok=authtok) response = make_response(json.dumps({'localport':port}),200) - response.mime_type = 'application/json' + response.mimetype = 'application/json' response.set_cookie('twsproxyauth', authtok, secure=True) return response except AppParamsException as e: @@ -605,17 +669,17 @@ class CreateTunnel(Resource): api.add_resource(TunnelstatEP, '/tunnelstat/<string:authtok>') api.add_resource(GetCert, '/getcert') -api.add_resource(JobStat, '/stat') + +api.add_resource(JobStat, '/stat') # trying to deprecate +api.add_resource(JobSubmit, '/submit') # trying to deprecate +api.add_resource(AppInstance, '/appinstance/<string:username>/<string:loginhost>/<string:batchhost>/<string:jobid>') # trying to deprecate +api.add_resource(JobCancel, '/cancel/<string:jobid>') # trying to deprecate + +api.add_resource(RemoteCommand,'/remotecommand') api.add_resource(Ping, '/ping') -api.add_resource(JobCancel, '/cancel/<string:jobid>') api.add_resource(SetTWSProxyAuth, '/settwsproxyauth/<string:authtok>') -api.add_resource(JobSubmit, '/submit') api.add_resource(CreateTunnel, '/createtunnel/<string:username>/<string:loginhost>/<string:batchhost>') -api.add_resource(AppInstance, '/appinstance/<string:username>/<string:loginhost>/<string:batchhost>/<string:jobid>') api.add_resource(AppUrl, '/appurl') -if 'ENABLELAUNCH' in app.config and app.config['ENABLELAUNCH']: - api.add_resource(AppLaunch, '/applaunch') -api.add_resource(AppLaunch, '/applaunch') api.add_resource(SSHAgent,'/sshagent') api.add_resource(DirList,'/ls') api.add_resource(MkDir,'/mkdir') diff --git a/twsproxy/__init__.py b/twsproxy/__init__.py index be5ea7857b704cbb7202b40e50e7e05ba72ca1ba..21f44d744582cd966e21a00301eb9fa1a659bdec 100644 --- a/twsproxy/__init__.py +++ b/twsproxy/__init__.py @@ -45,8 +45,11 @@ class TWSProxy(threading.Thread): keepreading = False else: port = TWSProxy.verifyauth(header[0:bytessofar]) + if port is None: + logger.error('select returned no new bytes, verification failed, headers are {}'.format(header[0:bytessofar])) keepreading = False if initcount > failthresh: + logger.error('Exceeded threhold, terminating proxy after {} bytes'.format(bytessofar)) keepreading = False if port is not None: