def create_command_channel(self, cmd): """ Create an SSH channel over the existing connection and issue a command. `cmd` should already be encoded as a byte string. Return the `CommandProtocol` instance. """ log = self.log if not self.connected_to_target: raise Exception( "Tried to open channel, but was not connected to the target host." ) ssh_conn = self.ssh_conn endpoint = SSHCommandClientEndpoint.existingConnection(ssh_conn, cmd) proto = CommandProtocol() proto.log = self.log d = connectProtocol(endpoint, proto) def _on_timeout(result, timeout): self.disconnect_from_target() raise Exception( "Timed out while attempting to establish command channel.") d.addTimeout(self.cmd_timeout, self.reactor, onTimeoutCancel=_on_timeout) log.debug("Establishing channel ...") proto = yield d log.debug("Channel established.") returnValue(proto)
def test_publicKeyAuthenticationFailure(self): """ If the SSH server rejects the key pair presented during authentication, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{AuthenticationFailed}. """ badKey = Key.fromString(privateRSA_openssh) self.setupKeyChecker(self.portal, {self.user: privateDSA_openssh}) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, keys=[badKey], knownHosts=self.knownHosts, 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(AuthenticationFailed) # XXX Should assert something specific about the arguments of the # exception # Nothing useful can be done with the connection at this point, so the # endpoint should close it. self.assertTrue(client.transport.disconnecting)
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)
def test_publicKeyAuthentication(self): """ If L{SSHCommandClientEndpoint} is initialized with any private keys, it will try to use them to authenticate with the SSH server. """ key = Key.fromString(privateDSA_openssh) self.setupKeyChecker(self.portal, {self.user: privateDSA_openssh}) self.realm.channelLookup[b'session'] = WorkingExecSession endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, keys=[key], knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) protocol = self.successResultOf(connected) self.assertNotIdentical(None, protocol.transport)
def connect_to_target(self): """ Establish an SSH connection to the target host. """ if self.connected_to_target: returnValue(None) known_hosts = self.get_known_hosts() keys = self.get_keys() command = self.anchor_cmd agent = self.get_agent() endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, command, self.ssh_user, self.host, keys=keys, knownHosts=known_hosts, agentEndpoint=agent) proto = AnchorProtocol() proto.log = self.log proto.reactor = self.reactor proto.connected_future.addCallback(self.set_connected).addTimeout( self.cmd_timeout, self.reactor) proto.finished.addCallback(self.set_disconnected) proto = yield connectProtocol(endpoint, proto) yield proto.connected_future self.anchor = proto self.ssh_conn = proto.transport.conn returnValue(None)
def main(reactor): ep = SSHCommandClientEndpoint.newConnection( reactor, b'/bin/cat', details['USER'], details['HOST'], port=details['PORT'], password=details['PASSWORD'], agentEndpoint=None, knownHosts=None) factory = Factory() factory.protocol = MyProtocol d = ep.connect(factory) def gotConnection(proto): # stdio interface stdio_proto = StdinProto(proto.transport.conn) stdio.StandardIO(stdio_proto) # factory = Factory() # factory.protocol = MyProtocol # e = SSHCommandClientEndpoint.existingConnection(conn, b"/bin/echo hey") # return e.connect(factory).addCallback(lambda proto: proto.finished) return stdio_proto.finished return d.addCallback(gotConnection)
def run_remote_command(reactor, command, args, host, debug_flag=False): shell_user = getpass.getuser() if args.user is not None: shell_user = args.user agent = get_agent(reactor) if agent is None: raise Exception("Could not connect to SSH agent.") known_hosts = get_known_hosts() endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, shell_user, host, knownHosts=known_hosts, agentEndpoint=agent) proto = CommandProtocol() proto.debug = debug_flag message = textwrap.dedent("""\ Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this. But, in a larger sense, we can not dedicate-- we can not consecrate-- we can not hallow-- this ground. The brave men, living and dead, who struggled here, have consecrated it, far above our poor power to add or detract. The world will little note, nor long remember what we say here, but it can never forget what they did here. It is for us the living, rather, to be dedicated here to the unfinished work which they who fought here have thus far so nobly advanced. It is rather for us to be here dedicated to the great task remaining before us-- that from these honored dead we take increased devotion to that cause for which they gave the last full measure of devotion-- that we here highly resolve that these dead shall not have died in vain-- that this nation, under God, shall have a new birth of freedom-- and that government of the people, by the people, for the people, shall not perish from the earth. """) lines = message.split("\n") for line in lines: proto.write_line_to_stdin(line) proto.write_line_to_stdin(None) proto.reactor = reactor #proto.register_output_callback(sys.stdout.write) proto.stdin_open = False proto = yield connectProtocol(endpoint, proto) yield proto.finished defer.returnValue(None)
def test_connectionCancelledBeforeSecure(self): """ If the connection is cancelled before the SSH transport layer has finished key exchange (ie, gotten to the point where we may attempt to authenticate), the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{CancelledError} and the connection is aborted. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) transport = AbortableFakeTransport(None, isServer=False) factory = self.reactor.tcpClients[0][2] client = factory.buildProtocol(None) client.makeConnection(transport) d.cancel() self.failureResultOf(d).trap(CancelledError) self.assertTrue(transport.aborted) # Make sure the connection closing doesn't result in unexpected # behavior when due to cancellation: client.connectionLost(Failure(ConnectionDone()))
def test_passwordAuthenticationFailure(self): """ If the SSH server rejects the password presented during authentication, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{AuthenticationFailed}. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, password=b"dummy password", knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) # For security, the server delays password authentication failure # response. Advance the simulation clock so the client sees the # failure. self.reactor.advance(server.service.passwordDelay) # Let the failure response traverse the "network" pump.flush() f = self.failureResultOf(connected) f.trap(AuthenticationFailed) # XXX Should assert something specific about the arguments of the # exception self.assertClientTransportState(client, False)
def spawnProcess(self, protocol, command): factory = Factory() factory.protocol = lambda: _CommandProtocol(protocol) e = SSHCommandClientEndpoint.existingConnection( self.master_proto.transport.conn, command) d = e.connect(factory) return d.addCallback(self._commandStarted)
def execute(self): """Execute the CLI command""" cmd = " ".join(self.cmd) LOGGER.debug('Executing {0}'.format(cmd)) endpoint = SSHCommandClientEndpoint.newConnection(reactor, b"{0}".format(cmd), self.config.get('username'), self.config.get('host'), port=self.config.get('port', 22), password=self.config.get('passsword'), agentEndpoint=self.agent_endpoint, keys=self.keys, knownHosts=self.known_hosts) factory = protocol.Factory() factory.protocol = AsteriskRemoteProtocol def _nominal(param): self.exitcode = 0 self.output = param.output self.err = self.output LOGGER.debug('Remote Asterisk process completed successfully') return self def _error(param): self.exitcode = -1 self.output = param.value.output self.err = self.output LOGGER.warning('Remote Asterisk process failed: {0}'.format(self.err)) return Failure(self) deferred = endpoint.connect(factory) deferred.addCallback(lambda proto: proto.finished) deferred.addCallbacks(_nominal, _error) return deferred
def _get_endpoint(self): """ Creates a generic endpoint connection that doesn't finish """ return SSHCommandClientEndpoint.newConnection( reactor, b'/bin/cat', self.username, self.hostname, port=self.port, keys=self.keys, password=self.password, knownHosts = self.knownHosts)
def execute(self): """Execute the CLI command""" cmd = " ".join(self.cmd) LOGGER.debug('Executing {0}'.format(cmd)) endpoint = SSHCommandClientEndpoint.newConnection(reactor, b"{0}".format(cmd), self.config.get('username'), self.config.get('host'), port=self.config.get('port', 22), password=self.config.get('passsword'), agentEndpoint=self.agent_endpoint, keys=self.keys, knownHosts=self.known_hosts) factory = protocol.Factory() factory.protocol = AsteriskRemoteProtocol def _nominal(param): self.exitcode = 0 self.output = param.output self.err = self.output LOGGER.debug('Remote Asterisk process completed successfully') return self def _error(param): self.exitcode = -1 self.output = param.value.output self.err = self.output LOGGER.warning('Remote Asterisk process failed: {0}'.format(self.err)) return Failure(self) deferred = endpoint.connect(factory) deferred.addCallback(lambda proto: proto.finished) deferred.addCallbacks(_nominal, _error) return deferred
def create(self): """ Create and return a new L{SSHCommandClientEndpoint} using the C{existingConnection} constructor. """ factory = Factory() factory.protocol = Protocol connected = self.endpoint.connect(factory) # Please, let me in. This kinda sucks. channelLookup = self.realm.channelLookup.copy() try: self.realm.channelLookup[b'session'] = WorkingExecSession server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) finally: self.realm.channelLookup.clear() self.realm.channelLookup.update(channelLookup) self._server = server self._client = client self._pump = pump protocol = self.successResultOf(connected) connection = protocol.transport.conn return SSHCommandClientEndpoint.existingConnection( connection, b"/bin/ls -l")
def test_connectionCancelledBeforeSecure(self): """ If the connection is cancelled before the SSH transport layer has finished key exchange (ie, gotten to the point where we may attempt to authenticate), the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{CancelledError} and the connection is aborted. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) transport = AbortableFakeTransport(None, isServer=False) factory = self.reactor.tcpClients[0][2] client = factory.buildProtocol(None) client.makeConnection(transport) d.cancel() self.failureResultOf(d).trap(CancelledError) self.assertTrue(transport.aborted) # Make sure the connection closing doesn't result in unexpected # behavior when due to cancellation: client.connectionLost(Failure(ConnectionDone()))
def test_publicKeyAuthenticationFailure(self): """ If the SSH server rejects the key pair presented during authentication, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{AuthenticationFailed}. """ badKey = Key.fromString(privateRSA_openssh) self.setupKeyChecker(self.portal, {self.user: privateDSA_openssh}) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, keys=[badKey], knownHosts=self.knownHosts, 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(AuthenticationFailed) # XXX Should assert something specific about the arguments of the # exception # Nothing useful can be done with the connection at this point, so the # endpoint should close it. self.assertTrue(client.transport.disconnecting)
def test_connectionClosedBeforeSecure(self): """ If the connection closes at any point before the SSH transport layer has finished key exchange (ie, gotten to the point where we may attempt to authenticate), the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping the reason for the lost connection. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) transport = StringTransport() factory = self.reactor.tcpClients[0][2] client = factory.buildProtocol(None) client.makeConnection(transport) client.connectionLost(Failure(ConnectionDone())) self.failureResultOf(d).trap(ConnectionDone)
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 connect(sdata, command, username, host, port=22, key_file=None, password=None): """ Connect to an SSH host (as it happens, persistently). """ sdata.set_conn_state('connecting') try: keys = [Key.fromFile(key_file)] if key_file else None except exceptions.IOError as e: print('### key load error:', str(e)) push_failure_message(str(e), sdata) return endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port=int(port), keys=keys, password=password, ui=None, knownHosts=PermissiveKnownHosts()) factory = Factory() factory.protocol = LineProtocol factory.sdata = sdata d = endpoint.connect(factory) # Very small race condition between here and the replacement # in connectionMade() above, but I've never managed to hit it. def disconnect(): sdata.log('Disconnecting while still attempting to connect, by request') d.cancel() sdata.transport_drop_cb = disconnect d.addErrback(lambda reason: push_failure_message(reason, sdata)) return d
def create(self): """ Create and return a new L{SSHCommandClientEndpoint} using the C{existingConnection} constructor. """ factory = Factory() factory.protocol = Protocol connected = self.endpoint.connect(factory) # Please, let me in. This kinda sucks. channelLookup = self.realm.channelLookup.copy() try: self.realm.channelLookup[b'session'] = WorkingExecSession server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) finally: self.realm.channelLookup.clear() self.realm.channelLookup.update(channelLookup) self._server = server self._client = client self._pump = pump protocol = self.successResultOf(connected) connection = protocol.transport.conn return SSHCommandClientEndpoint.existingConnection( connection, b"/bin/ls -l")
def executeNewCommand(self, line): factory = Factory() factory.protocol = MyProtocol e = SSHCommandClientEndpoint.existingConnection(self.ssh_conn, line.strip()) d = e.connect(factory) d.addCallback(self.protoStarted)
def test_interface(self): """ L{SSHCommandClientEndpoint} instances provide L{IStreamClientEndpoint}. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"dummy command", b"dummy user", self.hostname, self.port) self.assertTrue(verifyObject(IStreamClientEndpoint, endpoint))
def test_interface(self): """ L{SSHCommandClientEndpoint} instances provide L{IStreamClientEndpoint}. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"dummy command", b"dummy user", self.hostname, self.port) self.assertTrue(verifyObject(IStreamClientEndpoint, endpoint))
def create(self): """ Create and return a new L{SSHCommandClientEndpoint} using the C{newConnection} constructor. """ return SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, password=self.password, knownHosts=self.knownHosts, ui=FixedResponseUI(False))
def endpointForCommand(self, command): return SSHCommandClientEndpoint.newConnection( self.reactor, command, "osboxes", "localhost", port=22, password="******", knownHosts=PermissiveKnownHosts())
def exec_command(self, command): conn = self.transport.conn factory = protocol.Factory() factory.protocol = SingleCommandProtocol e = SSHCommandClientEndpoint.existingConnection(conn, command) d = e.connect(factory) d.addCallback(lambda p: p.finished) return d
def _endpoint_for_command(self, command): return SSHCommandClientEndpoint.newConnection( self.reactor, command, self.username, self.host, port=self.port, password=self.password, keys=self.keys, agentEndpoint=self.agent, knownHosts=self.knownHosts, ui=self.ui )
def _spawnProcess(self, protocol, command): vvvv('ACTUALLY SPAWN: %r' % (command,)) factory = Factory() factory.protocol = lambda: _CommandProtocol(protocol) e = SSHCommandClientEndpoint.existingConnection( self.master_proto.transport.conn, command) d = e.connect(factory) vvvv('STARTING') return d.addCallbacks(self._commandStarted, self._commandFailedToStart)
def _endpoint_for_command(self, command): return SSHCommandClientEndpoint.newConnection( self.reactor, command, self.username, self.host, port=self.port, password=self.password, keys=self.keys, agentEndpoint=self.agent, knownHosts=self.knownHosts, ui=self.ui )
def perform_run(dispatcher, intent): context.bind( message_type="flocker.provision.ssh:run", command=intent.log_command_filter(intent.command), ).write() endpoint = SSHCommandClientEndpoint.existingConnection( connection, intent.command) d = Deferred() connectProtocol(endpoint, CommandProtocol(deferred=d, context=context)) return d
def connectionMade(self): script_dir = os.getcwd() rel_path = "hostkeys" abs_file_path = os.path.join(script_dir, rel_path) knownHosts = KnownHostsFile.fromPath(abs_file_path) self.point = SSHCommandClientEndpoint.newConnection(reactor, 'cmd', 'user', '127.0.0.1', port=5122, password='******', knownHosts=PermissiveKnownHosts()) self.sshSide = FzSSHClient() self.sshSide.tcpSide = self connectProtocol(self.point, self.sshSide)
def perform_run(dispatcher, intent): context.bind( message_type="flocker.provision.ssh:run", command=intent.log_command_filter(intent.command), ).write() endpoint = SSHCommandClientEndpoint.existingConnection( connection, intent.command) d = Deferred() connectProtocol(endpoint, CommandProtocol( deferred=d, context=context)) return d
def endpointForStream(self): return SSHCommandClientEndpoint.newConnection( self.reactor, b"gerrit stream-events", self.username, self.host, port=self.port, keys=self.keys, password=self.password, agentEndpoint=self.agent, knownHosts=self.knownHosts)
def gotConnection(proto): conn = proto.transport.conn for i in range(50): factory = Factory() factory.protocol = PrinterProtocol factory.done = Deferred() done.append(factory.done) e = SSHCommandClientEndpoint.existingConnection( conn, b"/bin/echo %d" % (i,)) yield e.connect(factory)
def _get_endpoint(self): """ Creates a generic endpoint connection that doesn't finish """ return SSHCommandClientEndpoint.newConnection( reactor, b'/bin/cat', self.username, self.hostname, port=self.port, keys=self.keys, password=self.password, knownHosts=self.knownHosts)
def gotConnection(proto): conn = proto.transport.conn for i in range(50): factory = Factory() factory.protocol = PrinterProtocol factory.done = Deferred() done.append(factory.done) e = SSHCommandClientEndpoint.existingConnection( conn, b"/bin/echo %d" % (i, )) yield e.connect(factory)
def _connect(self, params): ep = SSHCommandClientEndpoint.newConnection( reactor, b'/bin/cat', params['username'], params['hostname'], port=params['port'], password=params['password'], agentEndpoint=None, knownHosts=EveryoneIsAKnownHostsFile()) factory = Factory() factory.protocol = _PersistentProtocol return ep.connect(factory).addCallback(self._connected)
def create(self): """ Create and return a new L{SSHCommandClientEndpoint} using the C{newConnection} constructor. """ return SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, password=self.password, knownHosts=self.knownHosts, ui=FixedResponseUI(False))
def _connect(self, host, port, user, password, private_key_file): keys = [Key.fromFile(private_key_file)] ep = SSHCommandClientEndpoint.newConnection( reactor, b'/bin/cat', user, host, port=port, password=password, keys=keys, agentEndpoint=None, knownHosts=EveryoneIsAKnownHostsFile()) factory = Factory() factory.protocol = _PersistentProtocol return ep.connect(factory).addCallback(self._connected)
def test_connectionCancelledBeforeConnected(self): """ If the connection is cancelled before it finishes connecting, the connection attempt is stopped. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) d.cancel() self.failureResultOf(d).trap(ConnectingCancelledError) self.assertTrue(self.reactor.connectors[0].stoppedConnecting)
def connectionMade(self): script_dir = os.getcwd() rel_path = "hostkeys" abs_file_path = os.path.join(script_dir, rel_path) knownHosts = KnownHostsFile.fromPath(abs_file_path) self.point = SSHCommandClientEndpoint.newConnection( reactor, 'cmd', 'user', '127.0.0.1', port=5122, password='******', knownHosts=PermissiveKnownHosts()) self.sshSide = FzSSHClient() self.sshSide.tcpSide = self connectProtocol(self.point, self.sshSide)
def connect(sdata, command, username, host, port=22, key_file=None, password=None): """ Connect to an SSH host (as it happens, persistently). """ sdata.set_conn_state('connecting') try: keys = [Key.fromFile(key_file)] if key_file else None except exceptions.IOError as e: print('### key load error:', str(e)) push_failure_message(str(e), sdata) return endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, username, host, port=int(port), keys=keys, password=password, ui=None, knownHosts=PermissiveKnownHosts()) factory = Factory() factory.protocol = LineProtocol factory.sdata = sdata d = endpoint.connect(factory) # Very small race condition between here and the replacement # in connectionMade() above, but I've never managed to hit it. def disconnect(): sdata.log( 'Disconnecting while still attempting to connect, by request') d.cancel() sdata.transport_drop_cb = disconnect d.addErrback(lambda reason: push_failure_message(reason, sdata)) return d
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_authenticationFallback(self): """ If the SSH server does not accept any of the specified SSH keys, the specified password is tried. """ badKey = Key.fromString(privateRSA_openssh) self.setupKeyChecker(self.portal, {self.user: privateDSA_openssh}) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, keys=[badKey], password=self.password, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) # Exercising fallback requires a failed authentication attempt. Allow # one. self.factory.attemptsBeforeDisconnect += 1 server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) pump.pump() # The server logs the channel open failure - this is expected. errors = self.flushLoggedErrors(ConchError) self.assertIn('unknown channel', (errors[0].value.data, errors[0].value.value)) self.assertEqual(1, len(errors)) # Now deal with the results on the endpoint side. f = self.failureResultOf(connected) f.trap(ConchError) self.assertEqual('unknown channel', f.value.value) # Nothing useful can be done with the connection at this point, so the # endpoint should close it. self.assertTrue(client.transport.disconnecting)
def test_destination(self): """ L{SSHCommandClientEndpoint} uses the L{IReactorTCP} passed to it to attempt a connection to the host/port address also passed to it. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, password=self.password, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol endpoint.connect(factory) host, port, factory, timeout, bindAddress = self.reactor.tcpClients[0] self.assertEqual(self.hostname, host) self.assertEqual(self.port, port) self.assertEqual(1, len(self.reactor.tcpClients))
def test_connectionFailed(self): """ If a connection cannot be established, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} representing the reason for the connection setup failure. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) factory = self.reactor.tcpClients[0][2] factory.clientConnectionFailed(None, Failure(ConnectionRefusedError())) self.failureResultOf(d).trap(ConnectionRefusedError)
def fork(self, command, args=(), env={}, path=None, _timeout=3600): """Execute a remote command on the SSH server """ if not self.connection: return defer.maybeDeferred(lambda: (None, "SSH not ready", 255)) if path: env['PATH'] = path if env: env = ' '.join('%s=%s' % (key, val) for key, val in env.items()) + ' ' else: env = '' if args: args = ' ' + ' '.join(args) else: args = '' existing = SSHCommandClientEndpoint.existingConnection( self.connection, (env + command + args).encode()) factory = protocol.Factory() factory.protocol = SSHCommandProtocol factory.done = defer.Deferred() def finished(result): """Command finished """ stdout, stderr, code = result return (stdout.read(), stderr.read(), code) factory.done.addCallback(finished) def connected(connection): """Connection established """ # Be nice if Conch exposed this better... connection.transport.extReceived = connection.extReceived return factory.done return existing.connect(factory).addCallback(connected)
def test_connectionCancelledBeforeConnected(self): """ If the connection is cancelled before it finishes connecting, the connection attempt is stopped. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) d.cancel() self.failureResultOf(d).trap(ConnectingCancelledError) self.assertTrue(self.reactor.connectors[0].stoppedConnecting)
def test_agentAuthentication(self): """ If L{SSHCommandClientEndpoint} is initialized with an L{SSHAgentClient}, the agent is used to authenticate with the SSH server. """ key = Key.fromString(privateRSA_openssh) agentServer = SSHAgentServer() agentServer.factory = Factory() agentServer.factory.keys = {key.blob(): (key, "")} self.setupKeyChecker(self.portal, {self.user: privateRSA_openssh}) agentEndpoint = SingleUseMemoryEndpoint(agentServer) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False), agentEndpoint=agentEndpoint) self.realm.channelLookup[b'session'] = WorkingExecSession factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) # Let the agent client talk with the agent server and the ssh client # talk with the ssh server. for i in range(14): agentEndpoint.pump.pump() pump.pump() protocol = self.successResultOf(connected) self.assertNotIdentical(None, protocol.transport)
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_authenticationFallback(self): """ If the SSH server does not accept any of the specified SSH keys, the specified password is tried. """ badKey = Key.fromString(privateRSA_openssh) self.setupKeyChecker(self.portal, {self.user: privateDSA_openssh}) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, keys=[badKey], password=self.password, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) # Exercising fallback requires a failed authentication attempt. Allow # one. self.factory.attemptsBeforeDisconnect += 1 server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) pump.pump() # The server logs the channel open failure - this is expected. errors = self.flushLoggedErrors(ConchError) self.assertIn( 'unknown channel', (errors[0].value.data, errors[0].value.value)) self.assertEqual(1, len(errors)) # Now deal with the results on the endpoint side. f = self.failureResultOf(connected) f.trap(ConchError) self.assertEqual('unknown channel', f.value.value) # Nothing useful can be done with the connection at this point, so the # endpoint should close it. self.assertTrue(client.transport.disconnecting)
def test_destination(self): """ L{SSHCommandClientEndpoint} uses the L{IReactorTCP} passed to it to attempt a connection to the host/port address also passed to it. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, password=self.password, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol endpoint.connect(factory) host, port, factory, timeout, bindAddress = self.reactor.tcpClients[0] self.assertEqual(self.hostname, host) self.assertEqual(self.port, port) self.assertEqual(1, len(self.reactor.tcpClients))
def test_publicKeyAuthentication(self): """ If L{SSHCommandClientEndpoint} is initialized with any private keys, it will try to use them to authenticate with the SSH server. """ key = Key.fromString(privateDSA_openssh) self.setupKeyChecker(self.portal, {self.user: privateDSA_openssh}) self.realm.channelLookup[b'session'] = WorkingExecSession endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, keys=[key], knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) protocol = self.successResultOf(connected) self.assertNotIdentical(None, protocol.transport)
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_connectionFailed(self): """ If a connection cannot be established, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} representing the reason for the connection setup failure. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) factory = self.reactor.tcpClients[0][2] factory.clientConnectionFailed(None, Failure(ConnectionRefusedError())) self.failureResultOf(d).trap(ConnectionRefusedError)
def test_passwordAuthenticationFailure(self): """ If the SSH server rejects the password presented during authentication, the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping L{AuthenticationFailed}. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, password=b"dummy password", knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) # For security, the server delays password authentication failure # response. Advance the simulation clock so the client sees the # failure. self.reactor.advance(server.service.passwordDelay) # Let the failure response traverse the "network" pump.flush() f = self.failureResultOf(connected) f.trap(AuthenticationFailed) # XXX Should assert something specific about the arguments of the # exception self.assertClientTransportState(client, 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)
def test_connectionClosedBeforeSecure(self): """ If the connection closes at any point before the SSH transport layer has finished key exchange (ie, gotten to the point where we may attempt to authenticate), the L{Deferred} returned by L{SSHCommandClientEndpoint.connect} fires with a L{Failure} wrapping the reason for the lost connection. """ endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", b"dummy user", self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False)) factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) transport = StringTransport() factory = self.reactor.tcpClients[0][2] client = factory.buildProtocol(None) client.makeConnection(transport) client.connectionLost(Failure(ConnectionDone())) self.failureResultOf(d).trap(ConnectionDone)
def test_agentAuthentication(self): """ If L{SSHCommandClientEndpoint} is initialized with an L{SSHAgentClient}, the agent is used to authenticate with the SSH server. """ key = Key.fromString(privateRSA_openssh) agentServer = SSHAgentServer() agentServer.factory = Factory() agentServer.factory.keys = {key.blob(): (key, "")} self.setupKeyChecker(self.portal, {self.user: privateRSA_openssh}) agentEndpoint = SingleUseMemoryEndpoint(agentServer) endpoint = SSHCommandClientEndpoint.newConnection( self.reactor, b"/bin/ls -l", self.user, self.hostname, self.port, knownHosts=self.knownHosts, ui=FixedResponseUI(False), agentEndpoint=agentEndpoint) self.realm.channelLookup[b'session'] = WorkingExecSession factory = Factory() factory.protocol = Protocol connected = endpoint.connect(factory) server, client, pump = self.connectedServerAndClient( self.factory, self.reactor.tcpClients[0][2]) # Let the agent client talk with the agent server and the ssh client # talk with the ssh server. for i in range(14): agentEndpoint.pump.pump() pump.pump() protocol = self.successResultOf(connected) self.assertNotIdentical(None, protocol.transport)
channel.wait_eof() channel.close() channel.wait_closed() # twisted.conch # Source: https://twistedmatrix.com/documents/current/conch/examples/index.html # https://stackoverflow.com/questions/22196373/sshcommandclientendpoint-twisted-how-to-execute-more-than-one-commands # https://twistedmatrix.com/documents/current/conch/howto/conch_client.html from twisted.conch.endpoints import SSHCommandClientEndpoint from twisted.internet.protocol import Factory from twisted.internet import reactor endpoint = SSHCommandClientEndpoint.newConnection( reactor, command, "username", "ssh.example.com", 22, password="******" ) factory = Factory() d = endpoint.connect(factory) d.addCallback(lambda protocol: protocol.finished) # trigger # Source: https://trigger.readthedocs.io/en/latest/examples.html#execute-commands-asynchronously-using-twisted from trigger.netdevices import NetDevices nd = NetDevices() dev = nd.find('ssh.example.com') dev.execute([command]) # parallel-ssh