-
Notifications
You must be signed in to change notification settings - Fork 390
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
SSH Host key checking #280
SSH Host key checking #280
Conversation
- before initiating connection, set preferred_key (types) to be those that we already possess for the host (if any) - when checking known_hosts, cater for ports other than 22 - if hostkey specified, remote host /must/ match that hostkey Formatting: - reformat function definition for easier reading of arguments - remove use of temporary single-character variables for clarity sake
2 similar comments
…client into feat/hostkey_validate_onconnect
Hi @einarnn here is completely redone version of host-key checking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the general approach, but have a few concerns.
ncclient/transport/ssh.py
Outdated
@@ -312,6 +328,8 @@ def connect(self, host, port=830, timeout=None, unknown_host_cb=default_unknown_ | |||
|
|||
*hostkey_verify* enables hostkey verification from ~/.ssh/known_hosts | |||
|
|||
*hostkey* only connect when server presents a public hostkey matching this | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hostkey
is a base64 string, correct? Would be good to mention the datatype.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Funny you mention, I had the same in an older version but reverted here. Happy to revert again. Thanks for quick review, I'm going through addressing today ...
ncclient/transport/ssh.py
Outdated
|
||
"""Connect via SSH and initialize the NETCONF session. First attempts the publickey authentication method and then password authentication. | ||
|
||
To disable attempting publickey authentication altogether, call with *allow_agent* and *look_for_keys* as `False`. | ||
|
||
*host* is the hostname or IP address to connect to | ||
|
||
*port* is by default 830, but some devices use the default SSH port of 22 so this may need to be specified | ||
*port* is by default PORT_NETCONF_DEFAULT, but some devices use the default SSH port of 22 (PORT_SSH_DEFAULT) so this may need to be specified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I'm not a fan of this change - it takes something clear and makes it less so (now I have to look elsewhere to find out what PORT_NETCONF_DEFAULT is).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's generally good practice to avoid magic numbers. You're welcome to keep (can revert) but I recommend avoiding
ncclient/transport/ssh.py
Outdated
if not hostkey_obj: | ||
# We've tried all known host key types and haven't found which one to use - bail | ||
raise e | ||
self._transport._preferred_keys = [hostkey_obj.get_name()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have both self._host_keys
and the hostkey
parameter, we set self._transport._preferred_keys
once above and then re-set it again here, losing the previously set value. That feels incorrect to me. Should the first if
statement be something like if self._host_keys and not hostkey:
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first 'if' is for when host keys have been set by code above (i.e. ssh_config and we read known_hosts)
However regardless of this - if the hostkey has been specified - it takes precedence.
ncclient/transport/ssh.py
Outdated
|
||
fingerprint = _colonify(hexlify(server_key.get_fingerprint())) | ||
# Raise original exception; don't swallow details | ||
raise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
This is a potentially breaking change for existing users of ncclient who are currently doing "except SSHError" and now need to change to "except paramiko.SSHException". Are we sure this is a desired change?
-
If this is a desired change, there's no point to keeping a try...except block that does nothing but re-raising the exception - just remove the block entirely instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
That's actually a good point. What I'll do is revert to SSHError, but put in the detail from paramiko. The reason for this change was that while using the library, I had an error where this failed but the swallowed/rewritten error obfuscated the root cause.
-
Correct there's no point functionally in doing so, only in being more explicit in the type of error we're expecting. Personal thing again; more explicit vs verbosity
def test_connect_ssh_with_hostkey_ed25519(self, mock_ssh): | ||
hostkey = 'AAAAC3NzaC1lZDI1NTE5AAAAIIiHpGSf8fla6tCwLpwshvMGmUK+B/0v5CsRu+5v4uT7' | ||
manager.connect(host='host', hostkey=hostkey) | ||
mock_ssh.assert_called_once_with(host='host', hostkey=hostkey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing feels a little thin here.
- Do we have the ability to check that
_preferred_keys
is set correctly in each case? - Test for host key lookup when port is non-default?
- Negative test for SSHUnknownHostError when key doesn't match?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep more tests would be great. I feel like what's there, is in line with precedents though. Might have a look later but are you OK with this PR without @glennmatthews ?
Thanks for quick reviews @glennmatthews @einarnn Also may be of interest - if you guys are using Ansible too - I'll be submitting PR to the netconf module to support specifying ssh hostkey in inventory, passing through to ncclient and using this functionality. The larger point of the work - is orchestrating device management at scale - once device is provisioned, putting its dns/ip along with host key in ansible inventory - and being more certain that orchestration/config is talking to the same device (was quite surprised this wasn't widely done as with ma-ssl...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks sane to me; will let @einarnn make the final determination.
…al port number appended) - move into if hostkey_verify, so that setting this False won't check, even when hostkey_b64 supplied - when checking host keys, use the existing is_known_host bool and raise Exception code (neater, fewer code paths)
- hostkey_b64 object creation - raise new (instead of reusing) exception with friendlier message - import NetconfFramingError (used on line 210 in _parse11 - remove unused ncclient.xml_ import - pep8 spacing
Thanks @glennmatthews - also implemented your suggestion and made key types for negotiation more explicit |
Formatting: