Host key docs (order + tweaks)
This commit is contained in:
parent
1556853713
commit
4bcac18d17
|
@ -16,9 +16,6 @@
|
||||||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
`.HostKeys`
|
|
||||||
"""
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
|
@ -31,95 +28,21 @@ from paramiko.rsakey import RSAKey
|
||||||
from paramiko.util import get_logger, constant_time_bytes_eq
|
from paramiko.util import get_logger, constant_time_bytes_eq
|
||||||
|
|
||||||
|
|
||||||
class InvalidHostKey(Exception):
|
|
||||||
|
|
||||||
def __init__(self, line, exc):
|
|
||||||
self.line = line
|
|
||||||
self.exc = exc
|
|
||||||
self.args = (line, exc)
|
|
||||||
|
|
||||||
|
|
||||||
class HostKeyEntry:
|
|
||||||
"""
|
|
||||||
Representation of a line in an OpenSSH-style "known hosts" file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, hostnames=None, key=None):
|
|
||||||
self.valid = (hostnames is not None) and (key is not None)
|
|
||||||
self.hostnames = hostnames
|
|
||||||
self.key = key
|
|
||||||
|
|
||||||
def from_line(cls, line, lineno=None):
|
|
||||||
"""
|
|
||||||
Parses the given line of text to find the names for the host,
|
|
||||||
the type of key, and the key data. The line is expected to be in the
|
|
||||||
format used by the openssh known_hosts file.
|
|
||||||
|
|
||||||
Lines are expected to not have leading or trailing whitespace.
|
|
||||||
We don't bother to check for comments or empty lines. All of
|
|
||||||
that should be taken care of before sending the line to us.
|
|
||||||
|
|
||||||
:param line: a line from an OpenSSH known_hosts file
|
|
||||||
:type line: str
|
|
||||||
"""
|
|
||||||
log = get_logger('paramiko.hostkeys')
|
|
||||||
fields = line.split(' ')
|
|
||||||
if len(fields) < 3:
|
|
||||||
# Bad number of fields
|
|
||||||
log.info("Not enough fields found in known_hosts in line %s (%r)" %
|
|
||||||
(lineno, line))
|
|
||||||
return None
|
|
||||||
fields = fields[:3]
|
|
||||||
|
|
||||||
names, keytype, key = fields
|
|
||||||
names = names.split(',')
|
|
||||||
|
|
||||||
# Decide what kind of key we're looking at and create an object
|
|
||||||
# to hold it accordingly.
|
|
||||||
try:
|
|
||||||
if keytype == 'ssh-rsa':
|
|
||||||
key = RSAKey(data=base64.decodestring(key))
|
|
||||||
elif keytype == 'ssh-dss':
|
|
||||||
key = DSSKey(data=base64.decodestring(key))
|
|
||||||
else:
|
|
||||||
log.info("Unable to handle key of type %s" % (keytype,))
|
|
||||||
return None
|
|
||||||
except binascii.Error, e:
|
|
||||||
raise InvalidHostKey(line, e)
|
|
||||||
|
|
||||||
return cls(names, key)
|
|
||||||
from_line = classmethod(from_line)
|
|
||||||
|
|
||||||
def to_line(self):
|
|
||||||
"""
|
|
||||||
Returns a string in OpenSSH known_hosts file format, or None if
|
|
||||||
the object is not in a valid state. A trailing newline is
|
|
||||||
included.
|
|
||||||
"""
|
|
||||||
if self.valid:
|
|
||||||
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
|
|
||||||
self.key.get_base64())
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
|
|
||||||
|
|
||||||
|
|
||||||
class HostKeys (UserDict.DictMixin):
|
class HostKeys (UserDict.DictMixin):
|
||||||
"""
|
"""
|
||||||
Representation of an openssh-style "known hosts" file. Host keys can be
|
Representation of an OpenSSH-style "known hosts" file. Host keys can be
|
||||||
read from one or more files, and then individual hosts can be looked up to
|
read from one or more files, and then individual hosts can be looked up to
|
||||||
verify server keys during SSH negotiation.
|
verify server keys during SSH negotiation.
|
||||||
|
|
||||||
A HostKeys object can be treated like a dict; any dict lookup is equivalent
|
A `.HostKeys` object can be treated like a dict; any dict lookup is
|
||||||
to calling `lookup`.
|
equivalent to calling `lookup`.
|
||||||
|
|
||||||
.. versionadded:: 1.5.3
|
.. versionadded:: 1.5.3
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, filename=None):
|
def __init__(self, filename=None):
|
||||||
"""
|
"""
|
||||||
Create a new HostKeys object, optionally loading keys from an openssh
|
Create a new HostKeys object, optionally loading keys from an OpenSSH
|
||||||
style host-key file.
|
style host-key file.
|
||||||
|
|
||||||
:param filename: filename to load host keys from, or ``None``
|
:param filename: filename to load host keys from, or ``None``
|
||||||
|
@ -150,7 +73,7 @@ class HostKeys (UserDict.DictMixin):
|
||||||
|
|
||||||
def load(self, filename):
|
def load(self, filename):
|
||||||
"""
|
"""
|
||||||
Read a file of known SSH host keys, in the format used by openssh.
|
Read a file of known SSH host keys, in the format used by OpenSSH.
|
||||||
This type of file unfortunately doesn't exist on Windows, but on
|
This type of file unfortunately doesn't exist on Windows, but on
|
||||||
posix, it will usually be stored in
|
posix, it will usually be stored in
|
||||||
``os.path.expanduser("~/.ssh/known_hosts")``.
|
``os.path.expanduser("~/.ssh/known_hosts")``.
|
||||||
|
@ -181,7 +104,7 @@ class HostKeys (UserDict.DictMixin):
|
||||||
|
|
||||||
def save(self, filename):
|
def save(self, filename):
|
||||||
"""
|
"""
|
||||||
Save host keys into a file, in the format used by openssh. The order of
|
Save host keys into a file, in the format used by OpenSSH. The order of
|
||||||
keys in the file will be preserved when possible (if these keys were
|
keys in the file will be preserved when possible (if these keys were
|
||||||
loaded from a file originally). The single exception is that combined
|
loaded from a file originally). The single exception is that combined
|
||||||
lines will be split into individual key lines, which is arguably a bug.
|
lines will be split into individual key lines, which is arguably a bug.
|
||||||
|
@ -314,7 +237,7 @@ class HostKeys (UserDict.DictMixin):
|
||||||
|
|
||||||
def hash_host(hostname, salt=None):
|
def hash_host(hostname, salt=None):
|
||||||
"""
|
"""
|
||||||
Return a "hashed" form of the hostname, as used by openssh when storing
|
Return a "hashed" form of the hostname, as used by OpenSSH when storing
|
||||||
hashed hostnames in the known_hosts file.
|
hashed hostnames in the known_hosts file.
|
||||||
|
|
||||||
:param hostname: the hostname to hash
|
:param hostname: the hostname to hash
|
||||||
|
@ -336,3 +259,75 @@ class HostKeys (UserDict.DictMixin):
|
||||||
return hostkey.replace('\n', '')
|
return hostkey.replace('\n', '')
|
||||||
hash_host = staticmethod(hash_host)
|
hash_host = staticmethod(hash_host)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidHostKey(Exception):
|
||||||
|
def __init__(self, line, exc):
|
||||||
|
self.line = line
|
||||||
|
self.exc = exc
|
||||||
|
self.args = (line, exc)
|
||||||
|
|
||||||
|
|
||||||
|
class HostKeyEntry:
|
||||||
|
"""
|
||||||
|
Representation of a line in an OpenSSH-style "known hosts" file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hostnames=None, key=None):
|
||||||
|
self.valid = (hostnames is not None) and (key is not None)
|
||||||
|
self.hostnames = hostnames
|
||||||
|
self.key = key
|
||||||
|
|
||||||
|
def from_line(cls, line, lineno=None):
|
||||||
|
"""
|
||||||
|
Parses the given line of text to find the names for the host,
|
||||||
|
the type of key, and the key data. The line is expected to be in the
|
||||||
|
format used by the OpenSSH known_hosts file.
|
||||||
|
|
||||||
|
Lines are expected to not have leading or trailing whitespace.
|
||||||
|
We don't bother to check for comments or empty lines. All of
|
||||||
|
that should be taken care of before sending the line to us.
|
||||||
|
|
||||||
|
:param line: a line from an OpenSSH known_hosts file
|
||||||
|
:type line: str
|
||||||
|
"""
|
||||||
|
log = get_logger('paramiko.hostkeys')
|
||||||
|
fields = line.split(' ')
|
||||||
|
if len(fields) < 3:
|
||||||
|
# Bad number of fields
|
||||||
|
log.info("Not enough fields found in known_hosts in line %s (%r)" %
|
||||||
|
(lineno, line))
|
||||||
|
return None
|
||||||
|
fields = fields[:3]
|
||||||
|
|
||||||
|
names, keytype, key = fields
|
||||||
|
names = names.split(',')
|
||||||
|
|
||||||
|
# Decide what kind of key we're looking at and create an object
|
||||||
|
# to hold it accordingly.
|
||||||
|
try:
|
||||||
|
if keytype == 'ssh-rsa':
|
||||||
|
key = RSAKey(data=base64.decodestring(key))
|
||||||
|
elif keytype == 'ssh-dss':
|
||||||
|
key = DSSKey(data=base64.decodestring(key))
|
||||||
|
else:
|
||||||
|
log.info("Unable to handle key of type %s" % (keytype,))
|
||||||
|
return None
|
||||||
|
except binascii.Error, e:
|
||||||
|
raise InvalidHostKey(line, e)
|
||||||
|
|
||||||
|
return cls(names, key)
|
||||||
|
from_line = classmethod(from_line)
|
||||||
|
|
||||||
|
def to_line(self):
|
||||||
|
"""
|
||||||
|
Returns a string in OpenSSH known_hosts file format, or None if
|
||||||
|
the object is not in a valid state. A trailing newline is
|
||||||
|
included.
|
||||||
|
"""
|
||||||
|
if self.valid:
|
||||||
|
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
|
||||||
|
self.key.get_base64())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
|
||||||
|
|
|
@ -2,3 +2,4 @@ Host keys / ``known_hosts`` files
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
.. automodule:: paramiko.hostkeys
|
.. automodule:: paramiko.hostkeys
|
||||||
|
:member-order: bysource
|
||||||
|
|
Loading…
Reference in New Issue