[X2Go-Commits] python-x2go.git - build-baikal (branch) updated: 0.2.1.1-20-g7914c64

X2Go dev team git-admin at x2go.org
Wed Jan 8 15:27:30 CET 2014


The branch, build-baikal has been updated
       via  7914c64237973c66222fc35094ccc34b6d921063 (commit)
      from  e1b34e08af86bd3c56119cf467dfae5520c07fac (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 debian/changelog                 |    5 ++
 x2go/backends/control/_stdout.py |   13 ++-
 x2go/backends/profiles/_file.py  |   40 ++++++---
 x2go/checkhosts.py               |  178 ++++++++++++++++++++++----------------
 x2go/defaults.py                 |    1 +
 x2go/utils.py                    |    1 +
 6 files changed, 152 insertions(+), 86 deletions(-)

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index 33a0f83..223faed 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -14,6 +14,11 @@ python-x2go (0.2.2.0-0~x2go1) UNRELEASED; urgency=low
     - Avoid the known_hosts file being flushed with localhost:[<someport>]
       entries. Store host keys of SSH-proxied hosts under the [<address>]:<port>
       the system has _behind_ the SSH proxy gateway.
+    - Add session profile option: uniquehostkeyaliases. Allow the
+      (by-design-unique) X2Go session profile ID to be a representative for
+      <hostname>:<port>. Update session profile IDs on hostname changes.
+      Re-arrange class structure for MissingHostKey policies, also provide an
+      X2goAutoAddPolicy class.
 
  -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Thu, 20 Dec 2012 08:58:44 +0100
 
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index 2112f14..ef8da9c 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -118,6 +118,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                  add_to_known_hosts=False,
                  known_hosts=None,
                  forward_sshagent=False,
+                 unique_hostkey_aliases=False,
                  terminal_backend=_X2goTerminalSession,
                  info_backend=_X2goServerSessionInfo,
                  list_backend=_X2goServerSessionList,
@@ -143,6 +144,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @type known_hosts: C{str}
         @param forward_sshagent: forward SSH agent authentication requests to the X2Go client-side
         @type forward_sshagent: C{bool}
+        @param unique_hostkey_aliases: instead of storing [<hostname>]:<port> in known_hosts file, use the
+            (unique-by-design) profile ID
+        @type unique_hostkey_aliases: C{bool}
         @param terminal_backend: X2Go terminal session backend to use
         @type terminal_backend: C{class}
         @param info_backend: backend for handling storage of server session information
@@ -177,6 +181,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         self.add_to_known_hosts = add_to_known_hosts
         self.known_hosts = known_hosts
         self.forward_sshagent = forward_sshagent
+        self.unique_hostkey_aliases = unique_hostkey_aliases
 
         self.hostname = None
         self.port = None
@@ -617,6 +622,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                 sshproxy_key_filename='', sshproxy_pkey=None, sshproxy_look_for_keys=False, sshproxy_allow_agent=False,
                 sshproxy_tunnel='',
                 forward_sshagent=None,
+                unique_hostkey_aliases=None,
                 session_instance=None,
                 add_to_known_hosts=False, force_password_auth=False):
         """\
@@ -660,6 +666,8 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @param forward_sshagent: forward SSH agent authentication requests to the X2Go client-side
             (will update the class property of the same name)
         @type forward_sshagent: C{bool}
+        @param unique_hostkey_aliases: update the unique_hostkey_aliases class property
+        @type unique_hostkey_aliases: C{bool}
         @param timeout: an optional timeout (in seconds) for the TCP connect
         @type timeout: float
         @param look_for_keys: set to C{True} to enable searching for discoverable
@@ -720,6 +728,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         """
         _fake_hostname = None
 
+        if unique_hostkey_aliases is not None:
+            self.unique_hostkey_aliases = unique_hostkey_aliases
+
         if use_sshproxy and sshproxy_host and sshproxy_user:
             try:
 
@@ -763,7 +774,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
             self.set_missing_host_key_policy(checkhosts.X2goInteractiveAddPolicy(caller=self, session_instance=session_instance, fake_hostname=_fake_hostname))
 
         if add_to_known_hosts:
-            self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+            self.set_missing_host_key_policy(checkhosts.X2goAutoAddPolicy(caller=self, session_instance=session_instance, fake_hostname=_fake_hostname))
 
         # disable pub/priv key authentication if forced
         if force_password_auth:
diff --git a/x2go/backends/profiles/_file.py b/x2go/backends/profiles/_file.py
index fbbf396..eb23582 100644
--- a/x2go/backends/profiles/_file.py
+++ b/x2go/backends/profiles/_file.py
@@ -67,6 +67,7 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         self._profile_metatypes = {}
         self._cached_profile_ids = []
         self._cached_profile_names = []
+        self._profiles_need_profile_id_renewal = []
 
         if logger is None:
             self.logger = log.X2goLogger(loglevel=loglevel)
@@ -115,7 +116,6 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         @rtype: C{str}
 
         """
-
         _profile_id = self.check_profile_id_or_name(profile_id_or_name)
         if not self._profile_metatypes.has_key(_profile_id) or force:
             _config = self.get_profile_config(_profile_id)
@@ -146,6 +146,23 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         else:
             return self._profile_metatypes[_profile_id]
 
+    def write(self):
+
+
+        # then update profile IDs for profiles that have a renamed host attribute...
+        for profile_id in self._profiles_need_profile_id_renewal:
+            _config = self.get_profile_config(profile_id=profile_id)
+            self.iniConfig.remove_section(profile_id)
+            try: self._cached_profile_ids.remove(profile_id)
+            except ValueError: pass
+            try: self._cached_profile_names.remove(_config['name'])
+            except ValueError: pass
+            self.add_profile(profile_id=None, **_config)
+
+        # at last write the profile config as is...
+        inifiles.X2goIniFile.write(self)
+
+
     def get_profile_option_type(self, option):
         """\
         Get the data type for a specific session profile option.
@@ -345,17 +362,12 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         if profile_id is None:
             profile_id = utils._genSessionProfileId()
         for key, value in kwargs.items():
-            if key in self.defaultSessionProfile:
-                self.update_value(profile_id, key, value)
-            else:
-                raise X2goProfileException('keyword ,,%s\'\' not supported in X2Go session profile' % key)
+            self.update_value(None, key, value, profile_id=profile_id)
 
         for key, value in self.defaultSessionProfile.items():
             if key in kwargs: continue
-            self.update_value(profile_id, key, value)
+            self.update_value(None, key, value, profile_id=profile_id)
 
-        self._cached_profile_ids = []
-        self._cached_profile_names = []
 
         return profile_id
 
@@ -365,6 +377,8 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
 
         @param profile_id_or_name: profile ID or profile name
         @type profile_id_or_name: C{str}
+        @param skip_writing: do not perform a write operation after deleting the session profile
+        @type skip_writing: C{bool}
 
         """
         _profile_id = self.check_profile_id_or_name(profile_id_or_name)
@@ -374,7 +388,7 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
         self._cached_profile_ids = []
         self._cached_profile_names = []
 
-    def update_value(self, section, key, value):
+    def update_value(self, section, key, value, profile_id=None):
         """\
         Update a value in a session profile.
 
@@ -387,7 +401,7 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
 
         """
         try:
-            profile_id = self.check_profile_id_or_name(section)
+            profile_id = profile_id or self.check_profile_id_or_name(section)
         except X2goProfileException:
             profile_id = section
         if key == 'name':
@@ -401,7 +415,6 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
             self._cached_profile_names = []
 
         if key == 'export' and type(value) == types.DictType:
-
             _strvalue = '"'
             for folder in value.keys():
                 _strvalue += "%s:%s;" % (folder, int(value[folder]))
@@ -409,6 +422,11 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
             _strvalue = _strvalue.replace('""', '')
             value = _strvalue
 
+        if key == 'host':
+            _config = self.get_profile_config(profile_id=profile_id)
+            if _config.has_key('host') and _config['host'] != value:
+                self._profiles_need_profile_id_renewal.append(profile_id)
+
         inifiles.X2goIniFile.update_value(self, profile_id, key, value)
 
     def check_profile_id_or_name(self, profile_id_or_name):
diff --git a/x2go/checkhosts.py b/x2go/checkhosts.py
index aedb08d..f1bda26 100644
--- a/x2go/checkhosts.py
+++ b/x2go/checkhosts.py
@@ -34,20 +34,9 @@ import x2go_exceptions
 import random
 import string
 
-class X2goInteractiveAddPolicy(paramiko.MissingHostKeyPolicy):
+class X2goMissingHostKeyPolicy(paramiko.MissingHostKeyPolicy):
     """\
-    Policy for making host key information available to Python X2Go after a
-    Paramiko/SSH connect has been attempted. This class needs information
-    about the associated L{X2goSession} instance.
-
-    Once called, the L{missing_host_key} method of this class will try to call
-    L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
-    in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
-    which then will return C{True} by default if not customized in your application.
-
-    To accept host key checks, make sure to either customize the 
-    L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
-    method and hook some interactive user dialog to either of them.
+    Skeleton class for Python X2Go's missing host key policies.
 
     """
     def __init__(self, caller=None, session_instance=None, fake_hostname=None):
@@ -62,67 +51,6 @@ class X2goInteractiveAddPolicy(paramiko.MissingHostKeyPolicy):
         self.session_instance = session_instance
         self.fake_hostname = fake_hostname
 
-    def missing_host_key(self, client, hostname, key):
-        """\
-        Handle a missing host key situation. This method calls
-
-        Once called, the L{missing_host_key} method will try to call
-        L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
-        in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
-        which then will return C{True} by default if not customized in your application.
-
-        To accept host key checks, make sure to either customize the 
-        L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
-        method and hook some interactive user dialog to either of them.
-
-        @param client: SSH client (C{X2goControlSession*}) instance
-        @type client: C{X2goControlSession*} instance
-        @param hostname: remote hostname
-        @type hostname: C{str}
-        @param key: host key to validate
-        @type key: Paramiko/SSH key instance
-
-        @raise X2goHostKeyException: if the X2Go server host key is not in the C{known_hosts} file
-        @raise X2goSSHProxyHostKeyException: if the SSH proxy host key is not in the C{known_hosts} file
-        @raise SSHException: if this instance does not know its {self.session_instance}
-
-        """
-        self.client = client
-        self.hostname = hostname
-        if (self.hostname.find(']') == -1) and (self.hostname.find(':') == -1):
-            # if hostname is an IPv4 quadruple with standard SSH port...
-            self.hostname = '[%s]:22' % self.hostname
-        self.key = key
-        client._log(paramiko.common.DEBUG, 'Interactively Checking %s host key for %s: %s' %
-                   (self.key.get_name(), self.hostname, binascii.hexlify(self.key.get_fingerprint())))
-        if self.session_instance:
-
-            if self.fake_hostname is not None:
-                server_key = client.get_transport().get_remote_server_key()
-                keytype = server_key.get_name()
-                our_server_key = client._system_host_keys.get(self.fake_hostname, {}).get(keytype, None)
-                if our_server_key is None:
-                    our_server_key = client._host_keys.get(self.fake_hostname, {}).get(keytype, None)
-                if our_server_key is not None:
-                    self.session_instance.logger('SSH host key verification for SSH-proxied host %s with %s fingerprint ,,%s\'\' succeeded. This host is known by the address it has behind the SSH proxy host.' % (self.fake_hostname, self.get_key_name(), self.get_key_fingerprint_with_colons()), loglevel=log.loglevel_NOTICE)
-                    return
-
-            self.session_instance.logger('SSH host key verification for host %s with %s fingerprint ,,%s\'\' initiated. We are seeing this X2Go server for the first time.' % (self.get_hostname(), self.get_key_name(), self.get_key_fingerprint_with_colons()), loglevel=log.loglevel_NOTICE)
-            _valid = self.session_instance.HOOK_check_host_dialog(self.get_hostname_name(),
-                                                                  port=self.get_hostname_port(),
-                                                                  fingerprint=self.get_key_fingerprint_with_colons(),
-                                                                  fingerprint_type=self.get_key_name(),
-                                                                 )
-            if _valid:
-                paramiko.AutoAddPolicy().missing_host_key(client, self.get_hostname(), key)
-            else:
-                if type(self.caller) in (sshproxy.X2goSSHProxy, ):
-                    raise x2go_exceptions.X2goSSHProxyHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % self.get_hostname())
-                else:
-                    raise x2go_exceptions.X2goHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % self.get_hostname())
-        else:
-            raise x2go_exceptions.SSHException('Policy has collected host key information on %s for further introspection' % self.get_hostname())
-
     def get_client(self):
         """\
         Retrieve the Paramiko SSH/Client.
@@ -213,6 +141,108 @@ class X2goInteractiveAddPolicy(paramiko.MissingHostKeyPolicy):
         return _colon_fingerprint.rstrip(':')
 
 
+class X2goAutoAddPolicy(X2goMissingHostKeyPolicy):
+
+    def missing_host_key(self, client, hostname, key):
+        self.client = client
+        self.hostname = hostname
+        self.key = key
+        if self.session_instance and self.session_instance.control_session.unique_hostkey_aliases:
+            self.client._host_keys.add(self.session_instance.get_profile_id(), self.key.get_name(), self.key)
+        else:
+            self.client._host_keys.add(self.get_hostname(), self.key.get_name(), self.key)
+        if self.client._host_keys_filename is not None:
+            self.client.save_host_keys(self.client._host_keys_filename)
+        self.client._log(paramiko.common.DEBUG, 'Adding %s host key for %s: %s' %
+                         (self.key.get_name(), self.get_hostname(), binascii.hexlify(self.key.get_fingerprint())))
+
+
+class X2goInteractiveAddPolicy(X2goMissingHostKeyPolicy):
+    """\
+    Policy for making host key information available to Python X2Go after a
+    Paramiko/SSH connect has been attempted. This class needs information
+    about the associated L{X2goSession} instance.
+
+    Once called, the L{missing_host_key} method of this class will try to call
+    L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
+    in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
+    which then will return C{True} by default if not customized in your application.
+
+    To accept host key checks, make sure to either customize the 
+    L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
+    method and hook some interactive user dialog to either of them.
+
+    """
+    def missing_host_key(self, client, hostname, key):
+        """\
+        Handle a missing host key situation. This method calls
+
+        Once called, the L{missing_host_key} method will try to call
+        L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
+        in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
+        which then will return C{True} by default if not customized in your application.
+
+        To accept host key checks, make sure to either customize the 
+        L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
+        method and hook some interactive user dialog to either of them.
+
+        @param client: SSH client (C{X2goControlSession*}) instance
+        @type client: C{X2goControlSession*} instance
+        @param hostname: remote hostname
+        @type hostname: C{str}
+        @param key: host key to validate
+        @type key: Paramiko/SSH key instance
+
+        @raise X2goHostKeyException: if the X2Go server host key is not in the C{known_hosts} file
+        @raise X2goSSHProxyHostKeyException: if the SSH proxy host key is not in the C{known_hosts} file
+        @raise SSHException: if this instance does not know its {self.session_instance}
+
+        """
+        self.client = client
+        self.hostname = hostname
+        if (self.hostname.find(']') == -1) and (self.hostname.find(':') == -1):
+            # if hostname is an IPv4 quadruple with standard SSH port...
+            self.hostname = '[%s]:22' % self.hostname
+        self.key = key
+        self.client._log(paramiko.common.DEBUG, 'Interactively Checking %s host key for %s: %s' %
+                         (self.key.get_name(), self.get_hostname(), binascii.hexlify(self.key.get_fingerprint())))
+        if self.session_instance:
+
+            if self.fake_hostname is not None:
+                server_key = client.get_transport().get_remote_server_key()
+                keytype = server_key.get_name()
+                our_server_key = client._system_host_keys.get(self.fake_hostname, {}).get(keytype, None)
+                if our_server_key is None:
+                    if self.session_instance.control_session.unique_hostkey_aliases:
+                        our_server_key = client._host_keys.get(self.session_instance.get_profile_id(), {}).get(keytype, None)
+                        self.session_instance.logger('SSH host key verification for SSH-proxied host %s with %s fingerprint ,,%s\'\' succeeded. This host is known by the X2Go session profile ID of profile »%s«.' % (self.fake_hostname, self.get_key_name(), self.get_key_fingerprint_with_colons(), self.session_instance.profile_name), loglevel=log.loglevel_NOTICE)
+                    else:
+                        our_server_key = client._host_keys.get(self.fake_hostname, {}).get(keytype, None)
+                        self.session_instance.logger('SSH host key verification for SSH-proxied host %s with %s fingerprint ,,%s\'\' succeeded. This host is known by the address it has behind the SSH proxy host.' % (self.fake_hostname, self.get_key_name(), self.get_key_fingerprint_with_colons()), loglevel=log.loglevel_NOTICE)
+                if our_server_key is not None:
+                    return
+
+            self.session_instance.logger('SSH host key verification for host %s with %s fingerprint ,,%s\'\' initiated. We are seeing this X2Go server for the first time.' % (self.get_hostname(), self.get_key_name(), self.get_key_fingerprint_with_colons()), loglevel=log.loglevel_NOTICE)
+            _valid = self.session_instance.HOOK_check_host_dialog(self.get_hostname_name(),
+                                                                  port=self.get_hostname_port(),
+                                                                  fingerprint=self.get_key_fingerprint_with_colons(),
+                                                                  fingerprint_type=self.get_key_name(),
+                                                                 )
+            if _valid:
+                if self.session_instance.control_session.unique_hostkey_aliases:
+                    paramiko.AutoAddPolicy().missing_host_key(client, self.session_instance.get_profile_id(), key)
+                else:
+                    paramiko.AutoAddPolicy().missing_host_key(client, self.get_hostname(), key)
+
+            else:
+                if type(self.caller) in (sshproxy.X2goSSHProxy, ):
+                    raise x2go_exceptions.X2goSSHProxyHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % self.get_hostname())
+                else:
+                    raise x2go_exceptions.X2goHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % self.get_hostname())
+        else:
+            raise x2go_exceptions.SSHException('Policy has collected host key information on %s for further introspection' % self.get_hostname())
+
+
 def check_ssh_host_key(x2go_sshclient_instance, hostname, port=22):
     """\
     Perform a Paramiko/SSH host key check by connecting to the host and
diff --git a/x2go/defaults.py b/x2go/defaults.py
index 8689608..193ef90 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -297,6 +297,7 @@ X2GO_SESSIONPROFILE_DEFAULTS = {
     'iconvto': 'UTF-8', 'iconvfrom': 'UTF-8', 'useiconv': False,
     'usesshproxy': False, 'sshproxyhost': 'proxyhost.mydomain', 'sshproxyport': 22, 'sshproxyuser': '', 'sshproxykeyfile': '',
     'sshproxytype': 'SSH', 'sshproxysameuser': False, 'sshproxysamepass': False, 'sshproxyautologin': True,
+    'uniquehostkeyaliases': False,
     'useexports': True, 'restoreexports': False, 'fstunnel': True, 'export': '',
     'usemimebox': False, 'mimeboxextensions': '', 'mimeboxaction': 'OPEN',
     'fullscreen': False,
diff --git a/x2go/utils.py b/x2go/utils.py
index a83c78a..a1dbab5 100644
--- a/x2go/utils.py
+++ b/x2go/utils.py
@@ -213,6 +213,7 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
             'forwardsshagent': 'forward_sshagent',
             'autologin': 'look_for_keys',
             'sshproxyautologin': 'sshproxy_look_for_keys',
+            'uniquehostkeyaliases': 'unique_hostkey_aliases',
     }
     _speed_dict = {
             '0': 'modem',


hooks/post-receive
-- 
python-x2go.git (Python X2Go Client API)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-x2go.git" (Python X2Go Client API).




More information about the x2go-commits mailing list