EdCdZddlZddlZddlZddlZddlZddlZddlmZm Z ddl m Z m Z ddl mZddlmZmZddlmZmZddlmZd d lmZd d lmZejeZd Zd Z d Z!dZ"eGddZ#eGddZ$eGddZ%dZ&dZ'dZ(dZ)d"dZ*dZ+dZ,dZ-ej.dZ/dZ0dZ1d Z2d!Z3dS)#z9Per-user domain config persistence (~/.lve/domains.json).N) dataclassfield)ListOptional) disable_quota)ClPwddrop_privileges) userdomainsdocroot)CPAPIException) id_registry)LvdErrorz.lvez domains.json)cpupmemionprociopsepvmemceZdZUdZeeed<dZeeed<dZeeed<dZ eeed<dZ eeed<dZ eeed<dZ eeed<d Z ed Zd Zd ZdS) DomainLimitsNrrrrrrrcHd|jDS)z+Return only fields that are set (non-None).ci|] \}}||| SN.0kvs py/websiteisolation/config.py z(DomainLimits.to_dict../sHHHA!-1---)__dict__itemsselfs r!to_dictzDomainLimits.to_dict-s$HH!4!4!6!6HHHHr#cb|s |S|did|DS)NcFi|]\}}|tv|t|Sr) LIMIT_FIELDSintrs r!r"z*DomainLimits.from_dict..5s.NNNDAqAz(DomainLimits.__bool__..=s&AAQ1D=AAAAAAr#)anyr$valuesr&s r!__bool__zDomainLimits.__bool__<s-AA$-*>*>*@*@AAAAAAr#)__name__ __module__ __qualname__rrr,__annotations__rrrrrrr( classmethodr0r4r:rr#r!rr#sC#D(3-B E8C=D(3-B D(3-IIIPP[P ))) BBBBBr#rc\eZdZUdZeed<eeZeed<dZ e dZ dS) DomainEntrynamedefault_factorylimitscD|j|jdS)NrCrF)rCrFr(r&s r!r(zDomainEntry.to_dictEs'Ik))++   r#c||ddt|dS)NrCrBrFrH)getrr0r-s r!r0zDomainEntry.from_dictKsIs&"%%))$((8*<*<==    r#N) r;r<r=rCstrr>rrrFr(r?r0rr#r!rArA@sjD#NNN 5>>>FL>>>     [   r#rAceZdZUeZeed<eeZ e e ed<edddZ e eed<dZd Zd Zd Zd Zedd ZedZdZeejdZedZdZdS) LvdConfigversionrDdomainsNF)defaultreprcompare_lve_idc8|jD]}|j|kr|cSdS)z5Find domain entry by name. Returns None if not found.N)rOrC)r'rCds r! find_domainzLvdConfig.find_domainYs1  Av~~tr#c8fd|jD|_dS)zRemove domain entry by name.c*g|]}|jk |SrrC)rrUrCs r! z+LvdConfig.remove_domain..bs BBBa16T>>>>>r#N)rO)r'rCs `r! remove_domainzLvdConfig.remove_domain`s$BBBB4<BBB r#cH t|j|}|rS||@|jt ||dSdSdS#t$r't d|j|dYdSwxYw)zEResolve domain name from *docroot* and add it to the config. NrYz(add_domain_by_docroot: uid=%s docroot=%sTexc_info) _resolve_domain_for_docrootrSrVrOappendrAsaverlogwarningr'r domain_names r!add_domain_by_docrootzLvdConfig.add_domain_by_docrootds >5dlGLLK t// <<D ##K[$A$A$ABBB   DD > > > KKB g  > > > > > > >sA(A00-B! B!c" t|j|}|r@||-|||dSdSdS#t $r't d|j|dYdSwxYw)zJResolve domain name from *docroot* and remove it from the config. Nz+remove_domain_by_docroot: uid=%s docroot=%sTr])r_rSrVr[rarrbrcrds r!remove_domain_by_docrootz"LvdConfig.remove_domain_by_docrootps >5dlGLLK t// <<H"";///   HH > > > KKE g  > > > > > > >sAA-B Bc4|jd|jDdS)Nc6g|]}|Sr)r(rrUs r!rZz%LvdConfig.to_dict..s ::: :::r#rNrOrlr&s r!r(zLvdConfig.to_dict|s)|::T\:::   r#ct|trd|vr ||S||dtd|dD|S)NrOrSrNcBg|]}t|Sr)rAr0rks r!rZz'LvdConfig.from_dict..s&GGG![**1--GGGr#)rNrOrS) isinstancedictrJLVD_CONFIG_VERSION)r.r/lve_ids r!r0zLvdConfig.from_dictst$%% '$)>)>3v&&& &sHHY(:;;GGtIGGG    r#c||5||cdddS#1swxYwYdS)z;Load domains.json for a user (acquires flock for the read).N)_flock_read)r.rss r!loadzLvdConfig.loadsZZ   % %99V$$ % % % % % % % % % % % % % % % % % %s 8<<c|jtd||j5|ddddS#1swxYwYdS)z@Write domains.json for this user (acquires flock for the write).Nz,cannot save: config has no associated lve_id)rSrru_writer&s r!razLvdConfig.saves < IJJ J [[ & &   KKMMM                  sAAAc#4Kt|5t|}tj|d}tj|tjtjzd} tj |tj dVtj |tj tj |n8#tj |tj tj |wxYw ddddS#1swxYwYdS)z+Exclusive flock on a per-user sidecar file.z .domains.lockiN) user_contextensure_config_dirospathjoinopenO_CREATO_RDWRfcntlflockLOCK_EXLOCK_UNclose)rs config_dir lock_pathfds r!ruzLvdConfig._flocks(& ! !  *622J ZAAIBJ$:EBBB  B ... B ...  B ...                   s*AD 0#C4D 5C<<D  DDct|}tj|s ||S t |dd5}t j|}dddn #1swxYwYnK#t jtf$r2}t d||||cYd}~Sd}~wwxYw| ||S)Nrnrzutf-8)encodingzfailed to read config %s: %srs) config_pathr}r~existsrjsonrwJSONDecodeErrorIOErrorrbrcr0)r.rsr~fr/es r!rvzLvdConfig._reads*6""w~~d## '3v&&& & 'dC'222 $ay|| $ $ $ $ $ $ $ $ $ $ $ $ $ $ $$g. ' ' ' KK6a @ @ @3v&&& & & & & & & '}}T&}111sAA;A/# A;/A33A;6A37A;;C'B>8C>Cc t|j}t|j}tj|ddz} t |||dS#t$r}td|d||d}~wwxYw)N)indent zfailed to write config z: ) r|rSrrdumpsr(_write_via_tmprr)r'rr~contentrs r!ryzLvdConfig._writes&t|44 4<((*T\\^^A666= I :tW 5 5 5 5 5 I I I@T@@Q@@AAq H IsA(( B 2BB r)r;r<r=rrrNr,r>rlistrOrrArSrrVr[rfrhr(r?r0rwra staticmethod contextlibcontextmanagerrurvryrr#r!rMrMSs]%GS%%%!&t! > > > > >      [ %%[%   \  2 2[ 2IIIIIr#rMc"tdS)Nr)min_uid)rrr#r!_clpwdrs    r#c t|djS#tj$r}t d|d|d}~wwxYw)zGet home directory for a uid.rzuser with uid z not foundN)r get_pw_by_uidpw_dirrNoSuchUserExceptionr)uidexcs r! get_homedirrsjBxx%%c**1-44  $BBB777788cABs+.AAAc t|S#tj$r}t d|d|d}~wwxYw)zResolve username to uid.zuser 'z ' not foundN)rget_uidrrr)usernamers r!get_uid_by_usernamersb@xx)))  $@@@555566C?@s #A AA c t|dS#tjtf$r}t d||d}~wwxYw)zResolve username from uid.rzno user found for uid N)r get_namesrr IndexErrorr)rsrs r! get_usernamersiCxx!!&))!,,  %z 2CCC88899sBCs&)AAAc|t|S|t|Stj}|dkrt d|S)zo Resolve lve_id from either --lve-id or --username. If neither, use effective UID (end-user mode). Nrz(root must specify --lve-id or --username)r,rr}geteuidr)rsrrs r!resolve_lve_idrsR 6{{"8,,, *,,C axxABBB Jr#cvt|}tj|tt S)z.Return path to the user's domains.json config.)rr}r~rLVD_CONFIG_DIRLVD_CONFIG_FILE)rshomedirs r!rrs'&!!G 7<< A AAr#ct|}tj|t}tj|stj|dd|S)zCreate ~/.lve/ directory with proper permissions if it doesn't exist. Caller must ensure privileges are already dropped to the target user.iT)modeexist_ok)rr}r~rrisdirmakedirs)rsrrs r!r|r|sY&!!Gg~66J 7== $ $; JUT:::: r#cpd} tjd|d5}|j}|||t j|dddn #1swxYwYt j|||rGt j |r* t j |dS#t$rYdSwxYwdSdS#|rEt j |r' t j |w#t$rYwwxYwwwxYw)z8Write content to a file atomically via a temporary file.NwF)dirdelete) tempfileNamedTemporaryFilerCwriteflushr}fsyncfilenoreplacer~rremoveOSError) directoryfilenamer temp_pathtmps r!rrsI   ()E J J J #cI IIg    IIKKK HSZZ\\ " " "  # # # # # # # # # # # # # # # 9h'''   22   )$$$$$        9  22   )$$$$      seC+AA>2 C+>BC+BC+C C$#C$+"D5D#"D5# D0-D5/D00D5c#Ktjdkrct|}t|5t 5dVdddn #1swxYwYddddS#1swxYwYdSdVdS)uDrop privileges to the target user if running as root. CLOS-4370: when invoked as root we pair drop_privileges with secureio.disable_quota — creating ~/.lve/ and the lockfile / writing domains.json must succeed even when the user is over disk quota, otherwise OSError(EDQUOT) inside this block aborts batch operations like `lvectl apply all` and leaves the rest of the users with stale limits. disable_quota raises CAP_SYS_RESOURCE in the effective set rN)r}rrr r)rsrs r!r{r{s z||q'' X & &      EEE                                  s5A0A A0A A0A A00A47A4ctj}tj|sgSg}tj|D]5} |t|&#t$rY2wxYw|S)a Return list of LVE IDs (UIDs) that have domain isolation configured. Scans the id_registry directory for per-user registry files. Users only appear here after ``lvdctl set`` or ``lvdctl apply`` has been called at least once for one of their domains. ) r LVD_IDS_DIRr}r~rlistdirr`r, ValueError)ids_dirresultrCs r!find_all_lve_ids_with_configr#s%G 7== ! ! F 7##  MM#d)) $ $ $ $    H  Ms"A** A76A7c6t|S)z2Load the domain isolation config for a given user.)rMrwrs r! load_configr6s >>& ! !!r#cz t|dS#t$r}td|d|d}~wwxYw)z8Resolve domain name to document root path via panel API.rz&failed to resolve docroot for domain ''N) cpapi_docroot Exceptionr)rers r!resolve_docrootr;s[Y[))!,, YYYN NNNOOUXXYs :5:c t|}t|pgD]\}}||kr|cSn;#tttf$r!t d||dYnwxYwdS)zBReturn domain name whose document root matches *docroot*, or None.z4Cannot resolve domain for docroot: uid=%s docroot=%sTr]N)rr rr rrbrc)rr rredrs r!r_r_Csi$$ +H 5 5 ; # #OKW}}"""" # ng .iii JCQXcg hhhhhi 4s.335A+*A+)NN)4__doc__rrrloggingr}r dataclassesrrtypingrrsecureiorclcommon.clpwdrr clcommon.cpapir r rclcommon.cpapi.cpapiexceptionsr rBr exceptionsr getLoggerr;rbrrrrr+rrArMrrrrrrr|rrr{rrrr_rr#r!rs @?  ((((((((!!!!!!!!""""""21111111@@@@@@@@999999 g!! C  BBBBBBB B8          $ lIlIlIlIlIlIlI lI^BBB@@@CCC    BBB $ $&""" YYY     r#