[X2go-Commits] python-x2go.git - master (branch) updated: 0.2.1.1-20-g7914c64

X2Go dev team git-admin at x2go.org
Sun Jan 13 21:53:05 CET 2013


The branch, master 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 -----------------------------------------------------------------
commit 7914c64237973c66222fc35094ccc34b6d921063
Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
Date:   Sun Jan 13 21:54:32 2013 +0100

    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.

-----------------------------------------------------------------------

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