Example #1
0
  def CreateRequests(self, args):

    name = args.name
    if not name:
      name = gaia_utils.GetDefaultAccountName(self.http)

    user_ref = self.clouduseraccounts_resources.Parse(
        name, collection='clouduseraccounts.users')

    if args.fingerprints:
      fingerprints = args.fingerprints
    else:
      fetcher = users_client.UserResourceFetcher(
          self.clouduseraccounts, self.project, self.http, self.batch_url)

      fingerprints = [k.fingerprint for k in
                      fetcher.LookupUser(user_ref.Name()).publicKeys]

    # Generate warning before deleting.
    prompt_list = ['[{0}]'.format(fingerprint) for fingerprint in fingerprints]
    prompt_title = ('The following public keys will be removed from the user ' +
                    user_ref.Name())
    utils.PromptForDeletionHelper(None, prompt_list, prompt_title=prompt_title)

    requests = []
    for fingerprint in fingerprints:
      request = self.messages.ClouduseraccountsUsersRemovePublicKeyRequest(
          project=self.project,
          fingerprint=fingerprint,
          user=user_ref.Name())
      requests.append(request)

    return requests
Example #2
0
    def Run(self, args):
        holder = base_classes.ComputeUserAccountsApiHolder(self.ReleaseTrack())
        client = holder.client
        name = args.name
        if not name:
            name = gaia.GetDefaultAccountName(client.http)

        user_ref = holder.resources.Parse(
            name,
            params={'project': properties.VALUES.core.project.GetOrFail},
            collection='clouduseraccounts.users')

        if args.fingerprints:
            fingerprints = args.fingerprints
        else:
            fetcher = users_client.UserResourceFetcher(
                client, user_ref.project, client.http,
                'https://www.googleapis.com/batch/')

            fingerprints = [
                k.fingerprint
                for k in fetcher.LookupUser(user_ref.Name()).publicKeys
            ]

        # Generate warning before deleting.
        prompt_list = [
            '[{0}]'.format(fingerprint) for fingerprint in fingerprints
        ]
        prompt_title = (
            'The following public keys will be removed from the user ' +
            user_ref.Name())
        utils.PromptForDeletionHelper(None,
                                      prompt_list,
                                      prompt_title=prompt_title)

        requests = []
        for fingerprint in fingerprints:
            request = (client.MESSAGES_MODULE.
                       ClouduseraccountsUsersRemovePublicKeyRequest(
                           project=user_ref.project,
                           fingerprint=fingerprint,
                           user=user_ref.Name()))
            requests.append((client.users, 'RemovePublicKey', request))

        errors = []
        responses = list(
            request_helper.MakeRequests(
                requests=requests,
                http=client.http,
                batch_url='https://www.googleapis.com/batch/',
                errors=errors))
        if errors:
            utils.RaiseToolException(errors,
                                     error_message='Could not fetch resource:')
        return responses
Example #3
0
    def Run(self, args):
        compute_holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
        holder = base_classes.ComputeUserAccountsApiHolder(self.ReleaseTrack())
        client = holder.client
        name = args.name
        if not name:
            name = gaia.GetDefaultAccountName(client.http)

        user_ref = holder.resources.Parse(
            name,
            params={'project': properties.VALUES.core.project.GetOrFail},
            collection='clouduseraccounts.users')

        if args.fingerprints:
            fingerprints = args.fingerprints
        else:
            fetcher = users_client.UserResourceFetcher(
                client, user_ref.project, client.http,
                compute_holder.client.batch_url)

            fingerprints = [
                k.fingerprint
                for k in fetcher.LookupUser(user_ref.Name()).publicKeys
            ]

        # Generate warning before deleting.
        prompt_list = [
            '[{0}]'.format(fingerprint) for fingerprint in fingerprints
        ]
        prompt_title = (
            'The following public keys will be removed from the user ' +
            user_ref.Name())
        utils.PromptForDeletionHelper(None,
                                      prompt_list,
                                      prompt_title=prompt_title)

        requests = []
        for fingerprint in fingerprints:
            request = (client.MESSAGES_MODULE.
                       ClouduseraccountsUsersRemovePublicKeyRequest(
                           project=user_ref.project,
                           fingerprint=fingerprint,
                           user=user_ref.Name()))
            requests.append((client.users, 'RemovePublicKey', request))

        return compute_holder.client.MakeRequests(requests)
Example #4
0
    def EnsureSSHKeyExists(self,
                           user,
                           instance,
                           project,
                           use_account_service=False):
        """Controller for EnsureSSHKey* variants.

    Sends the key to the project metadata, instance metadata or account service,
    and signals whether the key was newly added.

    Args:
      user: str, The user name.
      instance: Instance, the instance to connect to.
      project: Project, the project instance is in
      use_account_service: bool, when false upload ssh keys to project metadata.

    Returns:
      bool, True if the key was newly added.
    """
        if use_account_service:
            log.status.Print('using accounts service')
            fetcher = user_client.UserResourceFetcher(self.clouduseraccounts,
                                                      self.project, self.http,
                                                      self.batch_url)
            try:
                keys_newly_added = self._EnsureSSHKeyExistsForUser(
                    fetcher, user)
            # TODO(b/37739425): find out what desired fallback mechanism is and
            # implement it.
            except user_client.UserException as e:
                log.info(
                    'Error when attempting to prepare keys using clouduaseraccounts '
                    'API, falling back to metadata keys: %s', e)
                use_account_service = False
        if not use_account_service:
            # There are two kinds of metadata: project-wide metadata and per-instance
            # metadata. There are four SSH-key related metadata keys:
            #
            # * project['sshKeys']: shared project-wide
            # * instance['sshKeys']: legacy. Acts as an override to project['sshKeys']
            # * instance['block-project-ssh-keys']: If true, instance['ssh-keys']
            #     overrides project['sshKeys']. Otherwise, keys from both metadata
            #     pairs are valid.
            # * instance['ssh-keys']: Acts either in conjunction with or as an
            #     override to project['sshKeys'], depending on
            #     instance['block-project-ssh-keys']
            #
            # SSH-like commands work by copying a relevant SSH key to
            # the appropriate metadata value. The VM grabs keys from the metadata as
            # follows (pseudo-Python):
            #
            #   def GetAllSshKeys(project, instance):
            #       if 'sshKeys' in instance.metadata:
            #           return (instance.metadata['sshKeys'] +
            #                   instance.metadata['ssh-keys'])
            #       elif instance.metadata['block-project-ssh-keys'] == 'true':
            #           return instance.metadata['ssh-keys']
            #       else:
            #           return (instance.metadata['ssh-keys'] +
            #                   project.metadata['sshKeys'])
            #
            if _GetSSHKeysFromMetadata(instance.metadata):
                # If we add a key to project-wide metadata but the per-instance
                # 'sshKeys' metadata exists, we won't be able to ssh in because the VM
                # won't check the project-wide metadata. To avoid this, if the instance
                # has per-instance SSH key metadata, we add the key there instead.
                keys_newly_added = self.EnsureSSHKeyIsInInstance(
                    user, instance)
            elif _MetadataHasBlockProjectSshKeys(instance.metadata):
                # If the instance 'ssh-keys' metadata overrides the project-wide
                # 'sshKeys' metadata, we should put our key there.
                keys_newly_added = self.EnsureSSHKeyIsInInstance(user,
                                                                 instance,
                                                                 iam_keys=True)
            else:
                # Otherwise, try to add to the project-wide metadata. If we don't have
                # permissions to do that, add to the instance 'ssh-keys' metadata.
                try:
                    keys_newly_added = self.EnsureSSHKeyIsInProject(
                        user, project)
                except SetProjectMetadataError:
                    log.info('Could not set project metadata:', exc_info=True)
                    # If we can't write to the project metadata, it may be because of a
                    # permissions problem (we could inspect this exception object further
                    # to make sure, but because we only get a string back this would be
                    # fragile). If that's the case, we want to try the writing to the
                    # iam_keys metadata (we may have permissions to write to instance
                    # metadata). We prefer this to the per-instance override of the
                    # project metadata.
                    log.info('Attempting to set instance metadata.')
                    keys_newly_added = self.EnsureSSHKeyIsInInstance(
                        user, instance, iam_keys=True)
        return keys_newly_added
Example #5
0
    def ActuallyRun(self,
                    args,
                    cmd_args,
                    user,
                    instance,
                    project,
                    strict_error_checking=True,
                    use_account_service=False,
                    wait_for_sshable=True,
                    ignore_ssh_errors=False):
        """Runs the scp/ssh command specified in cmd_args.

    If the scp/ssh command exits non-zero, this command will exit with the same
    exit code.

    Args:
      args: argparse.Namespace, The calling command invocation args.
      cmd_args: [str], The argv for the command to execute.
      user: str, The user name.
      instance: Instance, the instance to connect to
      project: str, the project instance is in
      strict_error_checking: bool, whether to fail on a non-zero, non-255 exit
        code (alternative behavior is to return the exit code
      use_account_service: bool, when false upload ssh keys to project metadata.
      wait_for_sshable: bool, when false skip the sshability check.
      ignore_ssh_errors: bool, when true ignore all errors, including the 255
        exit code.

    Raises:
      CommandError: If the scp/ssh command fails.

    Returns:
      int, the exit code of the command that was run
    """
        cmd_args = ssh.LocalizeCommand(cmd_args, self.env)
        if args.dry_run:
            log.out.Print(' '.join(cmd_args))
            return

        if args.plain:
            keys_newly_added = []
        elif use_account_service:
            fetcher = user_client.UserResourceFetcher(self.clouduseraccounts,
                                                      self.project, self.http,
                                                      self.batch_url)
            keys_newly_added = self._EnsureSSHKeyExistsForUser(fetcher, user)
        else:
            # There are two kinds of metadata: project-wide metadata and per-instance
            # metadata. There are four SSH-key related metadata keys:
            #
            # * project['sshKeys']: shared project-wide
            # * instance['sshKeys']: legacy. Acts as an override to project['sshKeys']
            # * instance['block-project-ssh-keys']: If true, instance['ssh-keys']
            #     overrides project['sshKeys']. Otherwise, keys from both metadata
            #     pairs are valid.
            # * instance['ssh-keys']: Acts either in conjunction with or as an
            #     override to project['sshKeys'], depending on
            #     instance['block-project-ssh-keys']
            #
            # SSH-like commands work by copying a relevant SSH key to
            # the appropriate metadata value. The VM grabs keys from the metadata as
            # follows (pseudo-Python):
            #
            #   def GetAllSshKeys(project, instance):
            #       if 'sshKeys' in instance.metadata:
            #           return (instance.metadata['sshKeys'] +
            #                   instance.metadata['ssh-keys'])
            #       elif instance.metadata['block-project-ssh-keys'] == 'true':
            #           return instance.metadata['ssh-keys']
            #       else:
            #           return (instance.metadata['ssh-keys'] +
            #                   project.metadata['sshKeys'])
            #
            if _GetSSHKeysFromMetadata(instance.metadata):
                # If we add a key to project-wide metadata but the per-instance
                # 'sshKeys' metadata exists, we won't be able to ssh in because the VM
                # won't check the project-wide metadata. To avoid this, if the instance
                # has per-instance SSH key metadata, we add the key there instead.
                keys_newly_added = self.EnsureSSHKeyIsInInstance(
                    user, instance)
            elif _MetadataHasBlockProjectSshKeys(instance.metadata):
                # If the instance 'ssh-keys' metadata overrides the project-wide
                # 'sshKeys' metadata, we should put our key there.
                keys_newly_added = self.EnsureSSHKeyIsInInstance(user,
                                                                 instance,
                                                                 iam_keys=True)
            else:
                # Otherwise, try to add to the project-wide metadata. If we don't have
                # permissions to do that, add to the instance 'ssh-keys' metadata.
                try:
                    keys_newly_added = self.EnsureSSHKeyIsInProject(
                        user, project)
                except SetProjectMetadataError:
                    log.info('Could not set project metadata:', exc_info=True)
                    # If we can't write to the project metadata, it may be because of a
                    # permissions problem (we could inspect this exception object further
                    # to make sure, but because we only get a string back this would be
                    # fragile). If that's the case, we want to try the writing to the
                    # iam_keys metadata (we may have permissions to write to instance
                    # metadata). We prefer this to the per-instance override of the
                    # project metadata.
                    log.info('Attempting to set instance metadata.')
                    keys_newly_added = self.EnsureSSHKeyIsInInstance(
                        user, instance, iam_keys=True)

        if keys_newly_added and wait_for_sshable:
            external_ip_address = GetExternalIPAddress(instance)
            host_key_alias = self.HostKeyAlias(instance)
            ssh.WaitUntilSSHable(user, external_ip_address, self.env,
                                 self.keys.key_file, host_key_alias,
                                 args.plain, args.strict_host_key_checking,
                                 _SSH_KEY_PROPAGATION_TIMEOUT_SEC)

        logging.debug('%s command: %s', cmd_args[0], ' '.join(cmd_args))

        try:
            return ssh.RunExecutable(
                cmd_args,
                strict_error_checking=strict_error_checking,
                ignore_ssh_errors=ignore_ssh_errors)
        except ssh.CommandError as e:
            raise CommandError(e)
Example #6
0
  def EnsureSSHKeyExists(self, compute_client, cua_client, user, instance,
                         project, use_account_service=False):
    """Controller for EnsureSSHKey* variants.

    Sends the key to the project metadata, instance metadata or account service,
    and signals whether the key was newly added.

    Args:
      compute_client: The compute client.
      cua_client: The clouduseraccounts client.
      user: str, The user name.
      instance: Instance, the instance to connect to.
      project: Project, the project instance is in
      use_account_service: bool, when false upload ssh keys to project metadata.

    Returns:
      bool, True if the key was newly added.
    """
    if use_account_service:
      fetcher = user_client.UserResourceFetcher(
          cua_client,
          properties.VALUES.core.project.GetOrFail(),
          compute_client.apitools_client.http, compute_client.batch_url)
      try:
        keys_newly_added = self._EnsureSSHKeyExistsForUser(fetcher, user)
      # TODO(b/37739425): find out what desired fallback mechanism is and
      # implement it.
      except  user_client.UserException as e:
        log.info(
            'Error when attempting to prepare keys using clouduaseraccounts '
            'API, falling back to metadata keys: %s', e)
        use_account_service = False
    if not use_account_service:
      # There are two kinds of metadata: project-wide metadata and per-instance
      # metadata. There are five SSH-key related metadata keys:
      #
      # * project['ssh-keys']: shared project-wide list of keys.
      # * project['sshKeys']: legacy, shared project-wide list of keys.
      # * instance['block-project-ssh-keys']: bool, when true indicates that
      #     instance keys should replace project keys rather than being added
      #     to them.
      # * instance['ssh-keys']: instance specific list of keys.
      # * instance['sshKeys']: legacy, instance specific list of keys. When
      #     present, instance keys override project keys as if
      #     instance['block-project-ssh-keys'] was true.
      #
      # SSH-like commands work by copying a relevant SSH key to
      # the appropriate metadata value. The VM grabs keys from the metadata as
      # follows (pseudo-Python):
      #
      #   def GetAllSshKeys(project, instance):
      #       if 'sshKeys' in instance.metadata:
      #           return (instance.metadata['sshKeys'] +
      #                   instance.metadata['ssh-keys'])
      #       elif instance.metadata['block-project-ssh-keys'] == 'true':
      #           return instance.metadata['ssh-keys']
      #       else:
      #           return (instance.metadata['ssh-keys'] +
      #                   project.metadata['ssh-keys'] +
      #                   project.metadata['sshKeys']) # Legacy Project Keys
      #
      _, ssh_legacy_keys = _GetSSHKeysFromMetadata(instance.metadata)
      if ssh_legacy_keys:
        # If we add a key to project-wide metadata but the per-instance
        # 'sshKeys' metadata exists, we won't be able to ssh in because the VM
        # won't check the project-wide metadata. To avoid this, if the instance
        # has per-instance SSH key metadata, we add the key there instead.
        keys_newly_added = self.EnsureSSHKeyIsInInstance(
            compute_client, user, instance, legacy=True)
      elif _MetadataHasBlockProjectSshKeys(instance.metadata):
        # If the instance 'ssh-keys' metadata overrides the project-wide
        # 'ssh-keys' metadata, we should put our key there.
        keys_newly_added = self.EnsureSSHKeyIsInInstance(
            compute_client, user, instance)
      else:
        # Otherwise, try to add to the project-wide metadata. If we don't have
        # permissions to do that, add to the instance 'ssh-keys' metadata.
        try:
          keys_newly_added = self.EnsureSSHKeyIsInProject(
              compute_client, user, project)
        except SetProjectMetadataError:
          log.info('Could not set project metadata:', exc_info=True)
          # If we can't write to the project metadata, it may be because of a
          # permissions problem (we could inspect this exception object further
          # to make sure, but because we only get a string back this would be
          # fragile). If that's the case, we want to try the writing to instance
          # metadata. We prefer this to the per-instance override of the
          # project metadata.
          log.info('Attempting to set instance metadata.')
          keys_newly_added = self.EnsureSSHKeyIsInInstance(
              compute_client, user, instance)
    return keys_newly_added