[Checkins] SVN: zope.app.fssync/trunk/ - The ssh transport now looks for known_hosts in an application specific file,
Aaron Lehmann
aaron at zope.com
Tue Jan 5 10:34:32 EST 2010
Log message for revision 107705:
- The ssh transport now looks for known_hosts in an application specific file,
as well as the normal known_hosts file and in the user's Agent. This file is
~/.ssh/fssync_known_hosts if POSIX and ~/ssh/fssync_known_hosts if win32.
- BUGFIX: The ssh transport now will prompt the user if he wishes to use an
unrecognized hostkey. If he says 'yes', it will be added to the fssync
known_hosts file. if he says 'no', an exception is raised.
- BUGFIX: If the user's public key is encrypted, fssync will prompt for a
password.
Changed:
U zope.app.fssync/trunk/CHANGES.txt
U zope.app.fssync/trunk/setup.py
U zope.app.fssync/trunk/src/zope/app/fssync/ssh.py
-=-
Modified: zope.app.fssync/trunk/CHANGES.txt
===================================================================
--- zope.app.fssync/trunk/CHANGES.txt 2010-01-05 14:02:42 UTC (rev 107704)
+++ zope.app.fssync/trunk/CHANGES.txt 2010-01-05 15:34:31 UTC (rev 107705)
@@ -1,6 +1,20 @@
Changes
=======
+3.6
+---
+
+- The ssh transport now looks for known_hosts in an application specific file,
+ as well as the normal known_hosts file and in the user's Agent. This file is
+ ~/.ssh/fssync_known_hosts if POSIX and ~/ssh/fssync_known_hosts if win32.
+
+- BUGFIX: The ssh transport now will prompt the user if he wishes to use an
+ unrecognized hostkey. If he says 'yes', it will be added to the fssync
+ known_hosts file. if he says 'no', an exception is raised.
+
+- BUGFIX: If the user's public key is encrypted, fssync will prompt for a
+ password.
+
3.5
---
Modified: zope.app.fssync/trunk/setup.py
===================================================================
--- zope.app.fssync/trunk/setup.py 2010-01-05 14:02:42 UTC (rev 107704)
+++ zope.app.fssync/trunk/setup.py 2010-01-05 15:34:31 UTC (rev 107705)
@@ -6,7 +6,7 @@
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
setup(name='zope.app.fssync',
- version = '3.5',
+ version = '3.6dev',
url = 'http://pypi.python.org/pypi/zope.app.fssync',
license = 'ZPL 2.1',
description = "Filesystem synchronization utility for Zope 3.",
Modified: zope.app.fssync/trunk/src/zope/app/fssync/ssh.py
===================================================================
--- zope.app.fssync/trunk/src/zope/app/fssync/ssh.py 2010-01-05 14:02:42 UTC (rev 107704)
+++ zope.app.fssync/trunk/src/zope/app/fssync/ssh.py 2010-01-05 15:34:31 UTC (rev 107705)
@@ -15,8 +15,8 @@
import httplib
import os.path
import socket
-
import paramiko
+import sys
class FileSocket(object):
@@ -30,92 +30,78 @@
return self.file
+class ConfirmationPolicy(paramiko.WarningPolicy, paramiko.RejectPolicy, paramiko.AutoAddPolicy):
+ def _add_key(self, client, hostname, key):
+ paramiko.AutoAddPolicy.missing_host_key(self, client, hostname, key)
+ client.save_host_keys(SSHConnection.key_file_name)
+
+ def missing_host_key(self, client, hostname, key):
+ paramiko.WarningPolicy.missing_host_key(self, client, hostname, key)
+ answer = raw_input("Are you sure you want to continue connecting (yes/no)? ")
+ yes_no = {'no': paramiko.RejectPolicy.missing_host_key,
+ 'yes': ConfirmationPolicy._add_key,}
+ while answer.lower() not in yes_no:
+ print "Please type 'yes' or 'no'."
+ answer = raw_input("Are you sure you want to continue connecting (yes/no)? ")
+ return yes_no[answer.lower()](self, client, hostname, key)
+
+
class SSHConnection(object):
"""
SSH connection that implements parts of the httplib.HTTPConnection
interface
"""
- def __init__(self, host_port, user_passwd=None):
- self.headers = {}
- self.host, self.port = host_port.split(':')
- self.port = int(self.port)
+ if sys.platform == 'win32':
+ sys_key_file_name = os.path.expanduser('~/ssh/known_hosts')
+ key_file_name = os.path.expanduser('~/ssh/fssync_known_hosts')
+ else:
+ sys_key_file_name = os.path.expanduser('~/.ssh/known_hosts')
+ key_file_name = os.path.expanduser('~/.ssh/fssync_known_hosts')
- # if username is specified in URL then use it, otherwise
- # default to local userid
- if user_passwd:
- self.remote_user_name = user_passwd.split(':')[0]
+ # This and the __new__ method are to ensure that the SSHConnection doesn't
+ # get garbage collected by paramiko too early.
+ clients = {}
+
+ def __new__(cls, host_port, user_passwd=None):
+ if host_port in cls.clients:
+ return cls.clients[host_port]
else:
- self.remote_user_name = getpass.getuser()
+ cls.clients[host_port] = object.__new__(
+ cls, host_port, user_passwd)
+ return cls.clients[host_port]
- def putrequest(self, method, path):
- # open connection to server
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect((self.host, self.port))
- self.transport = paramiko.Transport(sock)
- self.transport.start_client()
+ def __init__(self, host_port, user_passwd=None):
+ if not hasattr(self, 'headers'):
+ self.headers = {}
+ self.host, self.port = host_port.split(':')
+ self.port = int(self.port)
- # try to get public key from ssh agent
- agent = paramiko.Agent()
- for key in agent.get_keys():
+ # if username is specified in URL then use it, otherwise
+ # default to local userid
+ if user_passwd:
+ self.remote_user_name = user_passwd.split(':')[0]
+ else:
+ self.remote_user_name = getpass.getuser()
+ self.client = paramiko.SSHClient()
+ self.client.set_missing_host_key_policy(ConfirmationPolicy())
+ self.client.load_system_host_keys(self.sys_key_file_name)
try:
- self.transport.auth_publickey(self.remote_user_name, key)
- break
- except paramiko.SSHException:
+ self.client.load_host_keys(self.key_file_name)
+ except IOError, ioe:
+ # This will get handled later in the ConfirmationPolicy. We can
+ # pass for now.
pass
-
- # try to get public key from fs
- if not self.transport.is_authenticated():
- path = os.path.expanduser('~/.ssh/id_rsa')
try:
- key = paramiko.RSAKey.from_private_key_file(path)
- except paramiko.PasswordRequiredException:
- password = getpass.getpass('RSA key password: ')
- key = paramiko.RSAKey.from_private_key_file(path, password)
- try:
- self.transport.auth_publickey(self.remote_user_name, key)
- except paramiko.SSHException:
- pass
+ self.client.connect(self.host, self.port, self.remote_user_name)
+ except paramiko.PasswordRequiredException, pre:
+ password = getpass.getpass("Password to unlock password-protected key? ")
+ self.client.connect(
+ self.host, self.port, self.remote_user_name, password=password)
- if not self.transport.is_authenticated():
- path = os.path.expanduser('~/.ssh/id_dsa')
- try:
- key = paramiko.DSSKey.from_private_key_file(path)
- except paramiko.PasswordRequiredException:
- password = getpass.getpass('DSS key password: ')
- key = paramiko.DSSKey.from_private_key_file(path, password)
- try:
- self.transport.auth_publickey(self.remote_user_name, key)
- except paramiko.SSHException:
- raise Exception('No valid public key found')
- # try to get host key
- hostkeytype = None
- hostkey = None
- try:
- host_keys = paramiko.util.load_host_keys(
- os.path.expanduser('~/.ssh/known_hosts'))
- except IOError:
- try:
- # try ~/ssh/ too, because windows can't have a folder
- # named ~/.ssh/
- host_keys = paramiko.util.load_host_keys(
- os.path.expanduser('~/ssh/known_hosts'))
- except IOError:
- host_keys = {}
-
- if host_keys.has_key(self.host):
- hostkeytype = host_keys[self.host].keys()[0]
- hostkey = host_keys[self.host][hostkeytype]
-
- # verify host key
- if hostkey:
- server_key = self.transport.get_server_key()
- if server_key != hostkey:
- raise Exception(
- "Remote host key doesn't match value in known_hosts")
-
+ def putrequest(self, method, path):
# start zsync subsystem on server
- self.channel = self.transport.open_session()
+ self.channel = self.client.get_transport().open_session()
self.channel.invoke_subsystem('zsync')
self.channelr = self.channel.makefile('rb')
self.channelw = self.channel.makefile('wb')
@@ -123,6 +109,7 @@
# start sending request
self.channelw.write('%s %s\r\n' % (method, path))
+
def putheader(self, name, value):
self.channelw.write('%s: %s\r\n' % (name, value))
More information about the checkins
mailing list