Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to skip host key verification #2345

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions paramiko/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def connect(
disabled_algorithms=None,
transport_factory=None,
auth_strategy=None,
skip_host_key_verification=False,
):
"""
Connect to an SSH server and authenticate to it. The server's host key
Expand Down Expand Up @@ -318,6 +319,9 @@ def connect(
:param dict disabled_algorithms:
an optional dict passed directly to `.Transport` and its keyword
argument of the same name.
:param bool skip_host_key_verification:
whether or not to skip host key verification (not recommended,
default ``False``)
:param transport_factory:
an optional callable which is handed a subset of the constructor
arguments (primarily those related to the socket, GSS
Expand Down Expand Up @@ -417,6 +421,7 @@ def connect(
disabled_algorithms=disabled_algorithms,
)
t.use_compression(compress=compress)
t.set_skip_host_key_verification(skip_host_key_verification)
t.set_gss_host(
# t.hostname may be None, but GSS-API requires a target name.
# Therefore use hostname as fallback.
Expand Down
26 changes: 25 additions & 1 deletion paramiko/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ class Transport(threading.Thread, ClosingContextManager):

_modulus_pack = None
_active_check_timeout = 0.1
_skip_host_key_verification = False

def __init__(
self,
Expand Down Expand Up @@ -684,6 +685,27 @@ def set_gss_host(self, gss_host, trust_dns=True, gssapi_requested=True):
# And set attribute for reference later.
self.gss_host = gss_host

def set_skip_host_key_verification(self, skip_host_key_verification):
"""
Skipping host key verification might be useful in very rare cases
where it is not possible to verify the target's key, for example if
it uses a legacy signature algorithm that is no longer supported by
the client.

This option should be avoided if possible because host key
verification protects against issues like MITM attacks and replacement
of a server by a malicious actor that might perhaps use it to log
usernames and passwords with which to access the client system later.

:param bool skip_host_key_verification:
Whether of not to skip host key verification
(Defaults to False.)
:returns: ``None``.

.. versionadded:: 3.5.0
"""
self._skip_host_key_verification = skip_host_key_verification

def start_client(self, event=None, timeout=None):
"""
Negotiate a new SSH2 session as a client. This is the first step after
Expand Down Expand Up @@ -1973,7 +1995,9 @@ def _verify_key(self, host_key, sig):
key = self._key_info[self.host_key_type](Message(host_key))
if key is None:
raise SSHException("Unknown host key type")
if not key.verify_ssh_sig(self.H, Message(sig)):
if not self._skip_host_key_verification and not key.verify_ssh_sig(
self.H, Message(sig)
):
raise SSHException(
"Signature verification ({}) failed.".format(
self.host_key_type
Expand Down
53 changes: 53 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

from ._util import _support, requires_sha1_signing, slow

from cryptography.exceptions import UnsupportedAlgorithm


requires_gss_auth = unittest.skipUnless(
paramiko.GSS_AUTH_AVAILABLE, "GSS auth not available"
Expand Down Expand Up @@ -501,6 +503,57 @@ def test_client_can_be_used_as_context_manager(self):

self.assertTrue(self.tc._transport is None)

@requires_sha1_signing
def test_host_key_verification_with_bad_key(self):
"""
verify that host key verification raises an exception on failure
"""
# Note that _test_connection uses an RSA host key,
# so that's what we mock
with patch.object(paramiko.RSAKey, "verify_ssh_sig") as verifier:
verifier.return_value = False
with self.assertRaises(SSHException) as cm:
self._test_connection(key_filename=_support("ed25519.key"))
self.assertEqual(
str(cm.exception), "Signature verification (ssh-rsa) failed."
)

@requires_sha1_signing
def test_host_key_verification_with_unsupported_key(self):
"""
verify that host key verification raises an exception on
getting a host key with an unsupported algorithm
"""
with patch.object(paramiko.RSAKey, "verify_ssh_sig") as verifier:
verifier.side_effect = UnsupportedAlgorithm("oops")
with self.assertRaises(UnsupportedAlgorithm) as cm:
self._test_connection(key_filename=_support("ed25519.key"))
self.assertEqual(str(cm.exception), "oops")

@requires_sha1_signing
def test_skip_host_key_verification_with_bad_key(self):
"""
verify that a bad host key can be ignored
"""
with patch.object(paramiko.RSAKey, "verify_ssh_sig") as verifier:
verifier.return_value = False
self._test_connection(
key_filename=_support("ed25519.key"),
skip_host_key_verification=True,
)

@requires_sha1_signing
def test_skip_host_key_verification_with_unsupported_key(self):
"""
verify that an unsupported host key can be ignored
"""
with patch.object(paramiko.RSAKey, "verify_ssh_sig") as verifier:
verifier.side_effect = UnsupportedAlgorithm("oops")
self._test_connection(
key_filename=_support("ed25519.key"),
skip_host_key_verification=True,
)

def test_banner_timeout(self):
"""
verify that the SSHClient has a configurable banner timeout.
Expand Down