def test_userRejectedHostKey(self): """ If the L{KnownHostsFile} instance used to construct L{SSHCommandClientEndpoint} rejects the SSH public key presented by the server, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{UserRejectedKey}. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=KnownHostsFile(self.mktemp()), ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) f = self.failureResultOf(connected) f.trap(UserRejectedKey)
def test_defaultInitializerIgnoresExisting(self): """ The default initializer for L{KnownHostsFile} disregards any existing contents in the save path. """ hostsFile = KnownHostsFile(self.pathWithContent(sampleHashedLine)) self.assertEqual([], list(hostsFile.iterentries()))
def test_iterentriesUnsaved(self): """ If the save path for a L{KnownHostsFile} does not exist, L{KnownHostsFile.iterentries} still returns added but unsaved entries. """ hostsFile = KnownHostsFile(FilePath(self.mktemp())) hostsFile.addHostKey("www.example.com", Key.fromString(sampleKey)) self.assertEqual(1, len(list(hostsFile.iterentries())))
def test_readOnlySavePath(self): """ L{KnownHostsFile.savePath} is read-only; if an assignment is made to it, L{AttributeError} is raised and the value is unchanged. """ path = FilePath(self.mktemp()) new = FilePath(self.mktemp()) hostsFile = KnownHostsFile(path) self.assertRaises(AttributeError, setattr, hostsFile, "savePath", new) self.assertEqual(path, hostsFile.savePath)
def setUp(self): """ Configure an SSH server with password authentication enabled for a well-known (to the tests) account. """ SSHCommandClientEndpointTestsMixin.setUp(self) # Make the server's host key available to be verified by the client. self.hostKeyPath = FilePath(self.mktemp()) self.knownHosts = KnownHostsFile(self.hostKeyPath) self.knownHosts.addHostKey(self.hostname, self.factory.publicKeys['ssh-rsa']) self.knownHosts.addHostKey(self.serverAddress.host, self.factory.publicKeys['ssh-rsa']) self.knownHosts.save()
def test_savingAvoidsDuplication(self): """ L{KnownHostsFile.save} only writes new entries to the save path, not entries which were added and already written by a previous call to C{save}. """ path = FilePath(self.mktemp()) knownHosts = KnownHostsFile(path) entry = knownHosts.addHostKey("some.example.com", Key.fromString(sampleKey)) knownHosts.save() knownHosts.save() knownHosts = KnownHostsFile.fromPath(path) self.assertEqual([entry], list(knownHosts.iterentries()))
def test_saveResetsClobberState(self): """ After L{KnownHostsFile.save} is used once with an instance initialized by the default initializer, contents of the save path are respected and preserved. """ hostsFile = KnownHostsFile(self.pathWithContent(sampleHashedLine)) preSave = hostsFile.addHostKey("www.example.com", Key.fromString(otherSampleKey)) hostsFile.save() postSave = hostsFile.addHostKey("another.example.com", Key.fromString(thirdSampleKey)) hostsFile.save() self.assertEqual([preSave, postSave], list(hostsFile.iterentries()))
def test_defaultInitializerClobbersExisting(self): """ After using the default initializer for L{KnownHostsFile}, the first use of L{KnownHostsFile.save} overwrites any existing contents in the save path. """ path = self.pathWithContent(sampleHashedLine) hostsFile = KnownHostsFile(path) entry = hostsFile.addHostKey("www.example.com", Key.fromString(otherSampleKey)) hostsFile.save() # Check KnownHostsFile to see what it thinks the state is self.assertEqual([entry], list(hostsFile.iterentries())) # And also directly check the underlying file itself self.assertEqual(entry.toString() + "\n", path.getContent())
def test_unsavedEntryHasKeyMismatch(self): """ L{KnownHostsFile.hasHostKey} raises L{HostKeyChanged} if the host key is present in memory (but not yet saved), but different from the expected one. The resulting exception has a C{offendingEntry} indicating the given entry, but no filename or line number information (reflecting the fact that the entry exists only in memory). """ hostsFile = KnownHostsFile(FilePath(self.mktemp())) entry = hostsFile.addHostKey("www.example.com", Key.fromString(otherSampleKey)) exception = self.assertRaises(HostKeyChanged, hostsFile.hasHostKey, "www.example.com", Key.fromString(thirdSampleKey)) self.assertEqual(exception.offendingEntry, entry) self.assertEqual(exception.lineno, None) self.assertEqual(exception.path, None)
def setUp(self): """ Patch 'open' in verifyHostKey. """ self.fakeFile = FakeFile() self.patch(default, "_open", self.patchedOpen) self.hostsOption = self.mktemp() knownHostsFile = KnownHostsFile(FilePath(self.hostsOption)) knownHostsFile.addHostKey("exists.example.com", Key.fromString(sampleKey)) knownHostsFile.addHostKey("4.3.2.1", Key.fromString(sampleKey)) knownHostsFile.save() self.fakeTransport = FakeObject() self.fakeTransport.factory = FakeObject() self.options = self.fakeTransport.factory.options = { 'host': "exists.example.com", 'known-hosts': self.hostsOption }
def setUp(self): """ Patch 'open' in verifyHostKey. """ self.fakeFile = FakeFile() self.patch(default, "_open", self.patchedOpen) self.hostsOption = self.mktemp() self.hashedEntries = {} knownHostsFile = KnownHostsFile(FilePath(self.hostsOption)) for host in (b"exists.example.com", b"4.3.2.1"): entry = knownHostsFile.addHostKey(host, Key.fromString(sampleKey)) self.hashedEntries[host] = entry knownHostsFile.save() self.fakeTransport = FakeObject() self.fakeTransport.factory = FakeObject() self.options = self.fakeTransport.factory.options = { 'host': b"exists.example.com", 'known-hosts': self.hostsOption }
def test_readExisting(self): """ Existing entries in the I{known_hosts} file are reflected by the L{KnownHostsFile} created by L{_NewConnectionHelper} when none is supplied to it. """ key = CommandFactory().publicKeys['ssh-rsa'] path = FilePath(self.mktemp()) knownHosts = KnownHostsFile(path) knownHosts.addHostKey("127.0.0.1", key) knownHosts.save() msg("Created known_hosts file at %r" % (path.path, )) # Unexpand ${HOME} to make sure ~ syntax is respected. home = os.path.expanduser("~/") default = path.path.replace(home, "~/") self.patch(_NewConnectionHelper, "_KNOWN_HOSTS", default) msg("Patched _KNOWN_HOSTS with %r" % (default, )) loaded = _NewConnectionHelper._knownHosts() self.assertTrue(loaded.hasHostKey("127.0.0.1", key))
def setUp(self): """ Configure an SSH server with password authentication enabled for a well-known (to the tests) account. """ SSHCommandClientEndpointTestsMixin.setUp(self) knownHosts = KnownHostsFile(FilePath(self.mktemp())) knownHosts.addHostKey(self.hostname, self.factory.publicKeys['ssh-rsa']) knownHosts.addHostKey(self.serverAddress.host, self.factory.publicKeys['ssh-rsa']) self.endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, password=self.password, knownHosts=knownHosts, ui=FixedResponseUI(False))
def test_mismatchedHostKey(self): """ If the SSH public key presented by the SSH server does not match the previously remembered key, as reported by the L{KnownHostsFile} instance use to construct the endpoint, for that server, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{HostKeyChanged}. """ differentKey = Key.fromString(privateDSA_openssh).public() knownHosts = KnownHostsFile(self.mktemp()) knownHosts.addHostKey(self.serverAddress.host, differentKey) knownHosts.addHostKey(self.hostname, differentKey) # The UI may answer true to any questions asked of it; they should # make no difference, since a *mismatched* key is not even optionally # allowed to complete a connection. ui = FixedResponseUI(True) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, password=b"dummy password", knownHosts=knownHosts, ui=ui) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) f = self.failureResultOf(connected) f.trap(HostKeyChanged)