Example #1
0
    def connect(self, username, key=None, password=None, cert_file=None,
                port=22):
        """Initialize an SSH connection.

        Tries to connect and configure self. If only password is provided, it
        will be used for authentication. If key is provided, it is treated as
        and OpenSSH private RSA key and used for authentication. If both key
        and password are provided, password is used as a passphrase to unlock
        the private key.

        Raises MachineUnauthorizedError if it fails to connect.

        """

        if not key and not password:
            raise RequiredParameterMissingError("neither key nor password "
                                                "provided.")

        if key:
            private = key.private
            if isinstance(key, SignedSSHKey) and cert_file:
                # signed ssh key, use RSACert
                rsa_key = paramiko.RSACert(privkey_file_obj=StringIO(private),
                                           cert_file_obj=StringIO(cert_file))
            else:
                rsa_key = paramiko.RSAKey.from_private_key(StringIO(private))
        else:
            rsa_key = None

        attempts = 3
        while attempts:
            attempts -= 1
            try:
                self.ssh.connect(
                    self.host,
                    port=port,
                    username=username,
                    password=password,
                    pkey=rsa_key,
                    allow_agent=False,
                    look_for_keys=False,
                    timeout=10
                )
                break
            except paramiko.AuthenticationException as exc:
                log.error("ssh exception %r", exc)
                raise MachineUnauthorizedError("Couldn't connect to "
                                               "%s@%s:%s. %s"
                                               % (username, self.host,
                                                  port, exc))
            except socket.error as exc:
                log.error("Got ssh error: %r", exc)
                if not attempts:
                    raise ServiceUnavailableError("SSH timed-out repeatedly.")
            except Exception as exc:
                log.error("ssh exception %r", exc)
                # don't fail if SSHException or other paramiko exception,
                # eg related to network, but keep until all attempts are made
                if not attempts:
                    raise ServiceUnavailableError(repr(exc))
Example #2
0
 def connect_control(self):
     """
     Connect to the control websocket for LXD
     """
     try:
         self.cws.connect(self.curi)
     except websocket.WebSocketException:
         raise MachineUnauthorizedError()
Example #3
0
 def connect(self):
     try:
         if self.header is not None:
             self.ws.connect(self.uri, header=self.header)
         else:
             self.ws.connect(self.uri)
     except websocket.WebSocketException as exc:
         msg = "Make sure you are authorized to access this machine"
         raise MachineUnauthorizedError(msg)
Example #4
0
    def deploy(self, machine, username=None, port=22):
        """"""
        # try to actually deploy
        log.info("Deploying key to host %s", machine.hostname)
        filename = '~/.ssh/authorized_keys'
        grep_output = '`grep \'%s\' %s`' % (self.key.public, filename)
        new_line_check_cmd = (
            'if [ "$(tail -c1 %(file)s; echo x)" != "\\nx" ];'
            ' then echo "" >> %(file)s; fi' % {
                'file': filename
            })
        append_cmd = ('if [ -z "%s" ]; then echo "%s" >> %s; fi' %
                      (grep_output, self.key.public, filename))
        command = new_line_check_cmd + " ; " + append_cmd
        log.debug("command = %s", command)

        # FIXME
        from mist.api.methods import ssh_command

        deploy_error = False

        try:
            # Deploy key.
            ssh_command(self.key.owner,
                        machine.cloud.id,
                        machine.machine_id,
                        machine.hostname,
                        command,
                        username=username,
                        port=port)
            log.info("Key associated and deployed successfully.")
        except MachineUnauthorizedError:
            # Couldn't deploy key, maybe key was already deployed?
            deploy_error = True
        try:
            ssh_command(self.key.owner,
                        machine.cloud.id,
                        machine.machine_id,
                        machine.hostname,
                        'uptime',
                        key_id=self.key.id,
                        username=username,
                        port=port)
        except MachineUnauthorizedError:
            if deploy_error:
                super(SSHKeyController, self).disassociate(machine)
                raise MachineUnauthorizedError("Couldn't connect to "
                                               "deploy new SSH key.")
            raise
Example #5
0
    def connect(self):

        try:
            self.ws.connect(self.uri)
        except websocket.WebSocketException:
            raise MachineUnauthorizedError()
Example #6
0
    def autoconfigure(self,
                      owner,
                      cloud_id,
                      machine_id,
                      key_id=None,
                      username=None,
                      password=None,
                      port=22):
        """Autoconfigure SSH client.

        This will do its best effort to find a suitable key and username
        and will try to connect. If it fails it raises
        MachineUnauthorizedError, otherwise it initializes self and returns a
        (key_id, ssh_user) tuple. If connection succeeds, it updates the
        association information in the key with the current timestamp and the
        username used to connect.

        """
        log.info("autoconfiguring Shell for machine %s:%s", cloud_id,
                 machine_id)

        cloud = Cloud.objects.get(owner=owner, id=cloud_id, deleted=None)
        machine = Machine.objects.get(cloud=cloud, id=machine_id)
        key_associations = KeyMachineAssociation.objects(machine=machine)
        log.info('Got cloud & machine: %d key associations' %
                 len(key_associations))
        if key_id:
            keys = [Key.objects.get(owner=owner, id=key_id, deleted=None)]
            log.info('Got key')
        else:
            keys = [
                association.key for association in key_associations
                if isinstance(association.key, Key)
            ]
            log.info('Got keys %d' % len(keys))
        if username:
            users = [username]
        else:
            users = list(
                set([
                    association.ssh_user for association in key_associations
                    if association.ssh_user
                ]))
        log.info('Got users:{}'.format(users))
        if not users:
            for name in [
                    'root', 'ubuntu', 'ec2-user', 'user', 'azureuser', 'core',
                    'centos', 'cloud-user', 'fedora'
            ]:
                if name not in users:
                    users.append(name)
        if port != 22:
            ports = [port]
        else:
            ports = list(
                set([key_assoc.port for key_assoc in key_associations]))
        if 22 not in ports:
            ports.append(22)
        log.info('Got ports:{}'.format(ports))
        # store the original destination IP to prevent rewriting it when NATing
        ssh_host = self.host
        for key in keys:
            for ssh_user in users:
                for port in ports:
                    try:
                        # store the original ssh port in case of NAT
                        # by the OpenVPN server
                        ssh_port = port
                        self.host, port = dnat(owner, ssh_host, port)
                        log.info("ssh -i %s %s@%s:%s", key.name, ssh_user,
                                 self.host, port)
                        cert_file = ''
                        if isinstance(key, SignedSSHKey):
                            cert_file = key.certificate

                        self.connect(username=ssh_user,
                                     key=key,
                                     password=password,
                                     cert_file=cert_file,
                                     port=port)
                    except MachineUnauthorizedError:
                        continue

                    retval, resp = self.command('uptime')
                    new_ssh_user = None
                    if 'Please login as the user ' in resp:
                        new_ssh_user = resp.split()[5].strip('"')
                    elif 'Please login as the' in resp:
                        # for EC2 Amazon Linux machines, usually with ec2-user
                        new_ssh_user = resp.split()[4].strip('"')
                    if new_ssh_user:
                        log.info("retrying as %s", new_ssh_user)
                        try:
                            self.disconnect()
                            cert_file = ''
                            if isinstance(key, SignedSSHKey):
                                cert_file = key.certificate
                            self.connect(username=new_ssh_user,
                                         key=key,
                                         password=password,
                                         cert_file=cert_file,
                                         port=port)
                            ssh_user = new_ssh_user
                        except MachineUnauthorizedError:
                            continue
                    # we managed to connect successfully, return
                    # but first update key
                    trigger_session_update_flag = False
                    for key_assoc in KeyMachineAssociation.objects(
                            machine=machine):
                        if key_assoc.key == key:
                            if key_assoc.ssh_user != ssh_user:
                                key_assoc.ssh_user = ssh_user
                                trigger_session_update_flag = True
                            break
                    else:
                        trigger_session_update_flag = True
                        # in case of a private host do NOT update the key
                        # associations with the port allocated by the OpenVPN
                        # server, instead use the original ssh_port
                        key_assoc = KeyMachineAssociation(
                            key=key,
                            machine=machine,
                            ssh_user=ssh_user,
                            port=ssh_port,
                            sudo=self.check_sudo())
                        key_assoc.save()
                    machine.save()
                    if trigger_session_update_flag:
                        trigger_session_update(owner.id, ['keys'])
                    return key.name, ssh_user

        raise MachineUnauthorizedError("%s:%s" % (cloud_id, machine_id))