
    դ                     j   S r SSKJr  SSKJr  SSKJr  SSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKJr  SSKJr  SSKJr  SS	KJr  SS
KJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSKJr  SSK J!r!  SSK J"r"  SSK#r#Sr$Sr%Sr&/ SQr' " S S\RP                  5      r) " S S\RP                  5      r* " S S\RP                  5      r+ " S  S!\RP                  5      r, " S" S#\RP                  5      r- " S$ S%\RP                  5      r. " S& S'\RP                  5      r/ " S( S)\RP                  5      r0 " S* S+\RP                  5      r1SBS, jr2SBS- jr3S. r4S/ r5S0 r6S1 r7S2 r8S3 r9S4 r:S5 r; SCS6 jr<S7 r= " S8 S9\>5      r?S: r@S; rA " S< S=\?5      rBS> rCS? rD SDS@ jrESA rFg)Ea  Utilities for subcommands that need to SSH into virtual machine guests.

This module provides the following things:
  Errors used by various SSH-based commands.
  Various helper functions.
  BaseSSHHelper: The primary purpose of the BaseSSHHelper class is to
      get the instance and project information, determine whether the user's
      SSH public key is in the metadata, determine if the SSH public key
      needs to be added to the instance/project metadata, and then add the
      key if necessary.
  BaseSSHCLIHelper: An additional wrapper around BaseSSHHelper that adds
      common flags needed by the various SSH-based commands.
    )absolute_import)division)unicode_literalsN)	constants)metadata_utils)path_simplifier)utils)actions)arg_parsers)
exceptions)ssh)log)
properties)
console_io)progress_tracker)encoding)times)
FileReader)
FileWriteri`  zIhttps://cloud.google.com/compute/docs/troubleshooting/troubleshooting-sshzenable-guest-attributes)zssh-rsazssh-ed25519zecdsa-sha2-nistp256c                       \ rS rSrSrSrg)UnallocatedIPAddressErrorF   zYAn exception to be raised when a network interface's IP address is yet

to be allocated.
 N__name__
__module____qualname____firstlineno____doc____static_attributes__r       3lib/googlecloudsdk/command_lib/compute/ssh_utils.pyr   r   F       r!   r   c                       \ rS rSrSrSrg)MissingExternalIPAddressErrorM   zZAn exception to be raised when a network interface does not have an

external IP address.
r   Nr   r   r!   r"   r%   r%   M   r#   r!   r%   c                       \ rS rSrSrSrg)MissingNetworkInterfaceErrorT   z Network interface was not found.r   Nr   r   r!   r"   r(   r(   T   s    (r!   r(   c                   0   ^  \ rS rSrSrSU 4S jjrSrU =r$ )CommandErrorX   zAWraps ssh.CommandError, primarly for adding troubleshooting info.c                    > Uc  SR                  [        S9n[        [        U ]  SR                  X5      UR
                  S9  g )Nz$See {url} for troubleshooting hints.)urlz{0}
{1})	exit_code)format_TROUBLESHOOTING_URLsuperr+   __init__r/   )selforiginal_errormessage	__class__s      r"   r3   CommandError.__init__[   sM    6==" > $g 
,&.2 ** ' ,r!   r   )Nr   r   r   r   r   r3   r    __classcell__r7   s   @r"   r+   r+   X   s    I, ,r!   r+   c                       \ rS rSrSrSrg)ArgumentErrore   z1Invalid combinations of, or malformed, arguments.r   Nr   r   r!   r"   r=   r=   e   s    9r!   r=   c                       \ rS rSrSrg)SetProjectMetadataErrorj   r   Nr   r   r   r   r    r   r!   r"   r@   r@   j       r!   r@   c                       \ rS rSrSrg)SecurityKeysNotSupportedErrorn   r   NrB   r   r!   r"   rE   rE   n   rC   r!   rE   c                       \ rS rSrSrg)SecurityKeysNotPresentErrorr   r   NrB   r   r!   r"   rH   rH   r   rC   r!   rH   c                   ,   ^  \ rS rSrSrU 4S jrSrU =r$ )NetworkErrorv   zCIndicates that an SSH connection couldn't be established right now.c                 ,   > [         [        U ]  S5        g )NzCould not SSH into the instance.  It is possible that your SSH key has not propagated to the instance yet. Try running this command again.  If you still cannot connect, verify that the firewall and instance are set to accept ssh traffic.)r2   rK   r3   )r4   r7   s    r"   r3   NetworkError.__init__y   s    	,&	23r!   r   r9   r;   s   @r"   rK   rK   v   s    K3 3r!   rK   c           	      H   SnU R                   (       a  U R                    H  nUR                  nUR                  nU(       a!  US   R                  (       a  Us  $ U(       d  SnU(       a!  US   R                  (       a  Us  $ U(       d  SnU(       d  Mt  [        SR                  U R                  [        R                  " U R                  5      5      5      e   U(       a  g[        SR                  U R                  [        R                  " U R                  5      5      5      e)aU  Returns the network interface of the instance with an external IP address.

Args:
  instance_resource: An instance resource object.
  no_raise: A boolean flag indicating whether or not to return None instead of
    raising.

Raises:
  UnallocatedIPAddressError: If the instance_resource's external IP address
    has yet to be allocated.
  MissingExternalIPAddressError: If no external IP address is found for the
    instance_resource and no_raise is False.

Returns:
  A network interface resource object or None if no_raise and a network
  interface with an external IP address does not exist.
Fr   TzqInstance [{0}] in zone [{1}] has not been allocated an external IP address yet. Try rerunning this command later.NzInstance [{0}] in zone [{1}] does not have an external IP address, so you cannot SSH into it. To add an external IP address to the instance, use [gcloud compute instances add-access-config].)networkInterfacesaccessConfigsipv6AccessConfigsnatIPexternalIpv6r   r0   namer   Namezoner%   )instance_resourceno_raiseno_ipnetwork_interfaceaccess_configsipv6_access_configss         r"   GetExternalInterfacer^      s    $ %((.@@(66n-??	!"""
"%	q!.."
"%	'@@F!&&$$%6%;%;<A>? 	? A( %DDJF

 
 /"6"67H7M7M"NEP	Q Qr!   c                    [        XS9nU(       ax  [        US5      (       a*  UR                  (       a  UR                  S   R                  $ [        US5      (       a+  UR                  (       a  UR                  S   R
                  $ ggg)a>  Returns the external IP address of the instance.

Args:
  instance_resource: An instance resource object.
  no_raise: A boolean flag indicating whether or not to return None instead of
    raising.

Raises:
  UnallocatedIPAddressError: If the instance_resource's external IP address
    has yet to be allocated.
  MissingExternalIPAddressError: If no external IP address is found for the
    instance_resource and no_raise is False.

Returns:
  A string IPv4 address or IPv6 address if the IPv4 address does not exit
  or None if no_raise is True and no external IP exists.
)rY   rQ   r   rR   N)r^   hasattrrQ   rS   rR   rT   )rX   rY   r[   s      r"   GetExternalIPAddressra      s    $ ++<P!?33++,,Q/555
#%8
9
911003@@@ 2 :	 r!   c                     U R                   (       a  U R                   S   $ [        SR                  U R                  [        R
                  " U R                  5      5      5      e)zReturns the a network interface of the instance.

Args:
  instance_resource: An instance resource object.

Raises:
  MissingNetworkInterfaceError: If instance has no network interfaces.

Returns:
  A network interface resource object.
r   z7Instance [{0}] in zone [{1}] has no network interfaces.)rP   r(   r0   rU   r   rV   rW   )rX   s    r"   GetInternalInterfacerc      sW     ((..q11$?FF

 
 


055
68	9 9r!   c                 V    [        U 5      nUR                  =(       d    UR                  $ )zReturns the internal IP address of the instance.

Args:
  instance_resource: An instance resource object.

Raises:
  ToolException: If instance has no network interfaces.

Returns:
  A string IPv4 address or IPv6 address if there is no IPv4 address.
)rc   	networkIPipv6Address)rX   	interfaces     r"   GetInternalIPAddressrh      s&     ##45)				5	 5 55r!   c                    U R                   (       a  U R                   nOGU R                  (       a5  [        R                  " 5       [        R
                  " U R                  S9-   nOg[        R                  " U5      S-  nU[        U5      4$ )z?Converts flags to an ssh key expiration in datetime and micros.)secondsNNg    .A)ssh_key_expirationssh_key_expire_afterr   Nowdatetime	timedeltaGetTimeStampFromDateTimeint)args
expirationexpiration_micross      r"   GetSSHKeyExpirationFromArgsrv      ss    	 ((J  x11)) + +J 44Z@3F	S*+	++r!   c                     / nU R                  S5       H-  nUR                  5       nU(       d  M  UR                  U5        M/     U$ )zFReturns a list of SSH keys (without whitespace) from a metadata entry.
)splitstripappend)metadata_entrykeysline
line_strips       r"   _GetSSHKeyListFromMetadataEntryr     s?    	$""4(dJz
kk* ) 
+r!   c                    / n/ nU (       d  X4$ U R                    Hm  nUR                  [        R                  :X  a  [	        UR
                  5      nM8  UR                  [        R                  :X  d  MX  [	        UR
                  5      nMo     X4$ )am  Returns the ssh-keys and legacy sshKeys metadata values.

This function will return all of the SSH keys in metadata, stored in
the default metadata entry ('ssh-keys') and the legacy entry ('sshKeys').

Args:
  metadata: An instance or project metadata object.

Returns:
  A pair of lists containing the SSH public keys in the default and
  legacy metadata entries.
)itemskeyr   SSH_KEYS_METADATA_KEYr   valueSSH_KEYS_LEGACY_METADATA_KEY)metadatassh_keysssh_legacy_keysitems       r"   _GetSSHKeysFromMetadatar     ss     (/	$$nndxx92220<h	Y;;	;7

Co	  
	""r!   c                     U (       a  U R                   (       d  gU R                    Vs/ s H%  nUR                  [        :X  d  M  UR                  PM'     nnU(       d  gUS   R	                  5       S:H  $ s  snf )zReturns true if the metadata has 'enable-guest-attributes' set to 'true'.

Args:
  metadata: Instance or Project metadata

Returns:
  True if Enabled, False if Disabled, None if key is not present.
Nr   true)r   r   GUEST_ATTRIBUTES_METADATA_KEYr   lowerr   r   matching_valuess      r"   "_MetadataHasGuestAttributesEnabledr   )  sk     x~~,4NN CND$AA  TZZN/ C 
			!	!	#v	--Cs   A4A4c                 X   U R                  5       n[        U5      S:  d	  US   S:w  a  gSR                  USS 5      n[        R                  " U5      n [
        R                  " US   5      n[
        R                  " U[
        R                  5      $ ! [         a    [        S5      ef = f)	a  Returns a datetime expiration time for an ssh key entry from metadata.

Args:
  ssh_key: A single ssh key entry.

Returns:
  None if no expiration set or a datetime object of the expiration (in UTC).

Raises:
  ValueError: If the ssh key entry could not be parsed for expiration (invalid
    format, missing expected entries, etc).
  dateutil.DateTimeSyntaxError: The found expiration could not be parsed.
  dateutil.DateTimeValueError: The found expiration could not be parsed.
      z
google-sshN    expireOnzUnable to find expireOn entry)ry   lenjoinjsonloadsr   ParseDateTimeKeyError
ValueErrorLocalizeDateTimeUTC)ssh_key	key_partsexpiration_jsonrt   expireons        r"   _SSHKeyExpirationr   <  s    $ mmo)^a9Q<<7HHYqr]+/zz/**6"":j#9:H 
		%))	44 
 6
4
556s   B B)c                    / nSn[         R                  " [         R                  " 5       [         R                  5      n[	        U 5       H  n [        U5      nUSL=(       a    XS:  nU(       a  M'   [        US-   5      nX(-   [        R                  :  a9  Sn	U	R                  [        R                  U5      n	[        R                   " U	SS9  M  UR#                  U5        X(-  nM     UR%                  5         SR'                  U5      $ ! [        [         R                  [         R                  4 a/  n[        R                  " SR                  XG5      5         SnANSnAff = f)a  Returns a string appropriate for the metadata.

Expired SSH keys are always removed.
Then Values are taken from the tail until either all values are taken or
_MAX_METADATA_VALUE_SIZE_IN_BYTES is reached, whichever comes first. The
selected values are then reversed. Only values at the head of the list will be
subject to removal.

Args:
  ssh_keys: A list of keys. Each entry should be one key.

Returns:
  A new-line-joined string of SSH keys.
r   Nz8Treating {0!r} as unexpiring, since unable to parse: {1}rx   zThe following SSH key will be removed from your project because your SSH keys metadata value has reached its maximum allowed size of {0} bytes: {1}T)r6   cancel_on_no)r   r   rn   r   reversedr   r   DateTimeSyntaxErrorDateTimeValueErrorr   warningr0   r   r    MAX_METADATA_VALUE_SIZE_IN_BYTESr   PromptContinuer{   reverser   )
r   r}   bytes_consumednowr   rt   expiredexc	num_bytesprompt_messages
             r"   _PrepareSSHKeysValuer   Z  s3    
$.uyy{EII6#hc
$S)j$&;:+;g	 
 C$JI!I$N$NNMn &,,

4
4c;nTJ
kk#!n/  2 ,,.	4) 11$$&  
kk
D
K
K s   !D)E0%EEc                 z   Uc  SR                  XR                  SS9S9nO[        R                  " US[        R                  5      nSR                  XR                  SS9[
        R                  " [        R                  " SU4S	U4/5      5      R                  S
S5      S9n[        U5      u  pX-   n
[        R                  " SR                  U
5      5        Xj;   a  U$ U(       a  [        R                  nU	nO[        R                  nUnUR!                  U5        ["        R$                  " U U['        U5      0US9$ )a  Adds the public key material to the metadata if it's not already there.

Args:
  message_classes: An object containing API message classes.
  user: The username for the SSH key.
  public_key: The SSH public key to add to the metadata.
  metadata: The existing metadata.
  expiration: If provided, a datetime after which the key is no longer valid.
  legacy: If true, store the key in the legacy "sshKeys" metadata entry.

Returns:
  An updated metadata API message.
z{user}:{public_key}T)include_comment)user
public_keyz%Y-%m-%dT%H:%M:%S+0000z){user}:{public_key} google-ssh {jsondict}FuserNamer   r    )r   r   jsondictz Current SSH keys in project: {0})message_classesr   existing_metadata)r0   ToEntryr   FormatDateTimer   r   dumpscollectionsOrderedDictreplacer   r   debugr   r   r   r{   r   ConstructMetadataMessager   )r   r   r   r   rt   legacyentry	expire_onr   r   all_ssh_keysmetadata_keyupdated_ssh_keyss                r"   _AddSSHKeyToMetadataMessager     sF    !((000F ) HE
 $$Z1I%*YY0I7>>000G
 K33#5% & ''.wsB'7 ? 9E 6h?(+,)).55lCD
O99L&22L% 		0	0%23CDE 
" "r!   c                    U (       a  U R                   (       d  gU R                    Vs/ s H/  nUR                  [        R                  :X  d  M#  UR                  PM1     nnU(       d  gUS   R                  5       S:H  $ s  snf )zHReturn true if the metadata has 'block-project-ssh-keys' set and 'true'.Fr   r   )r   r   r   SSH_KEYS_BLOCK_METADATA_KEYr   r   r   s      r"   _MetadataHasBlockProjectSshKeysr     sl    
x~~,4NN KNDI$I$II  TZZN/ K				!	!	#v	--	Ks   "A>A>c                       \ rS rSrSrSr\S 5       rS rS r	S r
 SS jrS	 rS
 rS rS rS r SS jr  SS jrS r  SS jrSrg)BaseSSHHelperi  aX  Helper class for subcommands that need to connect to instances using SSH.

Clients can call EnsureSSHKeyIsInProject() to make sure that the
user's public SSH key is placed in the project metadata before
proceeding.

Attributes:
  keys: ssh.Keys, the public/private key pair.
  env: ssh.Environment, the current environment, used by subclasses.
Nc                     U R                  SSSSS9  U R                  SSR                  [        R                  R                  5      S9  g)	ah  Args is called by calliope to gather arguments for this command.

Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
Args:
  parser: An argparse parser that you can use to add arguments that go
      on the command line after this command. Positional arguments are
      allowed.
z--force-key-file-overwrite
store_trueNa          If enabled, the gcloud command-line tool will regenerate and overwrite
        the files associated with a broken SSH key without asking for
        confirmation in both interactive and non-interactive environments.

        If disabled, the files associated with a broken SSH key will not be
        regenerated and will fail in both interactive and non-interactive
        environments.)actiondefaulthelpz--ssh-key-filezK        The path to the SSH key file. By default, this is ``{0}''.
        )r   )add_argumentr0   r   KeysDEFAULT_KEY_FILEparsers    r"   ArgsBaseSSHHelper.Args  sV     $	   F388,,-	  /r!   c                     [         R                  R                  UR                  5      U l        [         R
                  R                  5       U l        U R                  R                  5         g)zSets up resources to be used by concrete subclasses.

Subclasses must call this in their Run() before continuing.

Args:
  args: argparse.Namespace, arguments that this command was invoked with.

Raises:
  ssh.CommandNotFoundError: SSH is not supported.
N)	r   r   FromFilenamessh_key_filer}   EnvironmentCurrentenv
RequireSSH)r4   rs   s     r"   RunBaseSSHHelper.Run  sD     %%d&7&78DI&&(DHHHr!   c                     UR                   R                  SUR                  R                  UR	                  5       UR
                  UR                  S94nUR                  U/5      S   $ )z2Fetch an instance based on the given instance_ref.Get)instanceprojectrW   r   )apitools_client	instancesmessagesComputeInstancesGetRequestrV   r   rW   MakeRequests)r4   clientinstance_refrequests       r"   GetInstanceBaseSSHHelper.GetInstance	  sl     	((22!&&( (("" 	3 	
G y)!,,r!   c           
          UR                  UR                  R                  SUR                  R	                  U=(       d1    [
        R                  R                  R                  R                  SS9S94/5      S   $ )zReturns the project object.

Args:
  client: The compute client.
  project: str, the project we are requesting or None for value from
    from properties

Returns:
  The project object
r   Trequired)r   r   )
r   r   projectsr   ComputeProjectsGetRequestr   VALUEScorer   r   )r4   r   r   s      r"   
GetProjectBaseSSHHelper.GetProject  s     

 
 
)
)5
//
3
3 @$$,,00$0? 4 B
C 	DE FG	H Hr!   c           	         U(       a>  U(       a7  [        UR                  5      nUc  UR                  n[        U5      nU(       d  gUR                  R                  SUR
                  R                  UR                  5       UR                  SUR                  S94/n UR                  U5      S   n0 n
Ub  UR                  R                    H  nUR"                  S:X  d  M  UR$                  [&        ;   d  M+  UR(                  R+                  5       S   n [,        R.                  " U5      n[0        R2                  " [,        R4                  " U5      5      nX:X  d  M  XUR$                  '   M     U
$ ! [        R                   a(  n	S[        R                  " U	5      ;   a  Sn Sn	A	NU	eSn	A	ff = f! [6        [8        R:                  4 a    Sn Nxf = f)	ai  Get host keys from guest attributes.

Args:
  client: The compute client.
  instance_ref: The instance object.
  instance: The object representing the instance we are connecting to. If
    either project or instance is None, metadata won't be checked to
    determine if Guest Attributes are enabled.
  project: The object representing the current project. If either project
    or instance is None, metadata won't be checked to determine if
    Guest Attributes are enabled.

Returns:
  A dictionary of host keys, with the type as the key and the host key
  as the value, or None if Guest Attributes is not enabled.
NGetGuestAttributesz	hostkeys/)r   r   	queryPathrW   r   zAThe resource 'hostkeys/' of type 'Guest Attribute' was not found.hostkeysr   )r   r   commonInstanceMetadatar   r   r   )ComputeInstancesGetGuestAttributesRequestrV   r   rW   r   r   ToolExceptionsix	text_type
queryValuer   	namespacer   SUPPORTED_HOSTKEY_TYPESr   ry   base64	b64decoder   Decode	b64encode	TypeErrorbinasciiError)r4   r   r   r   r   guest_attributes_enabledproject_metadatarequestsr  ehostkey_dictr   	key_valuedecoded_keyencoded_keys                  r"   GetHostKeysFromGuestAttributes,BaseSSHHelper.GetHostKeysFromGuestAttributes(  s   $ G!C


"	!	)"99#E$ %''11%JJ*//1)11*&++	 K -. /H$$X.q1h L%%++$NNj(33 jj&&(+) **95K"//&*:*:;*GHK %%." ," 7 ## }}Q'
(* X^^, Ks1   E0 A F/0F,F'%F''F,/GGc                    / nUR                  5        H'  u  pVSR                  XV5      nUR                  U5        M)     UR                  5         UR	                  X4SS9nU(       aB  [
        R                  R                  SR                  [        U5      UR                  5      5        U(       a?  U(       d8  [
        R                  R                  SR                  UR                  5      5        UR                  5         g)a  Writes host keys to known hosts file.

Only writes keys to known hosts file if there are no existing keys for
the host.

Args:
  known_hosts: obj, known_hosts file object.
  host_keys: dict, dictionary of host keys.
  host_key_alias: str, alias for host key entries.
z{0} {1}F)	overwritezWriting {0} keys to {1}zExisting host keys found in {0}N)r   r0   r{   sortAddMultipler   statusPrintr   	file_pathWrite)	r4   known_hosts	host_keyshost_key_aliashost_key_entrieskey_typer   host_key_entrynew_keys_addeds	            r"   WriteHostKeysToKnownHosts'BaseSSHHelper.WriteHostKeysToKnownHostsl  s     "* ''6nn- +  ,,E - ;N	jj0s#34k6K6KLN	jj8{4457r!   c                 *   / nUR                  UR                  R                  SUR                  R	                  U[
        R                  R                  R                  R                  SS9S94/US9  U(       a  [        R                  " U[        SS9  gg)	z.Sets the project metadata to the new metadata.SetCommonInstanceMetadataTr   )r   r   r  errors_to_collectz*Could not add SSH key to project metadata:error_messageN)r   r   r   r   /ComputeProjectsSetCommonInstanceMetadataRequestr   r   r   r   r   r	   RaiseExceptionr@   )r4   r   new_metadataerrorss       r"   _SetProjectMetadata!BaseSSHHelper._SetProjectMetadata  s    F
##,,(__LL&#**//77;;" < $ M  !  	" 

!DF r!   c                     [         R                  " S5         U R                  X5        SSS5        g! , (       d  f       g= f)zDSets the project metadata to the new metadata with progress tracker.zUpdating project ssh metadataN)r   ProgressTrackerr8  )r4   r   r6  s      r"   SetProjectMetadata BaseSSHHelper.SetProjectMetadata  s,    		)	)*I	J
v4 
K	J	Js	   2
A c                 t   / nUR                   R                  S5      S   nUR                  UR                  R                  SUR
                  R                  UR                  U[        R                  R                  R                  R                  SS9US94/US9  U(       a  [        R                  " USS	9  g
g
)z/Sets the instance metadata to the new metadata./SetMetadataTr   )r   r   r   rW   r0  zCould not add SSH key to instance metadata, refer https://cloud.google.com/compute/docs/access#granting_users_ssh_access_to_vm_instances for granting users SSH access to VM instances:r2  N)rW   ry   r   r   r   r   "ComputeInstancesSetMetadataRequestrU   r   r   r   r   r   r	   RaiseToolException)r4   r   r   r6  r7  rW   s         r"   _SetInstanceMetadata"BaseSSHHelper._SetInstanceMetadata  s    F==s#B'D
##--__??"--&#**//77;;" < $ @ 	 !  " 
@ r!   c                     [         R                  " S5         U R                  XU5        SSS5        g! , (       d  f       g= f)zESets the instance metadata to the new metadata with progress tracker.zUpdating instance ssh metadataN)r   r;  rD  )r4   r   r   r6  s       r"   SetInstanceMetadata!BaseSSHHelper.SetInstanceMetadata  s.    		)	)*J	K
,? 
L	K	Ks	   3
Ac           	          U R                   R                  5       n[        UR                  X&UR                  XES9nXsR                  :g  nU(       a  U R                  XU5        U$ )aA  Ensures that the user's public SSH key is in the instance metadata.

Args:
  client: The compute client.
  user: str, the name of the user associated with the SSH key in the
      metadata
  instance: Instance, ensure the SSH key is in the metadata of this instance
  expiration: datetime, If not None, the point after which the key is no
      longer valid.
  legacy: If the key is not present in metadata, add it to the legacy
      metadata entry instead of the default entry.

Returns:
  bool, True if the key was newly added, False if it was in the metadata
      already
)rt   r   )r}   GetPublicKeyr   r   r   rG  )	r4   r   r   r   rt   r   r   r6  has_new_metadatas	            r"   EnsureSSHKeyIsInInstance&BaseSSHHelper.EnsureSSHKeyIsInInstance  s^    $ '')J.8+<+<.L $'8'88
v>r!   c                     U R                   R                  5       nU(       d  U R                  US5      nUR                  n[	        UR
                  X%UUS9nXv:w  a  U R                  X5        gg)a  Ensures that the user's public SSH key is in the project metadata.

Args:
  client: The compute client.
  user: str, the name of the user associated with the SSH key in the
      metadata
  project: Project, the project SSH key will be added to
  expiration: datetime, If not None, the point after which the key is no
      longer valid.

Returns:
  bool, True if the key was newly added, False if it was in the metadata
      already
N)rt   TF)r}   rJ  r   r  r   r   r<  )r4   r   r   r   rt   r   r   r6  s           r"   EnsureSSHKeyIsInProject%BaseSSHHelper.EnsureSSHKeyIsInProject  sj      '')J-g66.+<L (
f3r!   c                    [        UR                  5      u  pgU(       a  U R                  XX5SS9nU$ [        UR                  5      (       a  U R                  XX55      nU$  U R	                  XXE5      nU$ ! [
         aA    [        R                  " SSS9  [        R                  " S5        U R                  XX55      n U$ f = f)a  Controller for EnsureSSHKey* variants.

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

Args:
  compute_client: The compute client.
  user: str, The user name.
  instance: Instance, the instance to connect to.
  project: Project, the project instance is in.
  expiration: datetime, If not None, the point after which the key is no
      longer valid.


Returns:
  bool, True if the key was newly added.
T)r   zCould not set project metadata:)exc_infoz$Attempting to set instance metadata.)r   r   rL  r   rO  r@   r   info)	r4   compute_clientr   r   r   rt   _r   keys_newly_addeds	            r"   EnsureSSHKeyExists BaseSSHHelper.EnsureSSHKeyExists  s    ^ 11B1BCA
 66
T 7 C0 - 
)):):	;	; 66
6& 877'7  % 
82TB 	7888(8
8s   !A5 5AC ?C c                    0 n[         R                  R                  5       nUR                  US'   SUS'   SUS'   U(       d"  UR	                  U5      (       d  U(       a  SnOSnU(       a  U R                  XSU5        X$S'   XS'   SUS'   U$ )	a  Returns a dict of default `ssh-config(5)` options on the OpenSSH format.

Args:
  host_key_alias: str, Alias of the host key in the known_hosts file.
  strict_host_key_checking: str or None, whether to enforce strict host key
    checking. If None, it will be determined by existence of host_key_alias
    in the known hosts file. Accepted strings are 'yes', 'ask' and 'no'.
  host_keys_to_add: dict, A dictionary of host keys to add to the known
    hosts file.

Returns:
  Dict with OpenSSH options.
UserKnownHostsFileyesIdentitiesOnlynoCheckHostIPStrictHostKeyCheckingHostKeyAliasHashKnownHosts)r   
KnownHostsFromDefaultFiler#  ContainsAliasr,  )r4   r'  strict_host_key_checkinghost_keys_to_addconfigr%  s         r"   	GetConfigBaseSSHHelper.GetConfigE  s     F..002K#.#8#8F $F F=#		"	">	2	26F#( #' 
$$
9 '?"#+>#FMr!   )r   r}   rk   F)r   r   r   r   r   r}   staticmethodr   r   r   r   r  r,  r8  r<  rD  rG  rL  rO  rW  rh  r    r   r!   r"   r   r     s    	 
$/ /> -H$ =ABH4F&5
6@ ',6 ;?)-:M^ @D!%#r!   r   c                     U R                  S[        R                  " [        R                  R
                  R                  5      SSS9  g )Nz--verify-internal-ipTa  Whether or not `gcloud` should perform an initial SSH connection to verify an instance ID is correct when connecting via its internal IP. Without this check, `gcloud` will simply connect to the internal IP of the desired instance, which may be wrong if the desired instance is in a different subnet but happens to share the same internal IP as an instance in the current subnet. Defaults to True.)r   hiddenr   )r   r
   StoreBooleanPropertyr   r   r   verify_internal_ipr   s    r"   AddVerifyInternalIpArgrp  k  sC    ))





2
24=  
>r!   c                     U R                  5       nS nUR                  SUSS9  UR                  S[        R                  " SS9SS9  g	)
z-Additional flags to handle expiring SSH keys.c                     [         R                  R                  U 5      nU[        R                  " 5       :  a%  [         R
                  " SR                  U 5      5      eU$ )z4Parses a string value into a future Datetime object.z$Date/time must be in the future: {0})r   DatetimeParser   rn   ArgumentTypeErrorr0   )sdts     r"   ParseFutureDatetime4AddSSHKeyExpirationArgs.<locals>.ParseFutureDatetime}  sL    				#	#A	&B	EIIK))
0
7
7
:< <Ir!   z--ssh-key-expirationz        The time when the ssh key will be valid until, such as
        "2017-08-29T18:52:51.142Z." This is only valid if the instance is not
        using OS Login. See $ gcloud topic datetimes for information on time
        formats.
        )typer   z--ssh-key-expire-after1s)lower_boundz        The maximum length of time an SSH key is valid for once created and
        installed, e.g. 2m for 2 minutes. See $ gcloud topic datetimes for
        information on duration formats.
      N)add_mutually_exclusive_groupr   r   Duration)r   grouprx  s      r"   AddSSHKeyExpirationArgsr  y  s`    

-
-
/%    D1
  r!   c                   N   ^  \ rS rSrSr\U 4S j5       rU 4S jr SS jrSr	U =r
$ )BaseSSHCLIHelperi  z1Helper class for subcommands that use ssh or scp.c                    > [         [        [        ]  U 5        U R                  SSSS9  U R                  SSSS9  U R                  S/ SQS	S
9  [	        U 5        g)ai  Args is called by calliope to gather arguments for this command.

Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.

Args:
  parser: An argparse parser that you can use to add arguments that go
      on the command line after this command. Positional arguments are
      allowed.
z	--dry-runr   zZPrint the equivalent scp/ssh command that would be run to stdout, instead of executing it.)r   r   z--plainz        Suppress the automatic addition of *ssh(1)*/*scp(1)* flags. This flag
        is useful if you want to take care of authentication yourself or
        use specific ssh/scp features.
        z--strict-host-key-checking)r[  r]  aska          Override the default behavior of StrictHostKeyChecking for the
        connection. By default, StrictHostKeyChecking is set to 'no' the first
        time you connect to an instance, and will be set to 'yes' for all
        subsequent connections.
        )choicesr   N)r2   r  r   r   r  )r   r7   s    r"   r   BaseSSHCLIHelper.Args  s~     

,26:
1  3    $$   F#r!   c                    > [         [        U ]  U5        UR                  (       d%  U R                  R                  UR                  SS9  g g )NT)allow_passphrase)r2   r  r   plainr}   EnsureKeysExistforce_key_file_overwrite)r4   rs   r7   s     r"   r   BaseSSHCLIHelper.Run  sA    	
D%d+::
ii = =15   7 r!   c                    UR                  S5      S:X  a  [        R                  " S5        g[        R                  R
                  R                  R                  5       (       d0  [        R                  " SR                  UR                  5      5        gSnSR                  Xa5      /n[
        R                  " X#XGS9n[        [        R                  5      n	[        [        R                  5      n
[        [        R                  5      nUR!                  U R"                  UU
UU	S	9nUS
:X  a  gUS:X  a/  [$        R&                  " SR                  UR                  5      5      e[
        R(                  " XS9e)aT  Verify the instance's identity by connecting and running a command.

Args:
  instance_id: str, id of the compute instance.
  remote: ssh.Remote, remote to connect to.
  identity_file: str, optional key file.
  options: dict, optional ssh options.
  putty_force_connect: bool, whether to inject 'y' into the prompts for
    `plink`, which is insecure and not recommended. It serves legacy
    compatibility purposes for existing usages only; DO NOT SET THIS IN NEW
    CODE.

Raises:
  ssh.CommandError: The ssh command failed.
  core_exceptions.NetworkIssueError: The instance id does not match.
r_  r[  zGSkipping internal IP verification in favor of strict host key checking.NzSkipping internal IP verification connection and connecting to [{}] in the current subnet. This may be the wrong host if the instance is in a different subnet!z>http://metadata.google.internal/computeMetadata/v1/instance/idz?[ `curl "{}" -H "Metadata-Flavor: Google" -q` = {} ] || exit 23)identity_fileoptionsremote_command)putty_force_connectexplicit_output_fileexplicit_error_fileexplicit_input_filer      zQEstablished connection with host {} but was unable to confirm ID of the instance.)return_code)getr   r   r   r   r   ro  GetBoolr   r0   host
SSHCommandr   osdevnullr   r   r   core_exceptionsNetworkIssueErrorr+   )r4   instance_idremoter  r  r  metadata_id_urlr  cmdnull_innull_outnull_errr  s                r"   PreliminarilyVerifyInstance,BaseSSHCLIHelper.PreliminarilyVerifyInstance  sG   $ {{*+u4	ii   !  33;;==	kk&&,fV[[&9;  	I 
 	J		-/N ..!(IC $G"**%H"**%H''/%$#  %K a		--((.v{{(;= = 

3
88r!   r   rj  )r   r   r   r   r   rk  r   r   r  r    r:   r;   s   @r"   r  r    s/    9&$ &$P7 @E89 89r!   r  c                 8    SR                  U R                  5      $ )Nzcompute.{0})r0   id)r   s    r"   r`  r`    s    			hkk	**r!   c                     U R                  S5      n[        U5      S:X  a  [        R                  " SS9nUS   nX#4$ [        U5      S:X  a  U$ [	        SR                  U 5      5      e)z6Returns pair consiting of user name and instance name.@   T)warn_on_account_userr   r   z>Expected argument of the form [USER@]INSTANCE; received [{0}].)ry   r   r   GetDefaultSshUsernamer=   r0   )	user_hostpartsr   r   s       r"   GetUserAndInstancer  	  sf    
//#
%Z1_$$$?DQxH>Z1_LFvi	 r!   c                     U UUUU[         S.nU(       a  [        R                  " U5      US'   [        R                  " S0 UD6$ )z"Creates and returns an SSH poller.)r  r  r  iap_tunnel_argsextra_flagsmax_wait_msportr   )SSH_KEY_PROPAGATION_TIMEOUT_MSr  r  r   	SSHPoller)r  r  r  r  r  r  ssh_poller_argss          r"   CreateSSHPollerr    sH      &&3 '(7$/$BD/ 
!mmD1OF		)	))r!   c                    U R                   (       d  gU R                  (       d  [        S5      eU R                  (       a  gU R                  c  [        R
                  " S5        gU R                  S:X  a  [        S5      e[        S5      e)a  Check the OS Login security key state and take approprate action.

If OS Login security keys are not enabled, continue.
When security keys are enabled:
  - if no security keys are configured in the user's account, show an error.
  - if the local SSH client doesn't support them, show an error.
  - if the user is using Putty, show an error.
  - if we cannnot determine if the local client supports security keys, show
    a warning and continue.

Args:
  oslogin_state: An OsloginState object.

Raises:
  SecurityKeysNotPresentError: If no security keys are registered in the
    user's account.
  SecurityKeysNotSupportedError: If the user's SSH client does not support
    security keys.

Returns:
  None if no errors are raised.
NzeInstance requires security key for connection, but no security keys are registered in Google account.zInstance requires security key for connection, but cannot determine if the SSH client supports security keys. The connection may fail.puttyzuInstance requires security key for connection, but security keys are not supported on Windows using the PuTTY client.zInstance requires security key for connection, but security keys are not supported by the installed SSH version. OpenSSH 8.4 or higher is required.)security_keys_enabledsecurity_keysrH   ssh_security_key_supportr   r   environmentrE   )oslogin_states    r"   ConfirmSecurityKeyStatusr  *  s    0 
	,	,
 
	$	$
%	,- - ++
 ++3KK ' (  ')
'	?@ @
 	&	 r!   rj  )NFrk   )Gr   
__future__r   r   r   r  r  r   ro   r   r  googlecloudsdk.api_lib.computer   r   r   r	   googlecloudsdk.callioper
   r   r   #googlecloudsdk.command_lib.util.sshr   googlecloudsdk.corer  r   r   googlecloudsdk.core.consoler   r   googlecloudsdk.core.utilr   r   googlecloudsdk.core.util.filesr   r   r  r  r1   r   r  r  r   r%   r(   r+   r=   r@   rE   rH   rK   r^   ra   rc   rh   rv   r   r   r   r   r   r   r   objectr   rp  r  r  r`  r  r  r  r   r!   r"   <module>r     s   '  '      	 4 9 : 0 + / . 3 = # * 2 8 - * 5 5 
 "+  P  !: K  5 5 O$9$9 )?#8#8 )
,?(( 
,O)) 
o33 O$9$9 /"7"7 3?(( 3/QdA89(6 , #8.&5<.d 9>2"j.]F ]@>>j9} j9Z+ ,0*&9r!   