This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 436b30fe4508905a92baf41a3c98d0e8778f000e Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Tue Mar 18 19:23:03 2014 +0100 Provide tool: x2gobroker-testagent. --- bin/x2gobroker | 22 +----- debian/changelog | 1 + debian/rules | 1 + lib/x2gobroker-agent.pl | 24 +++++++ sbin/x2gobroker-testagent | 142 +++++++++++++++++++++++++++++++++++++ x2gobroker/agent.py | 43 ++++++++++- x2gobroker/brokers/base_broker.py | 6 +- x2gobroker/utils.py | 19 +++++ 8 files changed, 234 insertions(+), 24 deletions(-) diff --git a/bin/x2gobroker b/bin/x2gobroker index b94443c..b1f8258 100755 --- a/bin/x2gobroker +++ b/bin/x2gobroker @@ -92,25 +92,7 @@ except ImportError: from x2gobroker import __VERSION__ from x2gobroker import __AUTHOR__ from x2gobroker.loggers import logger_broker, logger_access, logger_error, tornado_log_request - -def drop_privileges(uid=x2gobroker.defaults.X2GOBROKER_DAEMON_USER, gid=x2gobroker.defaults.X2GOBROKER_DAEMON_USER): - if os.getuid() != 0: - # We're not root so, like, whatever dude - return - - # Get the uid/gid from the name - running_uid = pwd.getpwnam(uid).pw_uid - running_gid = grp.getgrnam(gid).gr_gid - - # Remove group privileges - os.setgroups([]) - - # Try setting the new uid/gid - os.setgid(running_gid) - os.setuid(running_uid) - - # Ensure a very conservative umask - old_umask = os.umask(077) +from x2gobroker.utils import drop_privileges interactive_mode_warning = False # check effective UID the broker runs as and complain appropriately... @@ -166,7 +148,7 @@ if __name__ == "__main__": cmdline_args = p.parse_args() if os.getuid() == 0 and cmdline_args.drop_privileges: - drop_privileges() + drop_privileges(uid=x2gobroker.defaults.X2GOBROKER_DAEMON_USER, gid=x2gobroker.defaults.X2GOBROKER_DAEMON_GROUP) if cmdline_args.config_file is not None: x2gobroker.defaults.X2GOBROKER_CONFIG = cmdline_args.config_file diff --git a/debian/changelog b/debian/changelog index 69afeed..0ac02f0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -98,6 +98,7 @@ x2gobroker (0.0.3.0-0x2go1) UNRELEASED; urgency=low - Handle selectsessions calls with a non-existent profile ID gracefully. - Session profiles with marker user=BROKER_USER will now auto-fill-in the broker username into the session profile's 'user' option. + - Provide tool: x2gobroker-testagent. * debian/control: + Replace LDAP support with session brokerage support in LONG_DESCRIPTION. + Fix SYNOPSIS texts. diff --git a/debian/rules b/debian/rules index b1eb079..3b69310 100755 --- a/debian/rules +++ b/debian/rules @@ -24,6 +24,7 @@ export PREFIX=/usr include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/makefile.mk DEB_PYTHON_SYSTEM = $(shell test -f /usr/bin/dh_python2 && echo "" || echo "pysupport") DEB_PYTHON_INSTALL_ARGS_python-x2gobroker ?= --root=$(DEB_DESTDIR) --prefix=$(DEB_PYTHON_PREFIX_ARG) --no-compile -O0 --install-layout=deb diff --git a/lib/x2gobroker-agent.pl b/lib/x2gobroker-agent.pl index abab968..7c04a42 100755 --- a/lib/x2gobroker-agent.pl +++ b/lib/x2gobroker-agent.pl @@ -24,6 +24,19 @@ use strict; use File::Basename; +my @available_tasks = ( + "availabletasks", + "listsessions", + "findbusyservers", + "findbusyservers_by_sessionstats", + "getservers", + "addauthkey", + "delauthkey", + "suspendsession", + "terminatesession", +); + + sub InitX2GoUser { my ($user, $uidNumber, $gidNumber, $home)=@_; @@ -111,6 +124,17 @@ if($uidNumber < 1000) die 'operation on system user'; } +if($mode eq 'availabletasks') +{ + InitX2GoUser($uid, $uidNumber, $gidNumber, $home); + print "OK\n"; + my $available_task; + foreach $available_task (@available_tasks) { + print "$available_task\n"; + } +} + + if($mode eq 'listsessions') { InitX2GoUser($uid, $uidNumber, $gidNumber, $home); diff --git a/sbin/x2gobroker-testagent b/sbin/x2gobroker-testagent new file mode 100755 index 0000000..c22b5a7 --- /dev/null +++ b/sbin/x2gobroker-testagent @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +# This file is part of the X2Go Project - http://www.x2go.org +# Copyright (C) 2011-2014 by Oleksandr Shneyder <oleksandr.shneyder@obviously-nice.de> +# Copyright (C) 2011-2014 by Heinz-Markus Graesing <heinz-m.graesing@obviously-nice.de> +# Copyright (C) 2012-2014 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de> +# +# X2Go Session Broker is free software; you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# X2Go Session Broker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +import os +import sys +import setproctitle +import argparse +import logging + +# perform an authentication against the authentication mechanism configured for WSGI +try: + import x2gobroker.defaults +except ImportError: + sys.path.insert(0, os.path.join(os.getcwd(), '..')) + import x2gobroker.defaults +import x2gobroker.loggers + +import x2gobroker.agent +from x2gobroker.utils import drop_privileges + +PROG_NAME = os.path.basename(sys.argv[0]) +PROG_OPTIONS = sys.argv[1:] +try: + _password_index = PROG_OPTIONS.index('--password')+1 + PROG_OPTIONS[_password_index] = "XXXXXXXX" +except ValueError: + # ignore if --password option is not specified + pass +setproctitle.setproctitle("%s %s" % (PROG_NAME, " ".join(PROG_OPTIONS))) + +if __name__ == "__main__": + + agent_options = [ + {'args':['--drop-privileges'], 'default': False, 'action': 'store_true', 'help': 'Drop privileges to user {x2gobroker} (recommended)'.format(x2gobroker=x2gobroker.defaults.X2GOBROKER_DAEMON_USER), }, + {'args':['-H','--host'], 'default': 'LOCAL', 'metavar': 'HOSTNAME', 'help': 'Test X2Go Session Broker Agent on this host (default: LOCAL)', }, + {'args':['-p','--port'], 'default': 22, 'metavar': 'PORT', 'help': 'For remote agent calls (via SSH) use this port as SSH port (default: 22)', }, + {'args':['-u','--username'], 'default': None, 'metavar': 'USERNAME', 'help': 'When testing the broker agent, test on behalf of this user (default: none)', }, + {'args':['-t','--task'], 'default': 'PING_ICMP', 'metavar': 'AGENT_TASK', 'help': 'Perform this task on the (remote) broker agent', }, + {'args':['--list-tasks'], 'default': False, 'action': 'store_true', 'help': 'List available broker agent tasks', }, + ] + misc_options = [ + {'args':['-C','--config-file'], 'default': None, 'metavar': 'CONFIG_FILE', 'help': 'Specify a special configuration file name, default is: {default}'.format(default=x2gobroker.defaults.X2GOBROKER_CONFIG), }, + {'args':['-d','--debug'], 'default': False, 'action': 'store_true', 'help': 'enable debugging code', }, + ] + p = argparse.ArgumentParser(description='X2Go Session Broker (Agent Test Utility)',\ + formatter_class=argparse.RawDescriptionHelpFormatter, \ + add_help=True, argument_default=None) + p_agent = p.add_argument_group('agent parameters') + p_misc = p.add_argument_group('miscellaneous parameters') + + for (p_group, opts) in ( (p_agent, agent_options), (p_misc, misc_options), ): + for opt in opts: + args = opt['args'] + del opt['args'] + p_group.add_argument(*args, **opt) + + cmdline_args = p.parse_args() + + if cmdline_args.username is None and not cmdline_args.list_tasks: + p.print_help() + print + print "*** Cannot continue without username... ***" + print + sys.exit(-1) + + if cmdline_args.config_file is not None: + x2gobroker.defaults.X2GOBROKER_CONFIG = cmdline_args.config_file + + if cmdline_args.drop_privileges: + drop_privileges(uid=x2gobroker.defaults.X2GOBROKER_DAEMON_USER, gid=x2gobroker.defaults.X2GOBROKER_DAEMON_GROUP) + + if cmdline_args.debug: + x2gobroker.defaults.X2GOBROKER_DEBUG = cmdline_args.debug + # raise log level to DEBUG if requested... + if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE: + x2gobroker.loggers.logger_broker.setLevel(logging.DEBUG) + x2gobroker.loggers.logger_error.setLevel(logging.DEBUG) + + username = cmdline_args.username + hostname = cmdline_args.host + port = cmdline_args.port + task = cmdline_args.task + + list_tasks = cmdline_args.list_tasks + +local_agent = (hostname == 'LOCAL') +query_mode = local_agent and 'LOCAL' or 'SSH' +if local_agent: remote_agent = None +else: remote_agent = {'hostname': hostname, 'port': port, } + +agent_client_tasks = x2gobroker.agent.tasks +if 'availabletasks' in agent_client_tasks: + try: + remote_agent_tasks = x2gobroker.agent.tasks_available(username=username, query_mode=query_mode, remote_agent=remote_agent) + except x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException, e: + print e + sys.exit(0) + +def call_agent(task): + return agent_client_tasks[task](username=username, query_mode=query_mode, remote_agent=remote_agent) + +if __name__ == "__main__": + + if not local_agent and not x2gobroker.agent.has_remote_broker_agent_setup(): + + print "This instance of X2Go Session Broker is not able to contact any remote" + print "X2Go Session Broker Agent instances. Check this broker's SSH setup!!!" + print + print "Aborting any futher tests..." + sys.exit(-1) + + if list_tasks: + print "The queried broker agent supports these tasks / features:" + print + for task in remote_agent_tasks: + try: + print " {task_name}: {task_function_obj}".format(task_name=task, task_function_obj=agent_client_tasks[task]) + except KeyError: + print " {task_name}: not supported by this broker version".format(task_name=task) + print + sys.exit(0) + + call_agent(task) diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py index 183985f..7d0baca 100644 --- a/x2gobroker/agent.py +++ b/x2gobroker/agent.py @@ -35,6 +35,8 @@ import x2gobroker.defaults import x2gobroker.x2gobroker_exceptions from x2gobroker.loggers import logger_broker, logger_error +tasks = {} + class delayed_execution(threading.Thread): @@ -54,7 +56,6 @@ class delayed_execution(threading.Thread): self.agent_func(**self.kwargs) - def has_remote_broker_agent_setup(): """\ Peform some integrity checks that may indicate that a remote @@ -72,6 +73,7 @@ def has_remote_broker_agent_setup(): elif os.path.exists(os.path.join(home, '.ssh', 'id_ecdsa')): return True + def call_local_broker_agent(username, mode, cmdline_args=[]): """\ Launch X2Go Broker Agent locally and process its output. @@ -110,6 +112,7 @@ def call_local_broker_agent(username, mode, cmdline_args=[]): raise x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException('Query to local X2Go Broker Agent failed with no response') + def call_remote_broker_agent(username, mode, cmdline_args=[], remote_agent=None): """\ Launch remote X2Go Broker Agent via SSH and process its output. @@ -162,7 +165,9 @@ def call_remote_broker_agent(username, mode, cmdline_args=[], remote_agent=None) client.close() if result and result[0].startswith('OK'): return [ r for r in result[1:] if r ] - except (paramiko.SSHException, paramiko.AuthenticationException, paramiko.BadHostKeyException, socket.error): + except paramiko.AuthenticationException: + raise x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException('Authentication to remote X2Go Broker Agent Host failed (user: {user}, hostname: {hostname}, port: {port}) failed'.format(user=remote_username, hostname=remote_hostname, port=remote_port)) + except (paramiko.SSHException, paramiko.BadHostKeyException, socket.error): raise x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException('Query to remote X2Go Broker Agent (user: {user}, hostname: {hostname}, port: {port}) failed'.format(user=remote_username, hostname=remote_hostname, port=remote_port)) @@ -212,6 +217,7 @@ def ping(query_mode='LOCAL', remote_agent=None): return remote_agent is not None and \ icmp_ping(remote_agent['hostname']) and \ call_remote_broker_agent(username, mode='ping', remote_agent=remote_agent) +tasks['ping'] = ping def list_sessions(username, query_mode='LOCAL', remote_agent=None): @@ -230,6 +236,7 @@ def list_sessions(username, query_mode='LOCAL', remote_agent=None): return call_local_broker_agent(username, mode='listsessions') else: return call_remote_broker_agent(username, mode='listsessions', remote_agent=remote_agent) +tasks['listsessions'] = list_sessions def suspend_session(username, session_name, query_mode='LOCAL', remote_agent=None): @@ -248,6 +255,7 @@ def suspend_session(username, session_name, query_mode='LOCAL', remote_agent=Non return call_local_broker_agent(username, mode='suspendsession', cmdline_args=[session_name, ], ) else: return call_remote_broker_agent(username, mode='suspendsession', cmdline_args=[session_name, ], remote_agent=remote_agent) +tasks['suspendsession'] = suspend_session def terminate_session(username, session_name, query_mode='LOCAL', remote_agent=None): @@ -266,6 +274,7 @@ def terminate_session(username, session_name, query_mode='LOCAL', remote_agent=N return call_local_broker_agent(username, mode='terminatesession', cmdline_args=[session_name, ], ) else: return call_remote_broker_agent(username, mode='terminatesession', cmdline_args=[session_name, ], remote_agent=remote_agent) +tasks['terminatesession'] = terminate_session def has_sessions(username, query_mode='LOCAL', remote_agent=None): @@ -287,6 +296,7 @@ def has_sessions(username, query_mode='LOCAL', remote_agent=None): _session_list = list_sessions(username, query_mode=query_mode, remote_agent=remote_agent) return ([ s.split('|')[3] for s in _session_list if s.split('|')[4] == 'R' ], [ s.split('|')[3] for s in _session_list if s.split('|')[4] == 'S' ]) + def find_busy_servers(username, query_mode='LOCAL', remote_agent=None): """\ Query X2Go Broker Agent for a list of servers with running @@ -317,6 +327,7 @@ def find_busy_servers(username, query_mode='LOCAL', remote_agent=None): server_usage.update({ server: int(usage) }) return server_usage +tasks['findbusyservers'] = find_busy_servers def add_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/authorized_keys', query_mode='LOCAL', remote_agent=None): @@ -339,6 +350,7 @@ def add_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/aut return call_local_broker_agent(username, mode='addauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ]) else: return call_remote_broker_agent(username, mode='addauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ], remote_agent=remote_agent) +tasks['addauthkey'] = add_authorized_key def delete_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/authorized_keys', query_mode='LOCAL', remote_agent=None, delay_deletion=0): @@ -371,6 +383,7 @@ def delete_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/ return call_local_broker_agent(username, mode='delauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ]) else: return call_remote_broker_agent(username, mode='delauthkey', cmdline_args=[pubkey_hash, authorized_keys_file, ], remote_agent=remote_agent) +tasks['delauthkey'] = delete_authorized_key def get_servers(username, query_mode='LOCAL', remote_agent=None): @@ -390,7 +403,30 @@ def get_servers(username, query_mode='LOCAL', remote_agent=None): if unicode(query_mode).upper() == u'LOCAL': return call_local_broker_agent(username, mode='getservers') else: - return call_local_broker_agent(username, mode='getservers', remote_agent=remote_agent) + return call_remote_broker_agent(username, mode='getservers', remote_agent=remote_agent) +tasks['getservers'] = get_servers + + +def tasks_available(username, query_mode='LOCAL', remote_agent=None): + """\ + Query X2Go Broker Agent for the list of available tasks. + + Depending on the remove broker agent's version, the result of this + query can vary tremendously from X2Go Server to X2Go Server. + + @param username: run the query on behalf of this username + @type username: C{unicode} + @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH}) + @type query_mode: C{unicode} + @param remote_agent: information about the remote agent that is to be called. + @type remote_agent: C{dict} + + """ + if unicode(query_mode).upper() == u'LOCAL': + return call_local_broker_agent(username, mode='availabletasks') + else: + return call_remote_broker_agent(username, mode='availabletasks', remote_agent=remote_agent) +tasks['availabletasks'] = tasks_available def genkeypair(local_username, client_address, key_type='RSA'): @@ -433,3 +469,4 @@ def genkeypair(local_username, client_address, key_type='RSA'): privkey = privkey_obj.getvalue() return (pubkey, privkey) + diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py index ad49ef7..0edb946 100644 --- a/x2gobroker/brokers/base_broker.py +++ b/x2gobroker/brokers/base_broker.py @@ -1044,7 +1044,11 @@ class X2GoBroker(object): # find already running/suspended sessions and resume the first one found if remote_agent and server_list and username: - session_list = x2gobroker.agent.list_sessions(username=username, query_mode=agent_query_mode, remote_agent=remote_agent) + try: + session_list = x2gobroker.agent.list_sessions(username=username, query_mode=agent_query_mode, remote_agent=remote_agent) + except x2gobroker.x2gobroker_exceptions.X2GoBrokerAgentException: + session_list = [] + if session_list: # if resuming, always select the first session in the list, there should only be one suspended session diff --git a/x2gobroker/utils.py b/x2gobroker/utils.py index 34117d5..776f380 100644 --- a/x2gobroker/utils.py +++ b/x2gobroker/utils.py @@ -169,3 +169,22 @@ def matching_hostnames(server_list_a, server_list_b): matching_hosts = list(set(server_list_a).intersection(set(server_list_b))) return matching_hosts + +def drop_privileges(uid, gid): + if os.getuid() != 0: + # We're not root so, like, whatever dude + return + + # Get the uid/gid from the name + running_uid = pwd.getpwnam(uid).pw_uid + running_gid = grp.getgrnam(gid).gr_gid + + # Remove group privileges + os.setgroups([]) + + # Try setting the new uid/gid + os.setgid(running_gid) + os.setuid(running_uid) + + # Ensure a very conservative umask + old_umask = os.umask(077) \ No newline at end of file -- Alioth's /srv/git/_hooks_/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git