示例#1
0
def _BuildComputeSection(instances, private_key_file, known_hosts_file):
    """Returns a string representing the Compute section that should be added."""
    buf = io.StringIO()
    buf.write(_HEADER)

    for instance in instances:
        external_ip_address = (ssh_utils.GetExternalIPAddress(instance,
                                                              no_raise=True))
        host_key_alias = 'compute.{0}'.format(instance.id)

        if external_ip_address:
            buf.write(
                textwrap.dedent("""\
          Host {alias}
              HostName {external_ip_address}
              IdentityFile {private_key_file}
              UserKnownHostsFile={known_hosts_file}
              HostKeyAlias={host_key_alias}
              IdentitiesOnly=yes
              CheckHostIP=no

          """.format(alias=_CreateAlias(instance),
                     external_ip_address=external_ip_address,
                     private_key_file=private_key_file,
                     known_hosts_file=known_hosts_file,
                     host_key_alias=host_key_alias)))

    buf.write(_END_MARKER)
    buf.write('\n')
    return buf.getvalue()
示例#2
0
    def TracerouteInstance(self, instance, traceroute_args, dry_run,
                           resource_registry):
        """Runs a traceroute from localhost to a GCE VM.

    Args:
      instance: Google Compute Engine VM.
      traceroute_args: Additional traceroute args to be passed on.
      dry_run: Whether to only print commands instead of running them.
      resource_registry: gcloud class used for obtaining data from the
        resources.
    """
        instance_string = internal_helpers.GetInstanceNetworkTitleString(
            instance)
        log.out.Print('>>> Tracerouting to %s' % instance_string)

        external_ip = ssh_utils.GetExternalIPAddress(instance)
        cmd = ['traceroute', external_ip]
        if traceroute_args:
            cmd += traceroute_args
        if dry_run:
            external_helper.DryRunLog(' '.join(cmd))
        else:
            external_helper.RunSubprocess(proc_name='Traceroute',
                                          command_list=cmd)
            log.out.Print('>>>')
def RunSSHCommandToInstance(command_list,
                            instance,
                            user,
                            args,
                            ssh_helper,
                            explicit_output_file=None,
                            explicit_error_file=None,
                            dry_run=False):
    """Runs a SSH command to a Google Compute Engine VM.

  Args:
    command_list: List with the ssh command to run.
    instance: The GCE VM object.
    user: The user to be used for the SSH command.
    args: The args used to call the gcloud instance.
    ssh_helper: ssh_utils.BaseSSHCLIHelper instance initialized
      for the command.
    explicit_output_file: Use this file for piping stdout of the SSH command,
                          instead of using stdout. This is useful for
                          capturing the command and analyzing it.
    explicit_error_file: Use this file for piping stdout of the SSH command,
                         instead of using stdout. This is useful for
                         capturing the command and analyzing it.
    dry_run: Whether or not this is a dry-run (only print, not run).
  Returns:
    The exit code of the SSH command
  Raises:
    ssh.CommandError: there was an error running a SSH command
  """
    external_ip_address = ssh_utils.GetExternalIPAddress(instance)
    remote = ssh.Remote(external_ip_address, user)

    identity_file = None
    options = None
    if not args.plain:
        identity_file = ssh_helper.keys.key_file
        options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance),
                                       args.strict_host_key_checking)
    extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, user)
    remainder = []

    remote_command = containers.GetRemoteCommand(None, command_list)

    cmd = ssh.SSHCommand(remote,
                         identity_file=identity_file,
                         options=options,
                         extra_flags=extra_flags,
                         remote_command=remote_command,
                         remainder=remainder)
    if dry_run:
        DryRunLog(' '.join(cmd.Build(ssh_helper.env)))
        return 0

    return_code = cmd.Run(ssh_helper.env,
                          force_connect=True,
                          explicit_output_file=explicit_output_file,
                          explicit_error_file=explicit_error_file)
    log.out.flush()
    return return_code
 def testSingleExternalNic(self):
     instance = self.messages.Instance(
         name='instance-1',
         zone='zone-1',
         networkInterfaces=[self.external_nic])
     ip_result = ssh_utils.GetExternalIPAddress(instance)
     self.assertEqual(self.external_ip_address, ip_result)
     self.assertIs(ssh_utils.GetExternalInterface(instance),
                   self.external_nic)
 def testMultipleNics(self):
     instance = self.messages.Instance(
         name='instance-1',
         zone='zone-1',
         networkInterfaces=[self.internal_nic, self.external_nic])
     self.assertEqual(ssh_utils.GetExternalIPAddress(instance),
                      self.external_ip_address)
     self.assertIs(ssh_utils.GetExternalInterface(instance),
                   self.external_nic)
     self.assertEqual(ssh_utils.GetInternalIPAddress(instance),
                      self.internal_ip_address)
     self.assertIs(ssh_utils.GetInternalInterface(instance),
                   self.internal_nic)
def GetInstanceNetworkTitleString(instance):
    """Returns a string that identifies the instance.

  Args:
    instance: The instance proto.

  Returns:
    A string that identifies the zone and the external ip of the instance.
  """
    external_ip = ssh_utils.GetExternalIPAddress(instance)

    result = '[{instance_name}] ({instance_ip})'.format(
        instance_name=instance.selfLink, instance_ip=external_ip)
    return result
示例#7
0
    def SSHToInstance(self, args, instance):
        """Helper to manage authentication followed by SSH to the instance."""
        args = self._DefaultArgsForSSH(args)

        external_nat = ssh_utils.GetExternalIPAddress(instance)
        log.status.Print(
            'Trying to SSH to VM with NAT IP:{}'.format(external_nat))
        remote = ssh.Remote(external_nat, ssh.GetDefaultSshUsername())
        args.ssh_key_file = ssh.Keys.DEFAULT_KEY_FILE

        ssh_helper = ssh_utils.BaseSSHCLIHelper()
        ssh_helper.Run(args)
        identity_file = ssh_helper.keys.key_file

        user, _ = ssh_utils.GetUserAndInstance(args.name)
        host_keys = self._GetHostKeyFromInstance(args.zone, ssh_helper,
                                                 instance)
        options = self._GetSSHOptions(args.name, ssh_helper, instance,
                                      host_keys)
        self._WaitForSSHKeysToPropagate(ssh_helper, remote, identity_file,
                                        user, instance, options)

        extra_flags = []
        # Ctpu seems to be forwarding some other ports on what
        # seems like the TPU node. Need to understand better before enabling.
        if args.forward_ports:
            extra_flags.extend([
                '-A', '-L', '6006:localhost:6006', '-L', '8888:localhost:8888'
            ])
        ssh_cmd_args = {
            'remote': remote,
            'identity_file': identity_file,
            'options': options,
            'extra_flags': extra_flags
        }
        cmd = ssh.SSHCommand(**ssh_cmd_args)
        # Errors from SSH itself result in an ssh.CommandError being raised
        return_code = cmd.Run(
            ssh_helper.env,
            force_connect=properties.VALUES.ssh.putty_force_connect.GetBool())
        if return_code:
            # This is the return code of the remote command.  Problems with SSH itself
            # will result in ssh.CommandError being raised above.
            sys.exit(return_code)
示例#8
0
    def Run(self, args):
        super(SshGA, self).Run(args)

        user, instance_name = ssh_utils.GetUserAndInstance(
            args.user_host, self._use_accounts_service, self.http)
        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance_name],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            self.resources,
            scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0]
        instance = self.GetInstance(instance_ref)

        if args.internal_ip:
            ip_address = ssh_utils.GetInternalIPAddress(instance)
            self._PreliminarylyVerifyInstance(args, instance,
                                              instance_ref.project, user,
                                              ip_address)
        else:
            ip_address = ssh_utils.GetExternalIPAddress(instance)

        # Don't use strict error checking for ssh: if the executed command fails, we
        # don't want to consider it an error. We do, however, want to propagate its
        # return code.
        ssh_args = ssh_utils.GetSshArgs(args, user, instance, ip_address,
                                        self.env, self.keys)
        return_code = self.ActuallyRun(
            args,
            ssh_args,
            user,
            instance,
            instance_ref.project,
            ip_address,
            strict_error_checking=False,
            use_account_service=self._use_accounts_service)
        if return_code:
            # Can't raise an exception because we don't want any "ERROR" message
            # printed; the output from `ssh` will be enough.
            sys.exit(return_code)
示例#9
0
    def Run(self, args):
        """See ssh_utils.BaseSSHCLICommand.Run."""
        holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
        client = holder.client

        ssh_helper = ssh_utils.BaseSSHCLIHelper()
        ssh_helper.Run(args)
        user, instance_name = ssh_utils.GetUserAndInstance(args.user_host)
        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance_name],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            holder.resources,
            scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0]
        instance = ssh_helper.GetInstance(client, instance_ref)
        project = ssh_helper.GetProject(client, instance_ref.project)
        if args.plain:
            use_oslogin = False
        else:
            user, use_oslogin = ssh_helper.CheckForOsloginAndGetUser(
                instance, project, user, self.ReleaseTrack())
        if self._use_internal_ip:
            ip_address = ssh_utils.GetInternalIPAddress(instance)
        else:
            ip_address = ssh_utils.GetExternalIPAddress(instance)

        remote = ssh.Remote(ip_address, user)

        identity_file = None
        options = None
        if not args.plain:
            identity_file = ssh_helper.keys.key_file
            options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance),
                                           args.strict_host_key_checking)

        extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, ip_address)
        remainder = []

        if args.ssh_args:
            remainder.extend(args.ssh_args)

        # Transform args.command into arg list or None if no command
        command_list = args.command.split(' ') if args.command else None
        tty = containers.GetTty(args.container, command_list)
        remote_command = containers.GetRemoteCommand(args.container,
                                                     command_list)

        cmd = ssh.SSHCommand(remote,
                             identity_file=identity_file,
                             options=options,
                             extra_flags=extra_flags,
                             remote_command=remote_command,
                             tty=tty,
                             remainder=remainder)
        if args.dry_run:
            log.out.Print(' '.join(cmd.Build(ssh_helper.env)))
            return

        if args.plain or use_oslogin:
            keys_newly_added = False
        else:
            keys_newly_added = ssh_helper.EnsureSSHKeyExists(
                client, remote.user, instance, project)

        if keys_newly_added:
            poller = ssh.SSHPoller(
                remote,
                identity_file=identity_file,
                options=options,
                extra_flags=extra_flags,
                max_wait_ms=ssh_utils.SSH_KEY_PROPAGATION_TIMEOUT_SEC)
            log.status.Print('Waiting for SSH key to propagate.')
            # TODO(b/35355795): Don't force_connect
            try:
                poller.Poll(ssh_helper.env, force_connect=True)
            except retry.WaitException:
                raise ssh_utils.NetworkError()

        if self._use_internal_ip:
            ssh_helper.PreliminarilyVerifyInstance(instance.id, remote,
                                                   identity_file, options)

        return_code = cmd.Run(ssh_helper.env, force_connect=True)
        if return_code:
            # Can't raise an exception because we don't want any "ERROR" message
            # printed; the output from `ssh` will be enough.
            sys.exit(return_code)
示例#10
0
    def RunScp(self,
               compute_holder,
               args,
               port=None,
               recursive=False,
               compress=False,
               extra_flags=None,
               release_track=None,
               ip_type=ip.IpTypeEnum.EXTERNAL):
        """SCP files between local and remote GCE instance.

    Run this method from subclasses' Run methods.

    Args:
      compute_holder: The ComputeApiHolder.
      args: argparse.Namespace, the args the command was invoked with.
      port: str or None, Port number to use for SSH connection.
      recursive: bool, Whether to use recursive copying using -R flag.
      compress: bool, Whether to use compression.
      extra_flags: [str] or None, extra flags to add to command invocation.
      release_track: obj, The current release track.
      ip_type: IpTypeEnum, Specify using internal ip or external ip address.

    Raises:
      ssh_utils.NetworkError: Network issue which likely is due to failure
        of SSH key propagation.
      ssh.CommandError: The SSH command exited with SSH exit code, which
        usually implies that a connection problem occurred.
    """
        if release_track is None:
            release_track = base.ReleaseTrack.GA
        super(BaseScpHelper, self).Run(args)

        dst = ssh.FileReference.FromPath(args.destination)
        srcs = [ssh.FileReference.FromPath(src) for src in args.sources]

        # Make sure we have a unique remote
        ssh.SCPCommand.Verify(srcs, dst, single_remote=True)

        remote = dst.remote or srcs[0].remote
        if not dst.remote:  # Make sure all remotes point to the same ref
            for src in srcs:
                src.remote = remote

        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [remote.host],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            compute_holder.resources,
            scope_lister=instance_flags.GetInstanceZoneScopeLister(
                compute_holder.client))[0]
        instance = self.GetInstance(compute_holder.client, instance_ref)
        project = self.GetProject(compute_holder.client, instance_ref.project)

        # Now replace the instance name with the actual IP/hostname
        if ip_type is ip.IpTypeEnum.INTERNAL:
            remote.host = ssh_utils.GetInternalIPAddress(instance)
        else:
            remote.host = ssh_utils.GetExternalIPAddress(instance)
        if not remote.user:
            remote.user = ssh.GetDefaultSshUsername(warn_on_account_user=True)
        if args.plain:
            use_oslogin = False
        else:
            public_key = self.keys.GetPublicKey().ToEntry(include_comment=True)
            remote.user, use_oslogin = ssh.CheckForOsloginAndGetUser(
                instance, project, remote.user, public_key, release_track)

        identity_file = None
        options = None
        if not args.plain:
            identity_file = self.keys.key_file
            options = self.GetConfig(ssh_utils.HostKeyAlias(instance),
                                     args.strict_host_key_checking)

        tunnel_helper = None
        cmd_port = port
        interface = None
        if hasattr(args, 'tunnel_through_iap') and args.tunnel_through_iap:
            tunnel_helper, interface = ssh_utils.CreateIapTunnelHelper(
                args, instance_ref, instance, ip_type, port=port)
            tunnel_helper.StartListener()
            cmd_port = str(tunnel_helper.GetLocalPort())
            if dst.remote:
                dst.remote.host = 'localhost'
            else:
                for src in srcs:
                    src.remote.host = 'localhost'

        cmd = ssh.SCPCommand(srcs,
                             dst,
                             identity_file=identity_file,
                             options=options,
                             recursive=recursive,
                             compress=compress,
                             port=cmd_port,
                             extra_flags=extra_flags)

        if args.dry_run:
            log.out.Print(' '.join(cmd.Build(self.env)))
            if tunnel_helper:
                tunnel_helper.StopListener()
            return

        if args.plain or use_oslogin:
            keys_newly_added = False
        else:
            keys_newly_added = self.EnsureSSHKeyExists(compute_holder.client,
                                                       remote.user, instance,
                                                       project)

        if keys_newly_added:
            poller_tunnel_helper = None
            if tunnel_helper:
                poller_tunnel_helper, _ = ssh_utils.CreateIapTunnelHelper(
                    args,
                    instance_ref,
                    instance,
                    ip_type,
                    port=port,
                    interface=interface)
                poller_tunnel_helper.StartListener(
                    accept_multiple_connections=True)
            poller = ssh_utils.CreateSSHPoller(remote,
                                               identity_file,
                                               options,
                                               poller_tunnel_helper,
                                               port=port)

            log.status.Print('Waiting for SSH key to propagate.')
            # TODO(b/35355795): Don't force_connect
            try:
                poller.Poll(self.env, force_connect=True)
            except retry.WaitException:
                if tunnel_helper:
                    tunnel_helper.StopListener()
                raise ssh_utils.NetworkError()
            finally:
                if poller_tunnel_helper:
                    poller_tunnel_helper.StopListener()

        if ip_type is ip.IpTypeEnum.INTERNAL and not tunnel_helper:
            # The IAP Tunnel connection uses instance name and network interface name,
            # so do not need to additionally verify the instance. Also, the
            # SSHCommand used within the function does not support IAP Tunnels.
            self.PreliminarilyVerifyInstance(instance.id, remote,
                                             identity_file, options)

        try:
            # Errors from the SCP command result in an ssh.CommandError being raised
            cmd.Run(self.env, force_connect=True)
        finally:
            if tunnel_helper:
                tunnel_helper.StopListener()
示例#11
0
  def Run(self, args):
    """See ssh_utils.BaseSSHCLICommand.Run."""

    on_prem = (
        args.IsKnownAndSpecified('network') and
        args.IsKnownAndSpecified('region'))
    if on_prem:
      args.plain = True

    # These two lines are needed to ensure reauth is performed as needed, even
    # for on-prem, which doesn't use the resulting variables.
    holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
    client = holder.client

    ssh_helper = ssh_utils.BaseSSHCLIHelper()
    ssh_helper.Run(args)
    oslogin_state = ssh.OsloginState()

    if on_prem:
      user, ip = ssh_utils.GetUserAndInstance(args.user_host)
      remote = ssh.Remote(ip, user)

      iap_tunnel_args = iap_tunnel.CreateOnPremSshTunnelArgs(
          args, self.ReleaseTrack(), ip)
      instance_address = ip
      internal_address = ip
    else:
      user, instance_name = ssh_utils.GetUserAndInstance(args.user_host)
      instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
          [instance_name], compute_scope.ScopeEnum.ZONE, args.zone,
          holder.resources,
          scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0]
      instance = ssh_helper.GetInstance(client, instance_ref)
      project = ssh_helper.GetProject(client, instance_ref.project)
      host_keys = ssh_helper.GetHostKeysFromGuestAttributes(
          client, instance_ref, instance, project)
      iap_tunnel_args = iap_tunnel.CreateSshTunnelArgs(
          args, self.ReleaseTrack(), instance_ref,
          ssh_utils.GetExternalInterface(instance, no_raise=True))

      internal_address = ssh_utils.GetInternalIPAddress(instance)

      if args.troubleshoot:
        log.status.Print(TROUBLESHOOT_HEADER.format(
            instance_ref, args.zone or instance_ref.zone,
            datetime.datetime.now()
        ))
        RunTroubleshooting(project, args.zone or instance_ref.zone,
                           instance, iap_tunnel_args)
        return

      if not host_keys and host_keys is not None:
        log.debug('Unable to retrieve host keys from instance metadata. '
                  'Continuing.')
      expiration, expiration_micros = ssh_utils.GetSSHKeyExpirationFromArgs(
          args)
      if args.plain:
        oslogin_state.oslogin_enabled = False
      else:
        public_key = ssh_helper.keys.GetPublicKey().ToEntry(
            include_comment=True)
        # If there is an '@' symbol in the user_host arg, the user is requesting
        # to connect as a specific user. This may get overridden by OS Login.
        username_requested = '@' in args.user_host
        oslogin_state = ssh.GetOsloginState(
            instance, project, user, public_key, expiration_micros,
            self.ReleaseTrack(), username_requested=username_requested)
        user = oslogin_state.user

      log.debug(oslogin_state)

      if iap_tunnel_args:
        # IAP Tunnel only uses instance_address for the purpose of --ssh-flag
        # substitution. In this case, dest_addr doesn't do much, it just matches
        # against entries in the user's ssh_config file. It's best to use
        # something unique to avoid false positive matches, thus we use
        # HostKeyAlias.
        instance_address = internal_address
        dest_addr = ssh_utils.HostKeyAlias(instance)
      elif args.internal_ip:
        instance_address = internal_address
        dest_addr = instance_address
      else:
        instance_address = ssh_utils.GetExternalIPAddress(instance)
        dest_addr = instance_address
      remote = ssh.Remote(dest_addr, user)

    # identity_file_list will be None if security keys are not enabled.
    identity_file_list = ssh.WriteSecurityKeys(oslogin_state)
    identity_file = None
    options = None
    if not args.plain:
      if not identity_file_list:
        identity_file = ssh_helper.keys.key_file
      options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance),
                                     args.strict_host_key_checking,
                                     host_keys_to_add=host_keys)

    extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, instance_address,
                                                 internal_address)
    remainder = []

    if args.ssh_args:
      remainder.extend(args.ssh_args)

    # Transform args.command into arg list or None if no command
    command_list = args.command.split(' ') if args.command else None
    tty = containers.GetTty(args.container, command_list)
    remote_command = containers.GetRemoteCommand(args.container, command_list)

    # Do not include default port since that will prevent users from
    # specifying a custom port (b/121998342).
    ssh_cmd_args = {'remote': remote,
                    'identity_file': identity_file,
                    'options': options,
                    'extra_flags': extra_flags,
                    'remote_command': remote_command,
                    'tty': tty,
                    'iap_tunnel_args': iap_tunnel_args,
                    'remainder': remainder,
                    'identity_list': identity_file_list}

    cmd = ssh.SSHCommand(**ssh_cmd_args)

    if args.dry_run:
      # Add quotes around any arguments that contain spaces.
      log.out.Print(' '.join('"{0}"'.format(arg) if ' ' in arg else arg
                             for arg in cmd.Build(ssh_helper.env)))
      return

    # Raise errors if instance requires a security key but the local
    # envionment doesn't support them. This is after the 'dry-run' because
    # we want to allow printing the command regardless.
    if self.enable_security_keys:
      ssh_utils.ConfirmSecurityKeyStatus(oslogin_state)

    if args.plain or oslogin_state.oslogin_enabled:
      keys_newly_added = False
    else:
      keys_newly_added = ssh_helper.EnsureSSHKeyExists(
          client, remote.user, instance, project, expiration=expiration)

    if keys_newly_added:
      poller = ssh_utils.CreateSSHPoller(remote, identity_file, options,
                                         iap_tunnel_args,
                                         extra_flags=extra_flags)
      log.status.Print('Waiting for SSH key to propagate.')
      # TODO(b/35355795): Don't force_connect
      try:
        poller.Poll(
            ssh_helper.env,
            force_connect=properties.VALUES.ssh.putty_force_connect.GetBool())
      except retry.WaitException:
        raise ssh_utils.NetworkError()

    if args.internal_ip and not on_prem:
      ssh_helper.PreliminarilyVerifyInstance(instance.id, remote, identity_file,
                                             options)

    # Errors from SSH itself result in an ssh.CommandError being raised
    try:
      return_code = cmd.Run(
          ssh_helper.env,
          force_connect=properties.VALUES.ssh.putty_force_connect.GetBool())
    except ssh.CommandError as e:
      if not on_prem:
        log.status.Print(self.createRecommendMessage(args, instance_name,
                                                     instance_ref, project))
      raise e

    if return_code:
      # This is the return code of the remote command.  Problems with SSH itself
      # will result in ssh.CommandError being raised above.
      sys.exit(return_code)
示例#12
0
  def Run(self, args):
    """See ssh_utils.BaseSSHCLICommand.Run."""
    holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
    client = holder.client

    ssh_helper = ssh_utils.BaseSSHCLIHelper()
    ssh_helper.Run(args)
    user, instance_name = ssh_utils.GetUserAndInstance(args.user_host)
    instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
        [instance_name], compute_scope.ScopeEnum.ZONE, args.zone,
        holder.resources,
        scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0]
    instance = ssh_helper.GetInstance(client, instance_ref)
    project = ssh_helper.GetProject(client, instance_ref.project)
    if self.get_host_keys:
      host_keys = ssh_helper.GetHostKeysFromGuestAttributes(
          client, instance_ref)
      if not host_keys:
        log.warning('Unable to retrieve host keys from instance metadata. '
                    'Continuing.')
    else:
      host_keys = {}
    expiration, expiration_micros = ssh_utils.GetSSHKeyExpirationFromArgs(args)
    if args.plain:
      use_oslogin = False
    else:
      public_key = ssh_helper.keys.GetPublicKey().ToEntry(include_comment=True)
      user, use_oslogin = ssh.CheckForOsloginAndGetUser(
          instance, project, user, public_key, expiration_micros,
          self.ReleaseTrack())

    iap_tunnel_args = iap_tunnel.SshTunnelArgs.FromArgs(
        args, self.ReleaseTrack(), instance_ref,
        ssh_utils.GetExternalInterface(instance, no_raise=True))

    internal_address = ssh_utils.GetInternalIPAddress(instance)

    if iap_tunnel_args:
      # IAP Tunnel only uses instance_address for the purpose of --ssh-flag
      # substitution. In this case, dest_addr doesn't do much, it just matches
      # against entries in the user's ssh_config file. It's best to use
      # something unique to avoid false positive matches, thus we use
      # HostKeyAlias.
      instance_address = internal_address
      dest_addr = ssh_utils.HostKeyAlias(instance)
    elif args.internal_ip:
      instance_address = internal_address
      dest_addr = instance_address
    else:
      instance_address = ssh_utils.GetExternalIPAddress(instance)
      dest_addr = instance_address
    remote = ssh.Remote(dest_addr, user)

    identity_file = None
    options = None
    if not args.plain:
      identity_file = ssh_helper.keys.key_file
      options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance),
                                     args.strict_host_key_checking,
                                     host_keys_to_add=host_keys)

    extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, instance_address,
                                                 internal_address)
    remainder = []

    if args.ssh_args:
      remainder.extend(args.ssh_args)

    # Transform args.command into arg list or None if no command
    command_list = args.command.split(' ') if args.command else None
    tty = containers.GetTty(args.container, command_list)
    remote_command = containers.GetRemoteCommand(args.container, command_list)

    # Do not include default port since that will prevent users from
    # specifying a custom port (b/121998342).
    ssh_cmd_args = {'remote': remote,
                    'identity_file': identity_file,
                    'options': options,
                    'extra_flags': extra_flags,
                    'remote_command': remote_command,
                    'tty': tty,
                    'iap_tunnel_args': iap_tunnel_args,
                    'remainder': remainder}

    cmd = ssh.SSHCommand(**ssh_cmd_args)

    if args.dry_run:
      log.out.Print(' '.join(cmd.Build(ssh_helper.env)))
      return

    if args.plain or use_oslogin:
      keys_newly_added = False
    else:
      keys_newly_added = ssh_helper.EnsureSSHKeyExists(
          client, remote.user, instance, project, expiration=expiration)

    if keys_newly_added:
      poller = ssh_utils.CreateSSHPoller(remote, identity_file, options,
                                         iap_tunnel_args,
                                         extra_flags=extra_flags)
      log.status.Print('Waiting for SSH key to propagate.')
      # TODO(b/35355795): Don't force_connect
      try:
        poller.Poll(ssh_helper.env, force_connect=True)
      except retry.WaitException:
        raise ssh_utils.NetworkError()

    if args.internal_ip:
      ssh_helper.PreliminarilyVerifyInstance(instance.id, remote, identity_file,
                                             options)

    # Errors from SSH itself result in an ssh.CommandError being raised
    return_code = cmd.Run(ssh_helper.env, force_connect=True)
    if return_code:
      # This is the return code of the remote command.  Problems with SSH itself
      # will result in ssh.CommandError being raised above.
      sys.exit(return_code)
示例#13
0
    def Run(self, args):
        """See ssh_utils.BaseSSHCLICommand.Run."""
        holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
        client = holder.client

        ssh_helper = ssh_utils.BaseSSHCLIHelper()
        ssh_helper.Run(args)
        user, instance_name = ssh_utils.GetUserAndInstance(args.user_host)
        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance_name],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            holder.resources,
            scope_lister=instance_flags.GetInstanceZoneScopeLister(client))[0]
        instance = ssh_helper.GetInstance(client, instance_ref)
        project = ssh_helper.GetProject(client, instance_ref.project)
        if args.plain:
            use_oslogin = False
        else:
            public_key = ssh_helper.keys.GetPublicKey().ToEntry(
                include_comment=True)
            user, use_oslogin = ssh.CheckForOsloginAndGetUser(
                instance, project, user, public_key, self.ReleaseTrack())
        if args.internal_ip:
            ip_address = ssh_utils.GetInternalIPAddress(instance)
        else:
            ip_address = ssh_utils.GetExternalIPAddress(instance)

        remote = ssh.Remote(ip_address, user)

        identity_file = None
        options = None
        if not args.plain:
            identity_file = ssh_helper.keys.key_file
            options = ssh_helper.GetConfig(ssh_utils.HostKeyAlias(instance),
                                           args.strict_host_key_checking)

        extra_flags = ssh.ParseAndSubstituteSSHFlags(args, remote, ip_address)
        remainder = []

        if args.ssh_args:
            remainder.extend(args.ssh_args)

        # Transform args.command into arg list or None if no command
        command_list = args.command.split(' ') if args.command else None
        tty = containers.GetTty(args.container, command_list)
        remote_command = containers.GetRemoteCommand(args.container,
                                                     command_list)

        target_remote = remote
        port = ssh_utils.DEFAULT_SSH_PORT
        ip_type = (ip.IpTypeEnum.INTERNAL
                   if args.internal_ip else ip.IpTypeEnum.EXTERNAL)
        tunnel_helper = None
        interface = None
        if hasattr(args, 'tunnel_through_iap') and args.tunnel_through_iap:
            tunnel_helper, interface = ssh_utils.CreateIapTunnelHelper(
                args, instance_ref, instance, ip_type)
            tunnel_helper.StartListener()
            target_remote = ssh.Remote('localhost', user)
            port = tunnel_helper.GetLocalPort()

        cmd = ssh.SSHCommand(target_remote,
                             port=str(port),
                             identity_file=identity_file,
                             options=options,
                             extra_flags=extra_flags,
                             remote_command=remote_command,
                             tty=tty,
                             remainder=remainder)
        if args.dry_run:
            log.out.Print(' '.join(cmd.Build(ssh_helper.env)))
            if tunnel_helper:
                tunnel_helper.StopListener()
            return

        if args.plain or use_oslogin:
            keys_newly_added = False
        else:
            keys_newly_added = ssh_helper.EnsureSSHKeyExists(
                client, remote.user, instance, project)

        if keys_newly_added:
            poller_tunnel_helper = None
            if tunnel_helper:
                poller_tunnel_helper, _ = ssh_utils.CreateIapTunnelHelper(
                    args, instance_ref, instance, ip_type, interface=interface)
                poller_tunnel_helper.StartListener(
                    accept_multiple_connections=True)
            poller = ssh_utils.CreateSSHPoller(remote,
                                               identity_file,
                                               options,
                                               poller_tunnel_helper,
                                               extra_flags=extra_flags)

            log.status.Print('Waiting for SSH key to propagate.')
            # TODO(b/35355795): Don't force_connect
            try:
                poller.Poll(ssh_helper.env, force_connect=True)
            except retry.WaitException:
                if tunnel_helper:
                    tunnel_helper.StopListener()
                raise ssh_utils.NetworkError()
            finally:
                if poller_tunnel_helper:
                    poller_tunnel_helper.StopListener()

        if args.internal_ip and not tunnel_helper:
            # The IAP Tunnel connection uses instance name and network interface name,
            # so do not need to additionally verify the instance.  Also, the
            # SSHCommand used within the function does not support IAP Tunnels.
            ssh_helper.PreliminarilyVerifyInstance(instance.id, remote,
                                                   identity_file, options)

        try:
            # Errors from SSH itself result in an ssh.CommandError being raised
            return_code = cmd.Run(ssh_helper.env, force_connect=True)
        finally:
            if tunnel_helper:
                tunnel_helper.StopListener()
        if return_code:
            # This is the return code of the remote command.  Problems with SSH itself
            # will result in ssh.CommandError being raised above.
            sys.exit(return_code)
示例#14
0
    def Run(self, args):
        super(CopyFiles, self).Run(args)
        # TODO(b/33467618): Change this implementation to use ssh.SCPCommand and
        # ssh.FileReference objects.

        file_specs = []

        # Parses the positional arguments.
        for arg in args.sources + [args.destination]:
            if ssh.IsScpLocalPath(arg):
                file_specs.append(LocalFile(arg))
            else:
                user_host, file_path = arg.split(':', 1)
                user_host_parts = user_host.split('@', 1)
                if len(user_host_parts) == 1:
                    user = ssh.GetDefaultSshUsername(warn_on_account_user=True)
                    source_instance = user_host_parts[0]
                else:
                    user, source_instance = user_host_parts
                file_specs.append(RemoteFile(user, source_instance, file_path))

        log.debug('Normalized arguments: %s', file_specs)

        # Validates the positional arguments.
        # TODO(user): Look into relaxing these conditions.
        sources = file_specs[:-1]
        destination = file_specs[-1]
        if isinstance(destination, LocalFile):
            for source in sources:
                if isinstance(source, LocalFile):
                    raise exceptions.ToolException(
                        'All sources must be remote files when the destination '
                        'is local.')

        else:  # RemoteFile
            for source in sources:
                if isinstance(source, RemoteFile):
                    raise exceptions.ToolException(
                        'All sources must be local files when the destination '
                        'is remote.')

        destination_instances = set()
        for file_spec in file_specs:
            if isinstance(file_spec, RemoteFile):
                destination_instances.add(file_spec.instance_name)

        if len(destination_instances) > 1:
            raise exceptions.ToolException(
                'Copies must involve exactly one virtual machine instance; '
                'your invocation refers to [{0}] instances: [{1}].'.format(
                    len(destination_instances),
                    ', '.join(sorted(destination_instances))))

        source_instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [source_instance],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            self.resources,
            scope_lister=flags.GetDefaultScopeLister(self.compute_client,
                                                     self.project))[0]
        source_instance = self.GetInstance(source_instance_ref)
        external_ip_address = ssh_utils.GetExternalIPAddress(source_instance)

        # Builds the scp command.
        scp_args = [self.env.scp]
        if not args.plain:
            scp_args.extend(ssh.GetDefaultFlags(self.keys.key_file))
            host_key_alias = self.HostKeyAlias(source_instance)
            scp_args.extend(
                ssh.GetHostKeyArgs(host_key_alias, args.plain,
                                   args.strict_host_key_checking))
            scp_args.append('-r')

        for file_spec in file_specs:
            if isinstance(file_spec, LocalFile):
                scp_args.append(file_spec.file_path)

            else:
                scp_args.append('{0}:{1}'.format(
                    ssh.UserHost(file_spec.user, external_ip_address),
                    file_spec.file_path))

        self.ActuallyRun(args, scp_args, user, source_instance,
                         source_instance_ref.project)
示例#15
0
    def Run(self, args):
        super(Scp, self).Run(args)

        file_specs = []

        # Parses the positional arguments.
        for arg in args.sources + [args.destination]:
            if ssh.IsScpLocalPath(arg):
                file_specs.append(LocalFile(arg))
            else:
                user_host, file_path = arg.split(':', 1)
                user_host_parts = user_host.split('@', 1)
                if len(user_host_parts) == 1:
                    user = ssh.GetDefaultSshUsername(warn_on_account_user=True)
                    instance = user_host_parts[0]
                else:
                    user, instance = user_host_parts
                file_specs.append(RemoteFile(user, instance, file_path))

        log.debug('Normalized arguments: %s', file_specs)

        # Validates the positional arguments.
        # TODO(b/21515495): Look into relaxing these conditions.
        sources = file_specs[:-1]
        destination = file_specs[-1]
        if isinstance(destination, LocalFile):
            for source in sources:
                if isinstance(source, LocalFile):
                    raise exceptions.ToolException(
                        'All sources must be remote files when the destination '
                        'is local.')

        else:  # RemoteFile
            for source in sources:
                if isinstance(source, RemoteFile):
                    raise exceptions.ToolException(
                        'All sources must be local files when the destination '
                        'is remote.')

        instances = set()
        for file_spec in file_specs:
            if isinstance(file_spec, RemoteFile):
                instances.add(file_spec.instance_name)

        if len(instances) > 1:
            raise exceptions.ToolException(
                'Copies must involve exactly one virtual machine instance; '
                'your invocation refers to [{0}] instances: [{1}].'.format(
                    len(instances), ', '.join(sorted(instances))))

        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            self.resources,
            scope_lister=flags.GetDefaultScopeLister(self.compute_client,
                                                     self.project))[0]
        instance = self.GetInstance(instance_ref)
        external_ip_address = ssh_utils.GetExternalIPAddress(instance)

        # Builds the scp command.
        scp_args = [self.scp_executable]
        if not args.plain:
            scp_args.extend(self.GetDefaultFlags())
            host_key_alias = self.HostKeyAlias(instance)
            scp_args.extend(self.GetHostKeyArgs(args, host_key_alias))

        # apply args
        if args.quiet:
            scp_args.append('-q')
        if args.port:
            scp_args.extend(['-P', args.port])
        if args.recurse:
            scp_args.append('-r')
        if args.compress:
            scp_args.append('-C')
        if args.scp_flag:
            scp_args.extend(args.scp_flag)

        for file_spec in file_specs:
            if isinstance(file_spec, LocalFile):
                scp_args.append(file_spec.file_path)

            else:
                scp_args.append('{0}:{1}'.format(
                    ssh.UserHost(file_spec.user, external_ip_address),
                    file_spec.file_path))

        self.ActuallyRun(args, scp_args, user, instance, instance_ref.project)
示例#16
0
  def SSHToInstance(self, args, instance):
    """Helper to manage authentication followed by SSH to the instance."""
    args = self._DefaultArgsForSSH(args)

    external_nat = ssh_utils.GetExternalIPAddress(instance)
    log.status.Print(
        'Trying to SSH to VM with NAT IP:{}'.format(external_nat))
    args.ssh_key_file = ssh.Keys.DEFAULT_KEY_FILE

    ssh_helper = ssh_utils.BaseSSHCLIHelper()
    ssh_helper.Run(args)
    identity_file = ssh_helper.keys.key_file

    user, _ = ssh_utils.GetUserAndInstance(args.name)
    host_keys = self._GetHostKeyFromInstance(args.zone, ssh_helper, instance)
    options = self._GetSSHOptions(args.name, ssh_helper,
                                  instance, host_keys)

    public_key = ssh_helper.keys.GetPublicKey().ToEntry(include_comment=True)
    user, use_oslogin = ssh.CheckForOsloginAndGetUser(
        instance,
        ssh_helper.GetProject(
            self.client, properties.VALUES.core.project.Get(required=True)),
        user,
        public_key,
        None,
        self.release_track,
        username_requested=False)

    remote = ssh.Remote(external_nat, user)
    if not use_oslogin:
      self._WaitForSSHKeysToPropagate(ssh_helper, remote, identity_file, user,
                                      instance, options)

    extra_flags = []
    # Ctpu seems to be forwarding some other ports on what
    # seems like the TPU node. Need to understand better before enabling.
    if args.forward_ports:
      extra_flags.extend(
          ['-A', '-L', '6006:localhost:6006', '-L', '8888:localhost:8888'])
    ssh_cmd_args = {
        'remote': remote,
        'identity_file': identity_file,
        'options': options,
        'extra_flags': extra_flags
    }

    cmd = ssh.SSHCommand(**ssh_cmd_args)
    max_attempts = 10
    sleep_interval = 30
    # Since the instance was just created, it can take a while for the instance
    # to be ready to accept ssh connections, therefore retry up to 5m. Doesn't
    # need to be backed off, regular interval retry is sufficient since we
    # aren't looking to throttle.
    for i in range(max_attempts):
      try:
        log.status.Print('SSH Attempt #{}...'.format(i))
        # Errors from SSH itself result in an ssh.CommandError being raised
        return_code = cmd.Run(
            ssh_helper.env,
            force_connect=properties.VALUES.ssh.putty_force_connect.GetBool())
        if return_code:
          # This is the return code of the remote command.
          # Problems with SSH itself will result in ssh.CommandError
          # being raised above.
          sys.exit(return_code)
      except ssh.CommandError as e:
        if i == max_attempts - 1:
          raise e
        log.status.Print(
            'Retrying: SSH command error: {}'.format(six.text_type(e)))
        time.sleep(sleep_interval)
        continue
      break
  def Run(self, args):
    super(ConnectToSerialPort, self).Run(args)

    parts = args.user_host.split('@')
    if len(parts) == 1:
      user = getpass.getuser()
      instance = parts[0]
    elif len(parts) == 2:
      user, instance = parts
    else:
      raise exceptions.ToolException(
          'Expected argument of the form [USER@]INSTANCE; received [{0}].'
          .format(args.user_host))

    # Update google_compute_known_hosts file with published host key
    if args.serial_port_gateway == SERIAL_PORT_GATEWAY:
      http_client = http.Http()
      http_response = http_client.request(HOST_KEY_URL)
      hostname = '[{0}]:{1}'.format(SERIAL_PORT_GATEWAY, CONNECTION_PORT)
      known_hosts = ssh.KnownHosts.FromDefaultFile()
      if http_response[0]['status'] == '200':
        host_key = http_response[1].strip()
        known_hosts.Add(hostname, host_key, overwrite=True)
        known_hosts.Write()
      elif known_hosts.ContainsAlias(hostname):
        log.warn('Unable to download and update Host Key for [{0}] from [{1}]. '
                 'Attempting to connect using existing Host Key in [{2}]. If '
                 'the connection fails, please try again to update the Host '
                 'Key.'.format(SERIAL_PORT_GATEWAY, HOST_KEY_URL,
                               known_hosts.file_path))
      else:
        known_hosts.Add(hostname, DEFAULT_HOST_KEY)
        known_hosts.Write()
        log.warn('Unable to download Host Key for [{0}] from [{1}]. To ensure '
                 'the security of the SSH connetion, gcloud will attempt to '
                 'connect using a hard-coded Host Key value. If the connection '
                 'fails, please try again. If the problem persists, try '
                 'updating gcloud and connecting again.'
                 .format(SERIAL_PORT_GATEWAY, HOST_KEY_URL))

    instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
        [instance], compute_scope.ScopeEnum.ZONE, args.zone, self.resources,
        scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0]
    instance = self.GetInstance(instance_ref)

    ssh_args = [self.env.ssh]

    ssh_args.extend(ssh.GetDefaultFlags(self.keys.key_file))
    if args.serial_port_gateway == SERIAL_PORT_GATEWAY:
      ssh_args.extend(['-o', 'StrictHostKeyChecking=yes'])

    ssh_args.extend(['-p', CONNECTION_PORT])

    if args.port:
      port = 'port={0}'.format(args.port)
    else:
      port = 'port=1'

    constructed_username_list = [instance_ref.project, instance_ref.zone,
                                 instance_ref.Name(), user, port]

    if args.extra_args:
      for k, v in args.extra_args.items():
        constructed_username_list.append('{0}={1}'.format(k, v))

    ssh_args.append(ssh.UserHost('.'.join(constructed_username_list),
                                 args.serial_port_gateway))

    log.info('ssh command: {0}'.format(' '.join(ssh_args)))
    ip_address = ssh_utils.GetExternalIPAddress(instance)

    # Don't wait for the instance to become SSHable. We are not connecting to
    # the instance itself through SSH, so the instance doesn't need to have
    # fully booted to connect to the serial port. Also, ignore exit code 255,
    # since the normal way to terminate the serial port connection is ~. and
    # that causes ssh to exit with 255.
    return_code = self.ActuallyRun(
        args, ssh_args, user, instance, instance_ref.project, ip_address,
        strict_error_checking=False, use_account_service=False,
        wait_for_sshable=False, ignore_ssh_errors=True)
    if return_code:
      sys.exit(return_code)
示例#18
0
    def Run(self, args):
        super(SshGA, self).Run(args)

        parts = args.user_host.split('@')
        if len(parts) == 1:
            if self._use_accounts_service:  # Using Account Service.
                user = gaia.GetDefaultAccountName(self.http)
            else:  # Uploading keys through metadata.
                user = ssh.GetDefaultSshUsername(warn_on_account_user=True)
            instance = parts[0]
        elif len(parts) == 2:
            user, instance = parts
        else:
            raise exceptions.ToolException(
                'Expected argument of the form [USER@]INSTANCE; received [{0}].'
                .format(args.user_host))

        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            self.resources,
            scope_lister=flags.GetDefaultScopeLister(self.compute_client,
                                                     self.project))[0]
        instance = self.GetInstance(instance_ref)
        external_ip_address = ssh_utils.GetExternalIPAddress(instance)

        ssh_args = [self.ssh_executable]
        if not args.plain:
            ssh_args.extend(self.GetDefaultFlags())
            # Allocates a tty if no command was provided and a container was provided.
            if args.container and not args.command:
                ssh_args.append('-t')

        if args.ssh_flag:
            for flag in args.ssh_flag:
                for flag_part in flag.split():  # We want grouping here
                    dereferenced_flag = (flag_part.replace(
                        '%USER%', user).replace('%INSTANCE%',
                                                external_ip_address))
                    ssh_args.append(dereferenced_flag)

        host_key_alias = self.HostKeyAlias(instance)
        ssh_args.extend(self.GetHostKeyArgs(args, host_key_alias))

        ssh_args.append(ssh.UserHost(user, external_ip_address))

        if args.ssh_args:
            ssh_args.extend(args.ssh_args)
        if args.container:
            ssh_args.append('--')
            ssh_args.append('container_exec')
            ssh_args.append(args.container)
            # Runs the given command inside the given container if --command was
            # specified, otherwise runs /bin/sh.
            if args.command:
                ssh_args.append(args.command)
            else:
                ssh_args.append('/bin/sh')

        elif args.command:
            if not platforms.OperatingSystem.IsWindows():
                ssh_args.append('--')
            ssh_args.append(args.command)

        # Don't use strict error checking for ssh: if the executed command fails, we
        # don't want to consider it an error. We do, however, want to propagate its
        # return code.
        return_code = self.ActuallyRun(
            args,
            ssh_args,
            user,
            instance,
            instance_ref.project,
            strict_error_checking=False,
            use_account_service=self._use_accounts_service)
        if return_code:
            # Can't raise an exception because we don't want any "ERROR" message
            # printed; the output from `ssh` will be enough.
            sys.exit(return_code)
示例#19
0
 def CmdTraceroute(instance):
     return ['traceroute', ssh_utils.GetExternalIPAddress(instance)]
示例#20
0
    def Run(self, args):
        """See ssh_utils.BaseSSHCLICommand.Run."""
        super(SshGA, self).Run(args)
        user, instance_name = ssh_utils.GetUserAndInstance(
            args.user_host, self._use_account_service, self.http)
        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance_name],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            self.resources,
            scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0]
        instance = self.GetInstance(instance_ref)
        if self._use_internal_ip:
            ip_address = ssh_utils.GetInternalIPAddress(instance)
        else:
            ip_address = ssh_utils.GetExternalIPAddress(instance)

        remote = ssh.Remote(ip_address, user)

        identity_file = None
        options = None
        if not args.plain:
            identity_file = self.keys.key_file
            options = self.GetConfig(ssh_utils.HostKeyAlias(instance),
                                     args.strict_host_key_checking)

        extra_flags = []
        remainder = []

        if args.ssh_flag:
            for flag in args.ssh_flag:
                for flag_part in flag.split():  # We want grouping here
                    dereferenced_flag = (flag_part.replace(
                        '%USER%', remote.user).replace('%INSTANCE%',
                                                       ip_address))
                    extra_flags.append(dereferenced_flag)

        if args.ssh_args:
            remainder.extend(args.ssh_args)

        tty = ssh_utils.GetTty(args.container, args.command)
        remote_command = ssh_utils.GetRemoteCommand(args.container,
                                                    args.command)

        cmd = ssh.SSHCommand(remote,
                             identity_file=identity_file,
                             options=options,
                             extra_flags=extra_flags,
                             remote_command=remote_command,
                             tty=tty,
                             remainder=remainder)
        if args.dry_run:
            log.out.Print(' '.join(cmd.Build(self.env)))
            return

        if args.plain:
            keys_newly_added = False
        else:
            keys_newly_added = self.EnsureSSHKeyExists(
                remote.user,
                instance,
                instance_ref.project,
                use_account_service=self._use_account_service)

        if keys_newly_added:
            poller = ssh.SSHPoller(
                remote,
                identity_file=identity_file,
                options=options,
                extra_flags=extra_flags,
                max_wait_ms=ssh_utils.SSH_KEY_PROPAGATION_TIMEOUT_SEC)
            log.status.Print('Waiting for SSH key to propagate.')
            # TODO(b/35355795): Don't force_connect
            try:
                poller.Poll(self.env, force_connect=True)
            except retry.WaitException:
                raise ssh_utils.NetworkError()

        if self._use_internal_ip:
            self._PreliminarylyVerifyInstance(instance.id, remote,
                                              identity_file, options,
                                              extra_flags)

        return_code = cmd.Run(self.env, force_connect=True)
        if return_code:
            # Can't raise an exception because we don't want any "ERROR" message
            # printed; the output from `ssh` will be enough.
            sys.exit(return_code)
示例#21
0
    def RunScp(self,
               compute_holder,
               args,
               port=None,
               recursive=False,
               compress=False,
               extra_flags=None,
               release_track=None,
               ip_type=ip.IpTypeEnum.EXTERNAL):
        """SCP files between local and remote GCE instance.

    Run this method from subclasses' Run methods.

    Args:
      compute_holder: The ComputeApiHolder.
      args: argparse.Namespace, the args the command was invoked with.
      port: str or None, Port number to use for SSH connection.
      recursive: bool, Whether to use recursive copying using -R flag.
      compress: bool, Whether to use compression.
      extra_flags: [str] or None, extra flags to add to command invocation.
      release_track: obj, The current release track.
      ip_type: IpTypeEnum, Specify using internal ip or external ip address.

    Raises:
      ssh_utils.NetworkError: Network issue which likely is due to failure
        of SSH key propagation.
      ssh.CommandError: The SSH command exited with SSH exit code, which
        usually implies that a connection problem occurred.
    """
        if release_track is None:
            release_track = base.ReleaseTrack.GA
        super(BaseScpHelper, self).Run(args)

        dst = ssh.FileReference.FromPath(args.destination)
        srcs = [ssh.FileReference.FromPath(src) for src in args.sources]

        # Make sure we have a unique remote
        ssh.SCPCommand.Verify(srcs, dst, single_remote=True)

        remote = dst.remote or srcs[0].remote
        if not dst.remote:  # Make sure all remotes point to the same ref
            for src in srcs:
                src.remote = remote

        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [remote.host],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            compute_holder.resources,
            scope_lister=instance_flags.GetInstanceZoneScopeLister(
                compute_holder.client))[0]
        instance = self.GetInstance(compute_holder.client, instance_ref)
        project = self.GetProject(compute_holder.client, instance_ref.project)

        if not remote.user:
            remote.user = ssh.GetDefaultSshUsername(warn_on_account_user=True)
        if args.plain:
            use_oslogin = False
        else:
            public_key = self.keys.GetPublicKey().ToEntry(include_comment=True)
            remote.user, use_oslogin = ssh.CheckForOsloginAndGetUser(
                instance, project, remote.user, public_key, release_track)

        identity_file = None
        options = None
        if not args.plain:
            identity_file = self.keys.key_file
            options = self.GetConfig(ssh_utils.HostKeyAlias(instance),
                                     args.strict_host_key_checking)

        iap_tunnel_args = iap_tunnel.SshTunnelArgs.FromArgs(
            args, release_track, instance_ref,
            ssh_utils.GetInternalInterface(instance),
            ssh_utils.GetExternalInterface(instance, no_raise=True))

        if iap_tunnel_args:
            remote.host = ssh_utils.HostKeyAlias(instance)
        elif ip_type is ip.IpTypeEnum.INTERNAL:
            remote.host = ssh_utils.GetInternalIPAddress(instance)
        else:
            remote.host = ssh_utils.GetExternalIPAddress(instance)

        cmd = ssh.SCPCommand(srcs,
                             dst,
                             identity_file=identity_file,
                             options=options,
                             recursive=recursive,
                             compress=compress,
                             port=port,
                             extra_flags=extra_flags,
                             iap_tunnel_args=iap_tunnel_args)

        if args.dry_run:
            log.out.Print(' '.join(cmd.Build(self.env)))
            return

        if args.plain or use_oslogin:
            keys_newly_added = False
        else:
            keys_newly_added = self.EnsureSSHKeyExists(compute_holder.client,
                                                       remote.user, instance,
                                                       project)

        if keys_newly_added:
            poller = ssh_utils.CreateSSHPoller(remote,
                                               identity_file,
                                               options,
                                               iap_tunnel_args,
                                               port=port)

            log.status.Print('Waiting for SSH key to propagate.')
            # TODO(b/35355795): Don't force_connect
            try:
                poller.Poll(self.env, force_connect=True)
            except retry.WaitException:
                raise ssh_utils.NetworkError()

        if ip_type is ip.IpTypeEnum.INTERNAL:
            # This will never happen when IAP Tunnel is enabled, because ip_type is
            # always EXTERNAL when IAP Tunnel is enabled, even if the instance has no
            # external IP. IAP Tunnel doesn't need verification because it uses
            # unambiguous identifiers for the instance.
            self.PreliminarilyVerifyInstance(instance.id, remote,
                                             identity_file, options)

        # Errors from the SCP command result in an ssh.CommandError being raised
        cmd.Run(self.env, force_connect=True)
示例#22
0
    def Run(self, args):
        super(SshGA, self).Run(args)
        user, instance_name = ssh_utils.GetUserAndInstance(
            args.user_host, self._use_accounts_service, self.http)
        instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
            [instance_name],
            compute_scope.ScopeEnum.ZONE,
            args.zone,
            self.resources,
            scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0]
        instance = self.GetInstance(instance_ref)
        external_ip_address = ssh_utils.GetExternalIPAddress(instance)

        ssh_args = [self.env.ssh]
        if not args.plain:
            ssh_args.extend(ssh.GetDefaultFlags(self.keys.key_file))
            # Allocates a tty if no command was provided and a container was provided.
            if args.container and not args.command:
                ssh_args.append('-t')

        if args.ssh_flag:
            for flag in args.ssh_flag:
                for flag_part in flag.split():  # We want grouping here
                    dereferenced_flag = (flag_part.replace(
                        '%USER%', user).replace('%INSTANCE%',
                                                external_ip_address))
                    ssh_args.append(dereferenced_flag)

        host_key_alias = ssh_utils.HostKeyAlias(instance)
        ssh_args.extend(
            ssh.GetHostKeyArgs(host_key_alias, args.plain,
                               args.strict_host_key_checking))

        ssh_args.append(ssh.UserHost(user, external_ip_address))

        if args.ssh_args:
            ssh_args.extend(args.ssh_args)
        if args.container:
            ssh_args.append('--')
            ssh_args.append('container_exec')
            ssh_args.append(args.container)
            # Runs the given command inside the given container if --command was
            # specified, otherwise runs /bin/sh.
            if args.command:
                ssh_args.append(args.command)
            else:
                ssh_args.append('/bin/sh')

        elif args.command:
            if not platforms.OperatingSystem.IsWindows():
                ssh_args.append('--')
            ssh_args.append(args.command)

        # Don't use strict error checking for ssh: if the executed command fails, we
        # don't want to consider it an error. We do, however, want to propagate its
        # return code.
        return_code = self.ActuallyRun(
            args,
            ssh_args,
            user,
            instance,
            instance_ref.project,
            external_ip_address,
            strict_error_checking=False,
            use_account_service=self._use_accounts_service)
        if return_code:
            # Can't raise an exception because we don't want any "ERROR" message
            # printed; the output from `ssh` will be enough.
            sys.exit(return_code)
示例#23
0
  def Run(self, args, port=None, recursive=False, extra_flags=None):
    """SCP files between local and remote GCE instance.

    Run this method from subclasses' Run methods.

    Args:
      args: argparse.Namespace, the args the command was invoked with.
      port: str, int or None, Port number to use for SSH connection.
      recursive: bool, Whether to use recursive copying using -R flag.
      extra_flags: [str] or None, extra flags to add to command invocation.

    Raises:
      ssh_utils.NetworkError: Network issue which likely is due to failure
        of SSH key propagation.
      ssh.CommandError: The SSH command exited with SSH exit code, which
        usually implies that a connection problem occurred.
    """
    super(BaseScpCommand, self).Run(args)

    dst = ssh.FileReference.FromPath(args.destination)
    srcs = [ssh.FileReference.FromPath(src) for src in args.sources]

    # Make sure we have a unique remote
    ssh.SCPCommand.Verify(srcs, dst, single_remote=True)

    remote = dst.remote or srcs[0].remote
    if not dst.remote:  # Make sure all remotes point to the same ref
      for src in srcs:
        src.remote = remote

    instance_ref = instance_flags.SSH_INSTANCE_RESOLVER.ResolveResources(
        [remote.host], compute_scope.ScopeEnum.ZONE, args.zone, self.resources,
        scope_lister=flags.GetDefaultScopeLister(self.compute_client))[0]
    instance = self.GetInstance(instance_ref)

    # Now replace the instance name with the actual IP/hostname
    remote.host = ssh_utils.GetExternalIPAddress(instance)
    if not remote.user:
      remote.user = ssh.GetDefaultSshUsername(warn_on_account_user=True)

    identity_file = None
    options = None
    if not args.plain:
      identity_file = self.keys.key_file
      options = self.GetConfig(ssh_utils.HostKeyAlias(instance),
                               args.strict_host_key_checking)

    cmd = ssh.SCPCommand(
        srcs, dst, identity_file=identity_file, options=options,
        recursive=recursive, port=port, extra_flags=extra_flags)

    if args.dry_run:
      log.out.Print(' '.join(cmd.Build(self.env)))
      return

    if args.plain:
      keys_newly_added = False
    else:
      keys_newly_added = self.EnsureSSHKeyExists(
          remote.user, instance, instance_ref.project,
          use_account_service=self._use_account_service)

    if keys_newly_added:
      poller = ssh.SSHPoller(
          remote, identity_file=identity_file, options=options,
          max_wait_ms=ssh_utils.SSH_KEY_PROPAGATION_TIMEOUT_SEC)
      log.status.Print('Waiting for SSH key to propagate.')
      # TODO(b/35355795): Don't force_connect
      try:
        poller.Poll(self.env, force_connect=True)
      except retry.WaitException:
        raise ssh_utils.NetworkError()
    return_code = cmd.Run(self.env, force_connect=True)
    if return_code:
      # Can't raise an exception because we don't want any "ERROR" message
      # printed; the output from `ssh` will be enough.
      sys.exit(return_code)