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

SSH Host key checking #280

Merged
merged 14 commits into from
Sep 19, 2018
Merged

Conversation

rdkls
Copy link
Contributor

@rdkls rdkls commented Sep 12, 2018

  • 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

- 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
@coveralls
Copy link

Coverage Status

Coverage increased (+6.1%) to 65.404% when pulling c04c05c on rdkls:feat/hostkey_validate_onconnect into 92d8932 on ncclient:master.

@coveralls
Copy link

coveralls commented Sep 12, 2018

Coverage Status

Coverage decreased (-0.7%) to 65.221% when pulling 841063b on rdkls:feat/hostkey_validate_onconnect into a06d73d on ncclient:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+6.1%) to 65.404% when pulling c04c05c on rdkls:feat/hostkey_validate_onconnect into 92d8932 on ncclient:master.

2 similar comments
@coveralls
Copy link

Coverage Status

Coverage increased (+6.1%) to 65.404% when pulling c04c05c on rdkls:feat/hostkey_validate_onconnect into 92d8932 on ncclient:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+6.1%) to 65.404% when pulling c04c05c on rdkls:feat/hostkey_validate_onconnect into 92d8932 on ncclient:master.

@rdkls rdkls closed this Sep 12, 2018
@rdkls rdkls reopened this Sep 12, 2018
@rdkls
Copy link
Contributor Author

rdkls commented Sep 12, 2018

Hi @einarnn here is completely redone version of host-key checking.
Much better version which checks the specified host_key MUST match the remote host's.
Also fixes for the existing ~/.ssh/known_hosts checking
and some tests with actual ssh host keys as requested
Kindly review? Thanks - Nick

Copy link

@glennmatthews glennmatthews left a 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.

@@ -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

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.

Copy link
Contributor Author

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 ...


"""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

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).

Copy link
Contributor Author

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

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()]

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?

Copy link
Contributor Author

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.


fingerprint = _colonify(hexlify(server_key.get_fingerprint()))
# Raise original exception; don't swallow details
raise

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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?

  2. 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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.

  2. 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)

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?

Copy link
Contributor Author

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 ?

@rdkls
Copy link
Contributor Author

rdkls commented Sep 15, 2018

Thanks for quick reviews @glennmatthews @einarnn
Please let me know if you'd like further amendments.

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...)
Nick

Copy link

@glennmatthews glennmatthews left a 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
@rdkls
Copy link
Contributor Author

rdkls commented Sep 18, 2018

Thanks @glennmatthews - also implemented your suggestion and made key types for negotiation more explicit

@einarnn einarnn merged commit 26b79d1 into ncclient:master Sep 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants