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/runners
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/runners/vault.py
""" Runner functions supporting the Vault modules. Configuration instructions are documented in the execution module docs. :maintainer: SaltStack :maturity: new :platform: all """ import base64 import copy import json import logging import time from collections.abc import Mapping import requests import salt.cache import salt.crypt import salt.exceptions import salt.pillar from salt.defaults import NOT_SET from salt.exceptions import SaltRunnerError log = logging.getLogger(__name__) def generate_token( minion_id, signature, impersonated_by_master=False, ttl=None, uses=None ): """ Generate a Vault token for minion minion_id minion_id The id of the minion that requests a token signature Cryptographic signature which validates that the request is indeed sent by the minion (or the master, see impersonated_by_master). impersonated_by_master If the master needs to create a token on behalf of the minion, this is True. This happens when the master generates minion pillars. ttl Ticket time to live in seconds, 1m minutes, or 2h hrs uses Number of times a token can be used """ log.debug( "Token generation request for %s (impersonated by master: %s)", minion_id, impersonated_by_master, ) _validate_signature(minion_id, signature, impersonated_by_master) try: config = __opts__.get("vault", {}) verify = config.get("verify", None) # Vault Enterprise requires a namespace namespace = config.get("namespace") # Allow disabling of minion provided values via the master allow_minion_override = config["auth"].get("allow_minion_override", False) # This preserves the previous behavior of default TTL and 1 use if not allow_minion_override or uses is None: uses = config["auth"].get("uses", 1) if not allow_minion_override or ttl is None: ttl = config["auth"].get("ttl", None) storage_type = config["auth"].get("token_backend", "session") policies_refresh_pillar = config.get("policies_refresh_pillar", None) policies_cache_time = config.get("policies_cache_time", 60) if config["auth"]["method"] == "approle": if _selftoken_expired(): log.debug("Vault token expired. Recreating one") # Requesting a short ttl token url = "{}/v1/auth/approle/login".format(config["url"]) payload = {"role_id": config["auth"]["role_id"]} if "secret_id" in config["auth"]: payload["secret_id"] = config["auth"]["secret_id"] # Vault Enterprise call requires headers headers = None if namespace is not None: headers = {"X-Vault-Namespace": namespace} response = requests.post( url, headers=headers, json=payload, verify=verify, timeout=120 ) if response.status_code != 200: return {"error": response.reason} config["auth"]["token"] = response.json()["auth"]["client_token"] url = _get_token_create_url(config) headers = {"X-Vault-Token": config["auth"]["token"]} if namespace is not None: headers["X-Vault-Namespace"] = namespace audit_data = { "saltstack-jid": globals().get("__jid__", "<no jid set>"), "saltstack-minion": minion_id, "saltstack-user": globals().get("__user__", "<no user set>"), } payload = { "policies": _get_policies_cached( minion_id, config, refresh_pillar=policies_refresh_pillar, expire=policies_cache_time, ), "num_uses": uses, "meta": audit_data, } if ttl is not None: payload["explicit_max_ttl"] = str(ttl) if payload["policies"] == []: return {"error": "No policies matched minion"} log.trace("Sending token creation request to Vault") response = requests.post( url, headers=headers, json=payload, verify=verify, timeout=120 ) if response.status_code != 200: return {"error": response.reason} auth_data = response.json()["auth"] ret = { "token": auth_data["client_token"], "lease_duration": auth_data["lease_duration"], "renewable": auth_data["renewable"], "issued": int(round(time.time())), "url": config["url"], "verify": verify, "token_backend": storage_type, "namespace": namespace, } if uses >= 0: ret["uses"] = uses return ret except Exception as e: # pylint: disable=broad-except return {"error": str(e)} def unseal(): """ Unseal Vault server This function uses the 'keys' from the 'vault' configuration to unseal vault server vault: keys: - n63/TbrQuL3xaIW7ZZpuXj/tIfnK1/MbVxO4vT3wYD2A - S9OwCvMRhErEA4NVVELYBs6w/Me6+urgUr24xGK44Uy3 - F1j4b7JKq850NS6Kboiy5laJ0xY8dWJvB3fcwA+SraYl - 1cYtvjKJNDVam9c7HNqJUfINk4PYyAXIpjkpN/sIuzPv - 3pPK5X6vGtwLhNOFv1U2elahECz3HpRUfNXJFYLw6lid .. note: This function will send unsealed keys until the api returns back that the vault has been unsealed CLI Examples: .. code-block:: bash salt-run vault.unseal """ for key in __opts__["vault"]["keys"]: ret = __utils__["vault.make_request"]( "PUT", "v1/sys/unseal", data=json.dumps({"key": key}) ).json() if ret["sealed"] is False: return True return False def show_policies(minion_id, refresh_pillar=NOT_SET, expire=None): """ Show the Vault policies that are applied to tokens for the given minion. minion_id The minion's id. refresh_pillar Whether to refresh the pillar data when rendering templated policies. None will only refresh when the cached data is unavailable, boolean values force one behavior always. Defaults to config value ``policies_refresh_pillar`` or None. expire Policy computation can be heavy in case pillar data is used in templated policies and it has not been cached. Therefore, a short-lived cache specifically for rendered policies is used. This specifies the expiration timeout in seconds. Defaults to config value ``policies_cache_time`` or 60. CLI Example: .. code-block:: bash salt-run vault.show_policies myminion """ config = __opts__.get("vault", {}) if refresh_pillar == NOT_SET: refresh_pillar = config.get("policies_refresh_pillar") expire = expire if expire is not None else config.get("policies_cache_time", 60) return _get_policies_cached( minion_id, config, refresh_pillar=refresh_pillar, expire=expire ) def _validate_signature(minion_id, signature, impersonated_by_master): """ Validate that either minion with id minion_id, or the master, signed the request """ pki_dir = __opts__["pki_dir"] if impersonated_by_master: public_key = f"{pki_dir}/master.pub" else: public_key = f"{pki_dir}/minions/{minion_id}" log.trace("Validating signature for %s", minion_id) signature = base64.b64decode(signature) if not salt.crypt.verify_signature(public_key, minion_id, signature): raise salt.exceptions.AuthenticationError( f"Could not validate token request from {minion_id}" ) log.trace("Signature ok") # **kwargs because salt.cache.Cache does not pop "expire" from kwargs def _get_policies( minion_id, config, refresh_pillar=None, **kwargs ): # pylint: disable=unused-argument """ Get the policies that should be applied to a token for minion_id """ grains, pillar = _get_minion_data(minion_id, refresh_pillar) policy_patterns = config.get( "policies", ["saltstack/minion/{minion}", "saltstack/minions"] ) mappings = {"minion": minion_id, "grains": grains, "pillar": pillar} policies = [] for pattern in policy_patterns: try: for expanded_pattern in __utils__["vault.expand_pattern_lists"]( pattern, **mappings ): policies.append( expanded_pattern.format(**mappings).lower() # Vault requirement ) except KeyError: log.warning( "Could not resolve policy pattern %s for minion %s", pattern, minion_id ) log.debug("%s policies: %s", minion_id, policies) return policies def _get_policies_cached(minion_id, config, refresh_pillar=None, expire=60): # expiration of 0 disables cache if not expire: return _get_policies(minion_id, config, refresh_pillar=refresh_pillar) cbank = f"minions/{minion_id}/vault" ckey = "policies" cache = salt.cache.factory(__opts__) policies = cache.cache( cbank, ckey, _get_policies, expire=expire, minion_id=minion_id, config=config, refresh_pillar=refresh_pillar, ) if not isinstance(policies, list): log.warning("Cached vault policies were not formed as a list. Refreshing.") cache.flush(cbank, ckey) policies = cache.cache( cbank, ckey, _get_policies, expire=expire, minion_id=minion_id, config=config, refresh_pillar=refresh_pillar, ) return policies def _get_minion_data(minion_id, refresh_pillar=None): _, grains, pillar = salt.utils.minions.get_minion_data(minion_id, __opts__) if grains is None: # In case no cached minion data is available, make sure the utils module # can distinguish a pillar refresh run impersonating a minion from running # on the master. grains = {"id": minion_id} # To properly refresh minion grains, something like this could be used: # __salt__["salt.execute"](minion_id, "saltutil.refresh_grains", refresh_pillar=False) # This is deliberately not done since grains should not be used to target # secrets anyways. # salt.utils.minions.get_minion_data only returns data from cache or None. # To make sure the correct policies are available, the pillar needs to be # refreshed. This can cause an infinite loop if the pillar data itself # depends on the vault execution module, which relies on this function. # By default, only refresh when necessary. Boolean values force one way. if refresh_pillar is True or (refresh_pillar is None and pillar is None): if __opts__.get("_vault_runner_is_compiling_pillar_templates"): raise SaltRunnerError( "Cyclic dependency detected while refreshing pillar for vault policy templating. " "This is caused by some pillar value relying on the vault execution module. " "Either remove the dependency from your pillar, disable refreshing pillar data " "for policy templating or do not use pillar values in policy templates." ) local_opts = copy.deepcopy(__opts__) # Relying on opts for ext_pillars does not work properly (only the first one runs # correctly). extra_minion_data = {"_vault_runner_is_compiling_pillar_templates": True} local_opts.update(extra_minion_data) pillar = LazyPillar( local_opts, grains, minion_id, extra_minion_data=extra_minion_data ) elif pillar is None: # Make sure pillar is a dict. Necessary because a check on LazyPillar would # refresh it unconditionally (even when no pillar values are used) pillar = {} return grains, pillar def _selftoken_expired(): """ Validate the current token exists and is still valid """ try: verify = __opts__["vault"].get("verify", None) # Vault Enterprise requires a namespace namespace = __opts__["vault"].get("namespace") url = "{}/v1/auth/token/lookup-self".format(__opts__["vault"]["url"]) if "token" not in __opts__["vault"]["auth"]: return True headers = {"X-Vault-Token": __opts__["vault"]["auth"]["token"]} # Add Vault namespace to headers if Vault Enterprise enabled if namespace is not None: headers["X-Vault-Namespace"] = namespace response = requests.get(url, headers=headers, verify=verify, timeout=120) if response.status_code != 200: return True return False except Exception as e: # pylint: disable=broad-except raise salt.exceptions.CommandExecutionError( f"Error while looking up self token : {str(e)}" ) def _get_token_create_url(config): """ Create Vault url for token creation """ role_name = config.get("role_name", None) auth_path = "/v1/auth/token/create" base_url = config["url"] return "/".join(x.strip("/") for x in (base_url, auth_path, role_name) if x) class LazyPillar(Mapping): """ Simulates a pillar dictionary. Only compiles the pillar once an item is requested. """ def __init__(self, opts, grains, minion_id, extra_minion_data=None): self.opts = opts self.grains = grains self.minion_id = minion_id self.extra_minion_data = extra_minion_data or {} self._pillar = None def _load(self): log.info("Refreshing pillar for vault templating.") self._pillar = salt.pillar.get_pillar( self.opts, self.grains, self.minion_id, extra_minion_data=self.extra_minion_data, ).compile_pillar() def __getitem__(self, key): if self._pillar is None: self._load() return self._pillar[key] def __iter__(self): if self._pillar is None: self._load() yield from self._pillar def __len__(self): if self._pillar is None: self._load() return len(self._pillar)