09.11.2016 Views

Foundations of Python Network Programming 978-1-4302-3004-5

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

CHAPTER 16 ■ TELNET AND SSH<br />

That “yes” answer buried deep on the next-to-last full line is the answer that I typed giving SSH the<br />

go-ahead to make the connection and remember the key for next time. If SSH ever connects to a host<br />

and sees a different key, its reaction is quite severe:<br />

$ ssh asaph.rhodesmill.org<br />

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<br />

@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @<br />

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<br />

IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!<br />

Someone could be eavesdropping on you right now (man-in-the-middle attack)!<br />

This message will be familiar to anyone who has ever had to re-build a server from scratch, and<br />

forgets to save the old SSH keys and lets new ones be generated by the re-install. It can be painful to go<br />

around to all <strong>of</strong> your SSH clients and remove the <strong>of</strong>fending old key so that they will quietly learn the new<br />

one upon reconnection.<br />

The paramiko library has full support for all <strong>of</strong> the normal SSH tactics surrounding host keys. But its<br />

default behavior is rather spare: it loads no host-key files by default, and will then, <strong>of</strong> course, raise an<br />

exception for the very first host to which you connect because it will not be able to verify its key! The<br />

exception that it raises is a bit un-informative; it is only by looking at the fact that it comes from inside<br />

the missing_host_key() function that I usually recognize what has caused the error:<br />

>>> import paramiko<br />

>>> client = paramiko.SSHClient()<br />

>>> client.connect('my.example.com', username='test')<br />

Traceback (most recent call last):<br />

...<br />

File ".../paramiko/client.py", line 85, in missing_host_key<br />

» raise SSHException('Unknown server %s' % hostname)<br />

paramiko.SSHException: Unknown server my.example.com<br />

To behave like the normal SSH command, load both the system and the current user's known-host<br />

keys before making the connection:<br />

>>> client.load_system_host_keys()<br />

>>> client.load_host_keys('/home/brandon/.ssh/known_hosts')<br />

>>> client.connect('my.example.com', username='test')<br />

The paramiko library also lets you choose how you handle unknown hosts. Once you have a client<br />

object created, you can provide it with a decision-making class that is asked what to do if a host key is<br />

not recognized. You can build these classes yourself by inheriting from the MissingHostKeyPolicy class:<br />

>>> class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):<br />

... def missing_host_key(self, client, hostname, key):<br />

... return<br />

...<br />

>>> client.set_missing_host_key_policy(AllowAnythingPolicy())<br />

>>> client.connect('my.example.com', username='test')<br />

Note that, through the arguments to the missing_host_key() method, you receive several pieces <strong>of</strong><br />

information on which to base your decision; you could, for example, allow connections to machines on<br />

your own server subnet without a host key, but disallow all others.<br />

Inside paramiko there are also several decision-making classes that already implement several basic<br />

host-key options:<br />

• paramiko.AutoAddPolicy: Host keys are automatically added to your user host-key<br />

store (the file ~/.ssh/known_hosts on Unix systems) when first encountered, but<br />

any change in the host key from then on will raise a fatal exception.<br />

281

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!