This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository python-x2go. commit ce97f345fb0e8b3d8cd2c2261242961a61b479c1 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Mar 20 08:36:28 2014 +0100 Handle injection of PKey (Paramiko SSH key) objects for authentication from the broker session profiles backend. --- debian/changelog | 2 ++ x2go/backends/profiles/base.py | 20 ++++++++++++++++++ x2go/backends/profiles/httpbroker.py | 13 ++++++++++++ x2go/client.py | 8 ++++++++ x2go/registry.py | 7 ++++++- x2go/session.py | 2 +- x2go/utils.py | 37 ++++++++++++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index d2bb58a..1907a4b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -13,6 +13,8 @@ python-x2go (0.5.0.0-0x2go1) UNRELEASED; urgency=low - Make session profile backends more unicode robust. - X2GoSessionProfile.get_server_hostname must return unicode objects. - Speed-optimize session profile ID <-> name mapping. + - Handle injection of PKey (Paramiko SSH key) objects for authentication + from the broker session profiles backend. * debian/control: + Add dependencies: python-requests, python-simplejson. * python-x2go.spec: diff --git a/x2go/backends/profiles/base.py b/x2go/backends/profiles/base.py index 3aff841..7630a8a 100644 --- a/x2go/backends/profiles/base.py +++ b/x2go/backends/profiles/base.py @@ -722,3 +722,23 @@ class X2GoSessionProfiles(): """ return 22 + def get_pkey_object(self, profile_id): + """\ + If available, return a PKey (Paramiko/SSH private key) object. + + @param profile_id: the profile's unique ID + @type profile_id: C{str} + + @return: a Paramiko/SSH PKey object + @rtype: C{obj} + + """ + return self._get_pkey_object(profile_id) + + def _get_pkey_object(self, profile_id): + """\ + Inherit from this class and provide a way for actually + providing such a PKey object. + + """ + return None diff --git a/x2go/backends/profiles/httpbroker.py b/x2go/backends/profiles/httpbroker.py index 569e851..30e9b54 100644 --- a/x2go/backends/profiles/httpbroker.py +++ b/x2go/backends/profiles/httpbroker.py @@ -30,6 +30,7 @@ import re import requests import copy import types +import time try: import simplejson as json except ImportError: import json @@ -38,6 +39,7 @@ from x2go.defaults import X2GO_SESSIONPROFILE_DEFAULTS as _X2GO_SESSIONPROFILE_D from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER import x2go.backends.profiles.base as base import x2go.log as log +from x2go.utils import genkeypair from x2go.x2go_exceptions import X2GoNotImplementedYetException @@ -110,6 +112,9 @@ class X2GoSessionProfiles(base.X2GoSessionProfiles): self._broker_type = "http" + # for broker based autologin, we have to be able to provide public/private key pair + self.broker_my_pubkey, self.broker_my_privkey = genkeypair(local_username=_CURRENT_LOCAL_USER, client_address='127.0.0.1') + def get_broker_noauth(self): return self.broker_noauth @@ -190,6 +195,7 @@ class X2GoSessionProfiles(base.X2GoSessionProfiles): 'profile-id': profile_id, 'user': self.broker_username, 'password': self.broker_password, + 'pubkey': self.broker_my_pubkey, } r = requests.post(self.broker_url, data=request_data) if r.status_code == 200 and r.headers['content-type'].startswith("text/json"): @@ -273,3 +279,10 @@ class X2GoSessionProfiles(base.X2GoSessionProfiles): def _get_server_port(self, profile_id): selected_session = self.broker_selectsession(profile_id) return int(selected_session['port']) + + def _get_pkey_object(self, profile_id): + selected_session = self.broker_selectsession(profile_id) + if selected_session.has_key('authentication_pubkey') and selected_session['authentication_pubkey'] == 'ACCEPTED': + time.sleep(2) + return self.broker_my_privkey + return None diff --git a/x2go/client.py b/x2go/client.py index b1fa978..95fb444 100644 --- a/x2go/client.py +++ b/x2go/client.py @@ -924,6 +924,14 @@ class X2GoClient(object): server = self.session_profiles.get_server_hostname(_profile_id) _params['port'] = self.session_profiles.get_server_port(_profile_id) + _pkey = self.session_profiles.get_pkey_object(_profile_id) + if _pkey is not None: + self.logger('received PKey object for authentication, ignoring all other auth mechanisms', log.loglevel_NOTICE, tag=self._logger_tag) + _params['pkey'] = _pkey + _params['allow_agent'] = False + _params['look_for_keys'] = False + _params['key_filename'] = [] + del _params['server'] _params['client_instance'] = self diff --git a/x2go/registry.py b/x2go/registry.py index ffe0708..1625dbd 100644 --- a/x2go/registry.py +++ b/x2go/registry.py @@ -533,6 +533,12 @@ class X2GoSessionRegistry(object): if _k in kwargs.keys(): _params[_k] = kwargs[_k] + # allow injection of PKey objects (Paramiko's private SSH keys) + if kwargs.has_key('pkey'): + _params['pkey'] = kwargs['pkey'] + if kwargs.has_key('sshproxy_pkey'): + _params['sshproxy_pkey'] = kwargs['sshproxy_pkey'] + # when starting a new session, we will try to use unused registered virgin sessions # depending on your application layout, there should either be one or no such virgin session at all _virgin_sessions = [ s for s in self.virgin_sessions_of_profile_name(profile_name, return_objects=True) if not s.activated ] @@ -560,7 +566,6 @@ class X2GoSessionRegistry(object): try: del _params['profile_id'] except: pass - print _params['port'] s = session.X2GoSession(server=server, control_session=control_session, profile_id=profile_id, profile_name=profile_name, session_name=session_name, diff --git a/x2go/session.py b/x2go/session.py index ec2cdc9..c7943ae 100644 --- a/x2go/session.py +++ b/x2go/session.py @@ -325,7 +325,7 @@ class X2GoSession(object): if self.logger.get_loglevel() & log.loglevel_DEBUG: self.logger('X2Go control session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG) - for p in self.control_params: + for p in [ _p for _p in self.control_params if not _p.endswith('pkey') ]: self.logger(' %s: %s' % (p, self.control_params[p]), log.loglevel_DEBUG) self.logger('X2Go terminal session parameters for profile %s:' % profile_name, loglevel=log.loglevel_DEBUG) for p in self.terminal_params: diff --git a/x2go/utils.py b/x2go/utils.py index 7f182ed..78ed642 100644 --- a/x2go/utils.py +++ b/x2go/utils.py @@ -34,6 +34,7 @@ import gevent import string import subprocess import distutils.version +import paramiko # Python X2Go modules from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS @@ -843,3 +844,39 @@ def _get_backend_class(backend, class_name): else: raise x2go_exceptions.X2GoBackendException('unknown backend name %s for class %s' % (backend, class_name)) return _this_class + +def genkeypair(local_username, client_address, key_type='RSA'): + """\ + Generate an SSH pub/priv key pair without writing the private key to file. + + @param local_username: the key is for this user + @type local_username: C{unicode} + @param client_address: the key is only valid for this client + @type client_address: C{unicode} + @param key_type: either of: RSA, DSA + @type key_type: C{unicode} + + """ + key = None + pubkey = None + privkey = None + + # generate key pair + if unicode(key_type) == u'RSA': + key = paramiko.RSAKey.generate(2048) + elif unicode(key_type) == u'DSA': + key = paramiko.DSSKey.generate(1024) + + if key: + + # assemble the public key + if key_type == "RSA": + pubkey_type = 'ssh-rsa' + elif key_type == "DSA": + pubkey_type = 'ssh-dss' + + # FIXME: the from option does not work properly by some reason. Fix it later + #pubkey = "from={client_address},no-X11-forwarding,no-pty,no-user-rc {pubkey_type} {pubkey} {local_username}@{client_address}".format(pubkey=key.get_base64(), pubkey_type=pubkey_type, local_username=local_username, client_address=client_address) + pubkey = "no-X11-forwarding,no-pty,no-user-rc {pubkey_type} {pubkey} {local_username}@{client_address}".format(pubkey=key.get_base64(), pubkey_type=pubkey_type, local_username=local_username, client_address=client_address) + + return (pubkey, key) -- Alioth's /srv/git/_hooks_/post-receive-email on /srv/git/code.x2go.org/python-x2go.git