a ah€iÞXã@slddlZddlZddlZddlmZddlmZmZmZddl m Z m Z m Z e  e¡ZdZdZdZdeeƒd ZGd d „d ƒZGd d „d ƒZdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zefdd„Zd7dd„ZGdd„dƒZeed œd!d"„Z eed œd#d$„Z!d%d&„Z"ee#d'œd(d)„Z$d*d+„Z%efd,d-„Z&d.d/„Z'efeeeefd0œd1d2„Z(d3d4„Z)d5d6„Z*dS)8éN)Úsuppress)ÚListÚSequenceÚTuple)Ú lifecycleÚsubpÚutilz/etc/ssh/sshd_config)ZrsaZecdsaZed25519z(ecdsa-sha2-nistp256-cert-v01@openssh.comzecdsa-sha2-nistp256z(ecdsa-sha2-nistp384-cert-v01@openssh.comzecdsa-sha2-nistp384z(ecdsa-sha2-nistp521-cert-v01@openssh.comzecdsa-sha2-nistp521z+sk-ecdsa-sha2-nistp256-cert-v01@openssh.comz"sk-ecdsa-sha2-nistp256@openssh.comz#sk-ssh-ed25519-cert-v01@openssh.comzsk-ssh-ed25519@openssh.comz ssh-ed25519-cert-v01@openssh.comz ssh-ed25519zssh-rsa-cert-v01@openssh.comzssh-rsazssh-xmss-cert-v01@openssh.comzssh-xmss@openssh.coméŽz§no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"$USER\" rather than the user \"$DISABLE_USER\".';echo;sleep 10;exit ú"c@s&eZdZddd„Zdd„Zdd„ZdS) Ú AuthKeyLineNcCs"||_||_||_||_||_dS©N)Úbase64ÚcommentÚoptionsÚkeytypeÚsource)Úselfrrr rr©rú6/usr/lib/python3.9/site-packages/cloudinit/ssh_util.pyÚ__init__Es zAuthKeyLine.__init__cCs |jo |jSr )r r©rrrrÚvalidNszAuthKeyLine.validcCsdg}|jr| |j¡|jr(| |j¡|jr:| |j¡|jrL| |j¡|sV|jSd |¡SdS©Nú )rÚappendrr rrÚjoin)rÚtoksrrrÚ__str__Qs    zAuthKeyLine.__str__)NNNN)Ú__name__Ú __module__Ú __qualname__rrrrrrrr Dsÿ r c@s"eZdZdZdd„Zddd„ZdS)ÚAuthKeyLineParserau AUTHORIZED_KEYS FILE FORMAT AuthorizedKeysFile specifies the file containing public keys for public key authentication; if none is specified, the default is ~/.ssh/authorized_keys. Each line of the file contains one key (empty (because of the size of the public key encoding) up to a limit of 8 kilo- bytes, which permits DSA keys up to 8 kilobits and RSA keys up to 16 kilobits. You don't want to type them in; instead, copy the identity.pub or the id_rsa.pub file and edit it. sshd enforces a minimum RSA key modulus size for protocol 1 and protocol 2 keys of 768 bits. The options (if present) consist of comma-separated option specifica- tions. No spaces are permitted, except within double quotes. The fol- lowing option specifications are supported (note that option keywords are case-insensitive): cCs¨d}d}|t|ƒkr„|s$||dvr„||}|dt|ƒkrF|d}q„||d}|dkrl|dkrl|d}n|dkrz| }|d}q|d|…}||d… ¡}||fS)z× The options (if present) consist of comma-separated option specifica- tions. No spaces are permitted, except within double quotes. Note that option keywords are case-insensitive. Fr)rú éú\r N)ÚlenÚlstrip)rÚentZquotedÚiZcurcZnextcrÚremainrrrÚ_extract_optionsus     z"AuthKeyLineParser._extract_optionsNc Cs¼| d¡}| d¡s | ¡dkr(t|ƒSdd„}| ¡}z||ƒ\}}}Wn^ty¨| |¡\} } |durr| }z|| ƒ\}}}Wnty¢t|ƒYYS0Yn0t|||||dS)Nz ú#ÚcSs^| dd¡}t|ƒdkr(tdt|ƒƒ‚|dtvrDtd|dƒ‚t|ƒdkrZ| d¡|S)NézTo few fields: %srzInvalid keytype %sr,)Úsplitr%Ú TypeErrorÚVALID_KEY_TYPESr)r'rrrrÚ parse_ssh_key•s     z.AuthKeyLineParser.parse..parse_ssh_key)rr rr)ÚrstripÚ startswithÚstripr r/r*) rZsrc_linerÚliner1r'rr rZkeyoptsr)rrrÚparses,   ûzAuthKeyLineParser.parse)N)rrr Ú__doc__r*r6rrrrr!asr!c Cszg}tƒ}g}|D]b}z8tj |¡rLt |¡ ¡}|D]}| | |¡¡q6Wqt t fyrt  t d|¡Yq0q|S)NzError reading lines from %s) r!ÚosÚpathÚisfilerÚload_text_fileÚ splitlinesrr6ÚIOErrorÚOSErrorÚlogexcÚLOG)ÚfnamesÚlinesÚparserÚcontentsÚfnamer5rrrÚparse_authorized_keysºs rFcCs tdd„|Dƒƒ}tt|ƒƒD]J}||}| ¡s4q|D]&}|j|jkr8|}||vr8| |¡q8|||<q|D]}| |¡qndd„|Dƒ}| d¡d |¡S)NcSsg|]}| ¡r|‘qSr)r©Ú.0ÚkrrrÚ Ëóz*update_authorized_keys..cSsg|] }t|ƒ‘qSr©Ústr)rHÚbrrrrJßrKr,Ú )ÚlistÚranger%rr Úremoverr)Z old_entriesÚkeysZto_addr(r'rIÚkeyrBrrrÚupdate_authorized_keysÊs      rUcCs4t |¡}|r|js td|ƒ‚tj |jd¡|fS)Nz"Unable to get SSH info for user %rz.ssh)ÚpwdÚgetpwnamÚpw_dirÚ RuntimeErrorr8r9r)ÚusernameÚpw_entrrrÚusers_ssh_infoæs   r\c Cspd|fd|fdf}|sd}| ¡}g}|D]@}|D]\}}| ||¡}q2| d¡s`tj ||¡}| |¡q*|S)Nú%hú%u)z%%ú%ú%h/.ssh/authorized_keysú/)r.Úreplacer3r8r9rr) ÚvalueÚhomedirrZZmacrosÚpathsZrenderedr9ZmacroZfieldrrrÚrender_authorizedkeysfile_pathsís   rfc CsÐd}|r d}t |¡}|r@||kr@|dkr@t d||||¡dSt |¡}||kr\|dM}n.t |¡}t |¡} || vr‚|dM}n|dM}||@d krªt d |||¡dS|rÌ|d @d krÌt d ||¡dSd S)aVCheck if the file/folder in @current_path has the right permissions. We need to check that: 1. If StrictMode is enabled, the owner is either root or the user 2. the user can access the file/folder, otherwise ssh won't use it 3. If StrictMode is enabled, no write permission is given to group and world users (022) iÉi¤ÚrootzXPath %s in %s must be own by user %s or by root, but instead is own by %s. Ignoring key.FéÀé8érzBPath %s in %s must be accessible by user %s, check its permissionsézRPath %s in %s must not give writepermission to group or world users. Ignoring key.T)rZ get_ownerr@ÚdebugZget_permissionsZ get_groupZget_user_groups) rZZ current_pathÚ full_pathÚis_fileÚ strictmodesZminimal_permissionsÚownerZparent_permissionZ group_ownerZ user_groupsrrrÚcheck_permissionssJ  ú      ûürqc Cst|ƒd}tdƒd}z²| d¡dd…}d}tj |j¡}|D]}|d|7}tj |¡rvt d|¡WdStj  |¡r–t d|¡WdS|  |¡sD||jkr¬qDtj  |¡s0t   |¡Zd } |j} |j} |  |j¡ròd } |j} |j} tj|| d d t  || | ¡Wdƒn1s&0Yt|||d|ƒ} | sDWdSqDtj |¡sjtj |¡r|t d |¡WdStj  |¡s®t j|ddd dt  ||j|j¡t|||d |ƒ} | sÊWdSWn<ttfy} zt  tt| ƒ¡WYd} ~ dSd} ~ 00d S)Nr#rgraéÿÿÿÿr,z-Invalid directory. Symlink exists in path: %sFz*Invalid directory. File exists in path: %séírhT)ÚmodeÚexist_okz%s is not a file!é€)rtZensure_dir_exists)r\r.r8r9ÚdirnamerXÚislinkr@rlr:r3ÚexistsrÚ SeLinuxGuardÚpw_uidÚpw_gidÚmakedirsZ chownbyidrqÚisdirÚ write_filer=r>r?rM)rZÚfilenameroZ user_pwentZ root_pwentZ directoriesZ parent_folderZ home_folderZ directoryrtÚuidÚgidZ permissionsÚerrrÚcheck_create_pathGsn     þ ÿÿþ  . ÿ   ÿ r„c Cs4t|ƒ\}}tj |d¡}|}g}tj|ddvz2t|ƒ}| dd¡}| dd¡} t||j |ƒ}Wn2t t fy–||d<t  t d t|d¡Yn0Wdƒn1s¬0Yt| ¡|ƒD]J\} } td | vd | v|  d  |j ¡¡gƒrÄt|| | dkƒ} | rÄ| }qqÄ||kr&t  d |¡|t|gƒfS)NZauthorized_keysT©Ú recursiveZauthorizedkeysfiler`roZyesrzhFailed extracting 'AuthorizedKeysFile' in SSH config from %r, using 'AuthorizedKeysFile' file %r insteadr^r]z{}/zAAuthorizedKeysFile has an user-specific authorized_keys, using %s)r\r8r9rrrzÚparse_ssh_config_mapÚgetrfrXr=r>r?r@Ú DEF_SSHD_CFGÚzipr.Úanyr3Úformatr„rlrF) rZZ sshd_cfg_fileÚssh_dirr[Zdefault_authorizedkeys_fileZuser_authorizedkeys_fileZ auth_key_fnsZssh_cfgZ key_pathsroZkey_pathÚ auth_key_fnZpermissions_okrrrÚextract_authorized_keys–sV ÿ ÿú( ýÿ ÿ ýþrc Cstƒ}g}|D]}| |jt|ƒ|d¡qt|ƒ\}}tj |¡}tj |dd*t ||ƒ} tj || ddWdƒn1s‚0YdS)N)rTr…©Ú preserve_mode) r!rr6rMrr8r9rwrrzrUr) rSrZrrCZ key_entriesrIrŽZauth_key_entriesrÚcontentrrrÚsetup_user_keysÏs   r“c@s*eZdZddd„Zedd„ƒZdd„ZdS) ÚSshdConfigLineNcCs||_||_||_dSr )r5Ú_keyrc)rr5rIÚvrrrrßszSshdConfigLine.__init__cCs|jdurdS|j ¡Sr )r•ÚlowerrrrrrTäs zSshdConfigLine.keycCs>|jdurt|jƒSt|jƒ}|jr6|dt|jƒ7}|SdSr)r•rMr5rc)rr–rrrrës    zSshdConfigLine.__str__)NN)rrr rÚpropertyrTrrrrrr”Þs  r”)ÚreturncCs"tj |¡sgStt |¡ ¡ƒSr )r8r9r:Úparse_ssh_config_linesrr;r<©rErrrÚparse_ssh_configõs rœc Cs¬g}|D]ž}| ¡}|r"| d¡r2| t|ƒ¡qz| dd¡\}}WnLty’z| dd¡\}}Wn$tyŒt d|¡YYqYn0Yn0| t|||ƒ¡q|S)Nr+r#ú=z;sshd_config: option "%s" has no key/value pair, skipping it)r4r3rr”r.Ú ValueErrorr@rl)rBÚretr5rTÚvalrrrršûs&  ýršcCs6t|ƒ}|siSi}|D]}|js$q|j||j<q|Sr )rœrTrc)rErBrŸr5rrrr‡sr‡)rEr™cCs@tj |¡sdSt |¡ ¡D]}| d|›d¡rdSqdS)NFzInclude z .d/*.confT)r8r9r:rr;r<r3)rEr5rrrÚ_includes_dconf"s  r¡cCs^t|ƒrZtj |›d¡s.tj|›dddtj |›dd¡}tj |¡sZt |d¡|S)Nz.drs)rtz50-cloud-init.confrv) r¡r8r9r~rZ ensure_dirrr:Z ensure_filer›rrrÚ"_ensure_cloud_init_ssh_config_file+s  r¢cCsPt|ƒ}t|ƒ}t||d}|rDtj|d dd„|Dƒ¡dddt|ƒdkS)z©Read fname, and update if changes are necessary. @param updates: dictionary of desired values {Option: value} @return: boolean indicating if an update was done.)rBÚupdatesrOcSsg|] }t|ƒ‘qSrrL)rHr5rrrrJArKz%update_ssh_config..Trr)r¢rœÚupdate_ssh_config_linesrrrr%)r£rErBÚchangedrrrÚupdate_ssh_config6s ýr¦c Cstƒ}g}tdd„| ¡Dƒƒ}t|ddD]v\}}|js.r#)Ústartz$line %d: option %s already set to %sz#line %d: option %s updated %s -> %sr,z line %d: option %s added with %s) ÚsetÚdictrSÚ enumeraterTÚaddrcr@rlrr%Úitemsr”) rBr£Úfoundr¥Zcasemapr(r5rTrcrrrr¤GsB    ÿ û  ÿr¤)rBcCs>|sdSt|ƒ}dd„|Dƒ}tj|d |¡dddddS)Ncss |]\}}|›d|›VqdS)rNr)rHrIr–rrrÚ yrKz$append_ssh_config..rOÚabT)Zomoder‘)r¢rrr)rBrEr’rrrÚappend_ssh_configus ür°cCs„d}ttjƒ*tjddgddgd\}}Wdƒn1s>0Yd}| d ¡D](}| |¡rV|t|ƒ| d ¡…SqVdS) zàGet the full version of the OpenSSH sshd daemon on the system. On an ubuntu system, this would look something like: 1.2p1 Ubuntu-1ubuntu0.1 If we can't find `sshd` or parse the version number, return None. r,Zsshdz-Vrr#)ZrcsNZOpenSSH_rOú,)rrZProcessExecutionErrorr.r3r%Úfind)ÚerrÚ_Úprefixr5rrrÚget_opensshd_version‚s  8 r¶c Cs”d}tƒ}|durtj |¡Sd|vr:|d| d¡…}n d|vrV|d| d¡…}n|}ztj |¡}|WSttfyŽt d|¡Yn0dS)zäGet the upstream version of the OpenSSH sshd daemon on the system. This will NOT include the portable number, so if the Ubuntu version looks like `1.2p1 Ubuntu-1ubuntu0.1`, then this function would return `1.2` z9.0NÚprz Could not parse sshd version: %s) r¶rZVersionZfrom_strr²ržr/r@Zwarning)Zupstream_versionZ full_versionrrrÚget_opensshd_upstream_version–s  r¸)N)+Zloggingr8rVÚ contextlibrÚtypingrrrZ cloudinitrrrZ getLoggerrr@r‰r0Z_DISABLE_USER_SSH_EXITrMZDISABLE_USER_OPTSr r!rFrUr\rfrqr„rr“r”rœršr‡Úboolr¡r¢r¦r¤r°r¶r¸rrrrÚ sH  ýýÿYEO 9    .