PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/saltstack/salt/lib/python3.10/site-packages/salt/utils
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/utils/verify.py
""" A few checks to make sure the environment is sane """ import errno import itertools import logging import os import re import socket import stat import sys import urllib.parse import salt.defaults.exitcodes import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.user from salt._logging import LOG_LEVELS from salt.exceptions import ( CommandExecutionError, SaltClientError, SaltSystemExit, SaltValidationError, ) # Original Author: Jeff Schroeder <jeffschroeder@computer.org> try: import win32file import salt.utils.win_reg except ImportError: import resource log = logging.getLogger(__name__) ROOT_DIR = "c:\\salt" if salt.utils.platform.is_windows() else "/" DEFAULT_SCHEMES = ["tcp://", "udp://", "file://"] def zmq_version(): """ ZeroMQ python bindings >= 2.1.9 are required """ try: import zmq except Exception: # pylint: disable=broad-except # Return True for local mode return True ver = zmq.__version__ # The last matched group can be None if the version # is something like 3.1 and that will work properly match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?", ver) # Fallthrough and hope for the best if not match: log.warning("Using untested zmq python bindings version: '%s'", ver) return True major, minor, point = match.groups() if major.isdigit(): major = int(major) if minor.isdigit(): minor = int(minor) # point very well could be None if point and point.isdigit(): point = int(point) if major == 2 and minor == 1: # zmq 2.1dev could be built against a newer libzmq if "dev" in ver and not point: log.warning("Using dev zmq module, please report unexpected results") return True elif point and point >= 9: return True elif major > 2 or (major == 2 and minor > 1): return True # If all else fails, gracefully croak and warn the user log.critical("ZeroMQ python bindings >= 2.1.9 are required") if "salt-master" in sys.argv[0]: log.critical( "The Salt Master is unstable using a ZeroMQ version " "lower than 2.1.11 and requires this fix: http://lists.zeromq." "org/pipermail/zeromq-dev/2011-June/012094.html" ) return False def lookup_family(hostname): """ Lookup a hostname and determine its address family. The first address returned will be AF_INET6 if the system is IPv6-enabled, and AF_INET otherwise. """ # If lookups fail, fall back to AF_INET sockets (and v4 addresses). fallback = socket.AF_INET try: hostnames = socket.getaddrinfo( hostname or None, None, socket.AF_UNSPEC, socket.SOCK_STREAM ) if not hostnames: return fallback h = hostnames[0] return h[0] except socket.gaierror: return fallback def verify_socket(interface, pub_port, ret_port): """ Attempt to bind to the sockets to verify that they are available """ addr_family = lookup_family(interface) for port in pub_port, ret_port: sock = socket.socket(addr_family, socket.SOCK_STREAM) try: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((interface, int(port))) except Exception as exc: # pylint: disable=broad-except msg = f"Unable to bind socket {interface}:{port}" if exc.args: msg = f"{msg}, error: {str(exc)}" else: msg = f"{msg}, this might not be a problem." msg += "; Is there another salt-master running?" log.warning(msg) return False finally: sock.close() return True def verify_logs_filter(files): """ Filter files to verify. """ to_verify = [] for filename in files: verify_file = True for scheme in DEFAULT_SCHEMES: if filename.startswith(scheme): verify_file = False break if verify_file: to_verify.append(filename) return to_verify def verify_log_files(files, user): """ Verify the log files exist and are owned by the named user. Filenames that begin with tcp:// and udp:// will be filtered out. Filenames that begin with file:// are handled correctly """ return verify_files(verify_logs_filter(files), user) def _get_pwnam(user): """ Get the user from passwords database """ if salt.utils.platform.is_windows(): return True import pwd # after confirming not running Windows try: return pwd.getpwnam(user) except KeyError: print( "Failed to prepare the Salt environment for user {}. " "The user is not available.".format(user), file=sys.stderr, flush=True, ) sys.exit(salt.defaults.exitcodes.EX_NOUSER) def verify_files(files, user): """ Verify that the named files exist and are owned by the named user """ if salt.utils.platform.is_windows(): return True # after confirming not running Windows pwnam = _get_pwnam(user) uid = pwnam[2] for fn_ in files: dirname = os.path.dirname(fn_) try: if dirname: try: os.makedirs(dirname) except OSError as err: if err.errno != errno.EEXIST: raise if not os.path.isfile(fn_): with salt.utils.files.fopen(fn_, "w"): pass except OSError as err: if os.path.isfile(dirname): msg = f"Failed to create path {fn_}, is {dirname} a file?" raise SaltSystemExit(msg=msg) if err.errno != errno.EACCES: raise msg = 'No permissions to access "{}", are you running as the correct user?'.format( fn_ ) raise SaltSystemExit(msg=msg) except OSError as err: # pylint: disable=duplicate-except msg = f'Failed to create path "{fn_}" - {err}' raise SaltSystemExit(msg=msg) stats = os.stat(fn_) if uid != stats.st_uid: try: os.chown(fn_, uid, -1) except OSError: pass return True def verify_env( dirs, user, permissive=False, pki_dir="", skip_extra=False, root_dir=ROOT_DIR ): """ Verify that the named directories are in place and that the environment can shake the salt """ if salt.utils.platform.is_windows(): return win_verify_env( root_dir, dirs, permissive=permissive, skip_extra=skip_extra ) # after confirming not running Windows pwnam = _get_pwnam(user) uid = pwnam[2] gid = pwnam[3] groups = salt.utils.user.get_gid_list(user, include_default=False) for dir_ in dirs: if not dir_: continue if not os.path.isdir(dir_): try: with salt.utils.files.set_umask(0o022): os.makedirs(dir_) # If starting the process as root, chown the new dirs if os.getuid() == 0: os.chown(dir_, uid, gid) except OSError as err: msg = 'Failed to create directory path "{0}" - {1}\n' sys.stderr.write(msg.format(dir_, err)) sys.exit(err.errno) mode = os.stat(dir_) # If starting the process as root, chown the new dirs if os.getuid() == 0: fmode = os.stat(dir_) if fmode.st_uid != uid or fmode.st_gid != gid: if permissive and fmode.st_gid in groups: # Allow the directory to be owned by any group root # belongs to if we say it's ok to be permissive pass else: # chown the file for the new user os.chown(dir_, uid, gid) for subdir in [a for a in os.listdir(dir_) if "jobs" not in a]: fsubdir = os.path.join(dir_, subdir) if f"{os.path.sep}jobs" in fsubdir: continue for root, dirs, files in salt.utils.path.os_walk(fsubdir): for name in itertools.chain(files, dirs): if name.startswith("."): continue path = os.path.join(root, name) try: fmode = os.stat(path) if fmode.st_uid != uid or fmode.st_gid != gid: if permissive and fmode.st_gid in groups: pass else: # chown the file for the new user os.chown(path, uid, gid) except OSError: continue # Allow the pki dir to be 700 or 750, but nothing else. # This prevents other users from writing out keys, while # allowing the use-case of 3rd-party software (like django) # to read in what it needs to integrate. # # If the permissions aren't correct, default to the more secure 700. # If acls are enabled, the pki_dir needs to remain readable, this # is still secure because the private keys are still only readable # by the user running the master if dir_ == pki_dir: smode = stat.S_IMODE(mode.st_mode) if smode != 448 and smode != 488: if os.access(dir_, os.W_OK): os.chmod(dir_, 448) else: log.critical( 'Unable to securely set the permissions of "%s".', dir_ ) if skip_extra is False: # Run the extra verification checks zmq_version() def check_user(user): """ Check user and assign process uid/gid. """ if salt.utils.platform.is_windows(): return True if user == salt.utils.user.get_user(): return True # after confirming not running Windows pwuser = _get_pwnam(user) try: if hasattr(os, "initgroups"): os.initgroups(user, pwuser.pw_gid) else: os.setgroups(salt.utils.user.get_gid_list(user, include_default=False)) os.setgid(pwuser.pw_gid) os.setuid(pwuser.pw_uid) # We could just reset the whole environment but let's just override # the variables we can get from pwuser if "HOME" in os.environ: os.environ["HOME"] = pwuser.pw_dir if "SHELL" in os.environ: os.environ["SHELL"] = pwuser.pw_shell for envvar in ("USER", "LOGNAME"): if envvar in os.environ: os.environ[envvar] = pwuser.pw_name except OSError: log.critical('Salt configured to run as user "%s" but unable to switch.', user) return False return True def list_path_traversal(path): """ Returns a full list of directories leading up to, and including, a path. So list_path_traversal('/path/to/salt') would return: ['/', '/path', '/path/to', '/path/to/salt'] in that order. This routine has been tested on Windows systems as well. list_path_traversal('c:\\path\\to\\salt') on Windows would return: ['c:\\', 'c:\\path', 'c:\\path\\to', 'c:\\path\\to\\salt'] """ out = [path] (head, tail) = os.path.split(path) if tail == "": # paths with trailing separators will return an empty string out = [head] (head, tail) = os.path.split(head) while head != out[0]: # loop until head is the same two consecutive times out.insert(0, head) (head, tail) = os.path.split(head) return out def check_path_traversal(path, user="root", skip_perm_errors=False): """ Walk from the root up to a directory and verify that the current user has access to read each directory. This is used for making sure a user can read all parent directories of the minion's key before trying to go and generate a new key and raising an IOError """ for tpath in list_path_traversal(path): if not os.access(tpath, os.R_OK): msg = f"Could not access {tpath}." if not os.path.exists(tpath): msg += " Path does not exist." else: current_user = salt.utils.user.get_user() # Make the error message more intelligent based on how # the user invokes salt-call or whatever other script. if user != current_user: msg += f" Try running as user {user}." else: msg += f" Please give {user} read permissions." # We don't need to bail on config file permission errors # if the CLI # process is run with the -a flag if skip_perm_errors: return # Propagate this exception up so there isn't a sys.exit() # in the middle of code that could be imported elsewhere. raise SaltClientError(msg) def check_max_open_files(opts): """ Check the number of max allowed open files and adjust if needed """ mof_c = opts.get("max_open_files", 100000) if sys.platform.startswith("win"): # Check the Windows API for more detail on this # http://msdn.microsoft.com/en-us/library/xt874334(v=vs.71).aspx # and the python binding http://timgolden.me.uk/pywin32-docs/win32file.html mof_s = mof_h = win32file._getmaxstdio() else: mof_s, mof_h = resource.getrlimit( # pylint: disable=used-before-assignment resource.RLIMIT_NOFILE ) accepted_keys_dir = os.path.join(opts.get("pki_dir"), "minions") accepted_count = len(os.listdir(accepted_keys_dir)) log.debug("This salt-master instance has accepted %s minion keys.", accepted_count) level = logging.INFO if (accepted_count * 4) <= mof_s: # We check for the soft value of max open files here because that's the # value the user chose to raise to. # # The number of accepted keys multiplied by four(4) is lower than the # soft value, everything should be OK return msg = ( "The number of accepted minion keys({}) should be lower than 1/4 " "of the max open files soft setting({}). ".format(accepted_count, mof_s) ) if accepted_count >= mof_s: # This should never occur, it might have already crashed msg += "salt-master will crash pretty soon! " level = logging.CRITICAL elif (accepted_count * 2) >= mof_s: # This is way too low, CRITICAL level = logging.CRITICAL elif (accepted_count * 3) >= mof_s: level = logging.WARNING # The accepted count is more than 3 time, WARN elif (accepted_count * 4) >= mof_s: level = logging.INFO if mof_c < mof_h: msg += ( "According to the system's hard limit, there's still a " "margin of {} to raise the salt's max_open_files " "setting. ".format(mof_h - mof_c) ) msg += "Please consider raising this value." log.log(level=level, msg=msg) def _realpath_darwin(path): base = "" for part in path.split(os.path.sep)[1:]: if base != "": if os.path.islink(os.path.sep.join([base, part])): base = os.readlink(os.path.sep.join([base, part])) else: base = os.path.abspath(os.path.sep.join([base, part])) else: base = os.path.abspath(os.path.sep.join([base, part])) return base def _realpath_windows(path): base = "" for part in path.split(os.path.sep): if base != "": try: # Need to use salt.utils.path.readlink as it handles junctions part = salt.utils.path.readlink(os.path.sep.join([base, part])) base = os.path.abspath(part) except OSError: base = os.path.abspath(os.path.sep.join([base, part])) else: base = part # Python 3.8 added support for directory junctions which prefixes the # return with `\\?\`. We need to strip that off. # https://docs.python.org/3/library/os.html#os.readlink if base.startswith("\\\\?\\"): base = base[4:] return base def _realpath(path): """ Cross platform realpath method. On Windows when python 3, this method uses the os.readlink method to resolve any filesystem links. All other platforms and version use ``os.path.realpath``. """ if salt.utils.platform.is_darwin(): return _realpath_darwin(path) elif salt.utils.platform.is_windows(): return _realpath_windows(path) return os.path.realpath(path) def clean_path(root, path, subdir=False, realpath=True): """ Accepts the root the path needs to be under and verifies that the path is under said root. Pass in subdir=True if the path can result in a subdirectory of the root instead of having to reside directly in the root. Pass realpath=False if filesystem links should not be resolved. """ if not os.path.isabs(root): root = os.path.join(os.getcwd(), root) normroot = os.path.normpath(root) if not os.path.isabs(path): path = os.path.join(normroot, path) normpath = os.path.normpath(path) if realpath: normroot = _realpath(normroot) normpath = _realpath(normpath) if subdir: if os.path.commonpath([normpath, normroot]) == normroot: return normpath else: if os.path.dirname(normpath) == normroot: return normpath return "" def clean_join(root, *paths, subdir=False, realpath=True): """ Performa a join and then check the result against the clean_path method. If clean_path fails a SaltValidationError is raised. """ parent = root for path in paths: child = os.path.join(parent, path) if not clean_path(parent, child, subdir, realpath): raise SaltValidationError(f"Invalid path: {path!r}") parent = child return child def valid_id(opts, id_): """ Returns if the passed id is valid """ try: if any(x in id_ for x in ("/", "\\", "\0")): return False return bool(clean_path(opts["pki_dir"], id_)) except (AttributeError, KeyError, TypeError, UnicodeDecodeError): return False def safe_py_code(code): """ Check a string to see if it has any potentially unsafe routines which could be executed via python, this routine is used to improve the safety of modules suct as virtualenv """ bads = ("import", ";", "subprocess", "eval", "open", "file", "exec", "input") for bad in bads: if code.count(bad): return False return True def insecure_log(): """ Return the insecure logs types """ insecure = [] for level, value in LOG_LEVELS.items(): if value < LOG_LEVELS.get("info", 20): insecure.append(level) return insecure def verify_log(opts): """ If an insecre logging configuration is found, show a warning """ level = LOG_LEVELS.get(str(opts.get("log_level")).lower(), logging.NOTSET) if level < logging.INFO: log.warning( "Insecure logging configuration detected! Sensitive data may be logged." ) def win_verify_env(path, dirs, permissive=False, pki_dir="", skip_extra=False): """ Verify that the named directories are in place and that the environment can shake the salt """ import salt.utils.path import salt.utils.win_dacl import salt.utils.win_functions # Make sure the file_roots is not set to something unsafe since permissions # on that directory are reset # `salt.utils.path.safe_path` will consider anything inside `C:\Windows` to # be unsafe. In some instances the test suite uses # `C:\Windows\Temp\salt-tests-tmpdir\rootdir` as the file_roots. So, we need # to consider anything in `C:\Windows\Temp` to be safe system_root = os.environ.get("SystemRoot", r"C:\Windows") allow_path = "\\".join([system_root, "TEMP"]) if not salt.utils.path.safe_path(path=path, allow_path=allow_path): raise CommandExecutionError( f"`file_roots` set to a possibly unsafe location: {path}" ) # Create the root path directory if missing if not os.path.isdir(path): os.makedirs(path) current_user = salt.utils.win_functions.get_current_user() # Set permissions to the registry key if salt.utils.win_functions.is_admin(current_user): reg_path = "HKLM\\SOFTWARE\\Salt Project\\salt" if not salt.utils.win_reg.key_exists( hive="HKLM", key="SOFTWARE\\Salt Project\\salt" ): salt.utils.win_reg.set_value( hive="HKLM", key="SOFTWARE\\Salt Project\\salt" ) try: # Make the Administrators group owner # Use the SID to be locale agnostic salt.utils.win_dacl.set_owner( obj_name=reg_path, principal="S-1-5-32-544", obj_type="registry" ) except CommandExecutionError: log.critical("Unable to securely set the owner of '%s'.", reg_path) try: # Get a clean dacl by not passing an obj_name dacl = salt.utils.win_dacl.dacl(obj_type="registry") # Add aces to the dacl, use the GUID (locale non-specific) # Administrators Group dacl.add_ace( principal="S-1-5-32-544", access_mode="grant", permissions="full_control", applies_to="this_key_subkeys", ) # System dacl.add_ace( principal="S-1-5-18", access_mode="grant", permissions="full_control", applies_to="this_key_subkeys", ) # Owner dacl.add_ace( principal="S-1-3-4", access_mode="grant", permissions="full_control", applies_to="this_key_subkeys", ) # Save the dacl to the object dacl.save(obj_name=reg_path, protected=True) except CommandExecutionError: log.critical("Unable to securely set the permissions of '%s'.", reg_path) # Set permissions to the root path directory if salt.utils.win_functions.is_admin(current_user): try: # Make the Administrators group owner # Use the SID to be locale agnostic salt.utils.win_dacl.set_owner(obj_name=path, principal="S-1-5-32-544") except CommandExecutionError: log.critical('Unable to securely set the owner of "%s".', path) if not permissive: try: # Get a clean dacl by not passing an obj_name dacl = salt.utils.win_dacl.dacl() # Add aces to the dacl, use the GUID (locale non-specific) # Administrators Group dacl.add_ace( principal="S-1-5-32-544", access_mode="grant", permissions="full_control", applies_to="this_folder_subfolders_files", ) # System dacl.add_ace( principal="S-1-5-18", access_mode="grant", permissions="full_control", applies_to="this_folder_subfolders_files", ) # Owner dacl.add_ace( principal="S-1-3-4", access_mode="grant", permissions="full_control", applies_to="this_folder_subfolders_files", ) # Save the dacl to the object dacl.save(obj_name=path, protected=True) except CommandExecutionError: log.critical("Unable to securely set the permissions of '%s'", path) # Create the directories for dir_ in dirs: if not dir_: continue if not os.path.isdir(dir_): try: os.makedirs(dir_) except OSError as err: msg = 'Failed to create directory path "{0}" - {1}\n' sys.stderr.write(msg.format(dir_, err)) sys.exit(err.errno) # The PKI dir gets its own permissions if dir_ == pki_dir: try: # Make Administrators group the owner salt.utils.win_dacl.set_owner(obj_name=path, principal="S-1-5-32-544") # Give Admins, System and Owner permissions # Get a clean dacl by not passing an obj_name dacl = salt.utils.win_dacl.dacl() # Add aces to the dacl, use the GUID (locale non-specific) # Administrators Group dacl.add_ace( principal="S-1-5-32-544", access_mode="grant", permissions="full_control", applies_to="this_folder_subfolders_files", ) # System dacl.add_ace( principal="S-1-5-18", access_mode="grant", permissions="full_control", applies_to="this_folder_subfolders_files", ) # Owner dacl.add_ace( principal="S-1-3-4", access_mode="grant", permissions="full_control", applies_to="this_folder_subfolders_files", ) # Save the dacl to the object dacl.save(obj_name=dir_, protected=True) except CommandExecutionError: log.critical("Unable to securely set the permissions of '%s'.", dir_) if skip_extra is False: # Run the extra verification checks zmq_version() SCHEMES = ( "http", "https", "ssh", "ftp", "sftp", "file", ) class URLValidator: PCHAR = r"^([a-z0-9\-._~!$&'();=:@,]|%\d\d)+$" ALL_VALID = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=" @classmethod def pchar_matcher(cls): return re.compile(cls.PCHAR, re.IGNORECASE) def __init__(self, schemes=SCHEMES): self.schemes = schemes def __call__(self, data): if any([x not in self.ALL_VALID for x in data]): return False parsed = urllib.parse.urlparse(data) if parsed.scheme not in self.schemes: return False matcher = self.pchar_matcher() for part in parsed.path.split("/"): if part and not matcher.match(part): return False return True url = URLValidator()