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/modules
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/selinux.py
""" Execute calls on selinux .. note:: This module requires the ``semanage``, ``setsebool``, and ``semodule`` commands to be available on the minion. On RHEL-based distributions, ensure that the ``policycoreutils`` and ``policycoreutils-python`` packages are installed. If not on a Fedora or RHEL-based distribution, consult the selinux documentation for your distribution to ensure that the proper packages are installed. """ import os import re import salt.utils.decorators as decorators import salt.utils.files import salt.utils.path import salt.utils.stringutils import salt.utils.versions from salt.exceptions import CommandExecutionError, SaltInvocationError _SELINUX_FILETYPES = { "a": "all files", "f": "regular file", "d": "directory", "c": "character device", "b": "block device", "s": "socket", "l": "symbolic link", "p": "named pipe", } def __virtual__(): """ Check if the os is Linux, and then if selinux is running in permissive or enforcing mode. """ required_cmds = ("semanage", "setsebool", "semodule") # Iterate over all of the commands this module uses and make sure # each of them are available in the standard PATH to prevent breakage for cmd in required_cmds: if not salt.utils.path.which(cmd): return (False, cmd + " is not in the path") # SELinux only makes sense on Linux *obviously* if __grains__["kernel"] == "Linux": return "selinux" return (False, "Module only works on Linux with selinux installed") # Cache the SELinux directory to not look it up over and over @decorators.memoize def selinux_fs_path(): """ Return the location of the SELinux VFS directory CLI Example: .. code-block:: bash salt '*' selinux.selinux_fs_path """ # systems running systemd (e.g. Fedora 15 and newer) # have the selinux filesystem in a different location try: for directory in ("/sys/fs/selinux", "/selinux"): if os.path.isdir(directory): if os.path.isfile(os.path.join(directory, "enforce")): return directory return None # If selinux is Disabled, the path does not exist. except AttributeError: return None def getenforce(): """ Return the mode selinux is running in CLI Example: .. code-block:: bash salt '*' selinux.getenforce """ _selinux_fs_path = selinux_fs_path() if _selinux_fs_path is None: return "Disabled" try: enforce = os.path.join(_selinux_fs_path, "enforce") with salt.utils.files.fopen(enforce, "r") as _fp: if salt.utils.stringutils.to_unicode(_fp.readline()).strip() == "0": return "Permissive" else: return "Enforcing" except (OSError, AttributeError): return "Disabled" def getconfig(): """ Return the selinux mode from the config file CLI Example: .. code-block:: bash salt '*' selinux.getconfig """ try: config = "/etc/selinux/config" with salt.utils.files.fopen(config, "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) if line.strip().startswith("SELINUX="): return line.split("=")[1].capitalize().strip() except (OSError, AttributeError): return None return None def setenforce(mode): """ Set the SELinux enforcing mode CLI Example: .. code-block:: bash salt '*' selinux.setenforce enforcing """ if isinstance(mode, str): if mode.lower() == "enforcing": mode = "1" modestring = "enforcing" elif mode.lower() == "permissive": mode = "0" modestring = "permissive" elif mode.lower() == "disabled": mode = "0" modestring = "disabled" else: return f"Invalid mode {mode}" elif isinstance(mode, int): if mode: mode = "1" else: mode = "0" else: return f"Invalid mode {mode}" # enforce file does not exist if currently disabled. Only for toggling enforcing/permissive if getenforce() != "Disabled": enforce = os.path.join(selinux_fs_path(), "enforce") try: with salt.utils.files.fopen(enforce, "w") as _fp: _fp.write(salt.utils.stringutils.to_str(mode)) except OSError as exc: msg = "Could not write SELinux enforce file: {0}" raise CommandExecutionError(msg.format(exc)) config = "/etc/selinux/config" try: with salt.utils.files.fopen(config, "r") as _cf: conf = _cf.read() try: with salt.utils.files.fopen(config, "w") as _cf: conf = re.sub(r"\nSELINUX=.*\n", "\nSELINUX=" + modestring + "\n", conf) _cf.write(salt.utils.stringutils.to_str(conf)) except OSError as exc: msg = "Could not write SELinux config file: {0}" raise CommandExecutionError(msg.format(exc)) except OSError as exc: msg = "Could not read SELinux config file: {0}" raise CommandExecutionError(msg.format(exc)) return getenforce() def getsebool(boolean): """ Return the information on a specific selinux boolean CLI Example: .. code-block:: bash salt '*' selinux.getsebool virt_use_usb """ return list_sebool().get(boolean, {}) def setsebool(boolean, value, persist=False): """ Set the value for a boolean CLI Example: .. code-block:: bash salt '*' selinux.setsebool virt_use_usb off """ if persist: cmd = f"setsebool -P {boolean} {value}" else: cmd = f"setsebool {boolean} {value}" return not __salt__["cmd.retcode"](cmd, python_shell=False) def setsebools(pairs, persist=False): """ Set the value of multiple booleans CLI Example: .. code-block:: bash salt '*' selinux.setsebools '{virt_use_usb: on, squid_use_tproxy: off}' """ if not isinstance(pairs, dict): return {} if persist: cmd = "setsebool -P " else: cmd = "setsebool " for boolean, value in pairs.items(): cmd = f"{cmd} {boolean}={value}" return not __salt__["cmd.retcode"](cmd, python_shell=False) def list_sebool(): """ Return a structure listing all of the selinux booleans on the system and what state they are in CLI Example: .. code-block:: bash salt '*' selinux.list_sebool """ bdata = __salt__["cmd.run"]("semanage boolean -l").splitlines() ret = {} for line in bdata[1:]: if not line.strip(): continue comps = line.split() ret[comps[0]] = { "State": comps[1][1:], "Default": comps[3][:-1], "Description": " ".join(comps[4:]), } return ret def getsemod(module): """ Return the information on a specific selinux module CLI Example: .. code-block:: bash salt '*' selinux.getsemod mysql .. versionadded:: 2016.3.0 """ return list_semod().get(module, {}) def setsemod(module, state): """ Enable or disable an SELinux module. CLI Example: .. code-block:: bash salt '*' selinux.setsemod nagios Enabled .. versionadded:: 2016.3.0 """ if state.lower() == "enabled": cmd = f"semodule -e {module}" elif state.lower() == "disabled": cmd = f"semodule -d {module}" return not __salt__["cmd.retcode"](cmd) def install_semod(module_path): """ Install custom SELinux module from file CLI Example: .. code-block:: bash salt '*' selinux.install_semod [salt://]path/to/module.pp .. versionadded:: 2016.11.6 """ if module_path.find("salt://") == 0: module_path = __salt__["cp.cache_file"](module_path) cmd = f"semodule -i {module_path}" return not __salt__["cmd.retcode"](cmd) def remove_semod(module): """ Remove SELinux module CLI Example: .. code-block:: bash salt '*' selinux.remove_semod module_name .. versionadded:: 2016.11.6 """ cmd = f"semodule -r {module}" return not __salt__["cmd.retcode"](cmd) def list_semod(): """ Return a structure listing all of the selinux modules on the system and what state they are in CLI Example: .. code-block:: bash salt '*' selinux.list_semod .. versionadded:: 2016.3.0 """ helptext = __salt__["cmd.run"]("semodule -h").splitlines() semodule_version = "" for line in helptext: if line.strip().startswith("full"): semodule_version = "new" if semodule_version == "new": mdata = __salt__["cmd.run"]("semodule -lfull").splitlines() ret = {} for line in mdata: if not line.strip(): continue comps = line.split() if len(comps) == 4: ret[comps[1]] = {"Enabled": False, "Version": None} else: ret[comps[1]] = {"Enabled": True, "Version": None} else: mdata = __salt__["cmd.run"]("semodule -l").splitlines() ret = {} for line in mdata: if not line.strip(): continue comps = line.split() if len(comps) == 3: ret[comps[0]] = {"Enabled": False, "Version": comps[1]} else: ret[comps[0]] = {"Enabled": True, "Version": comps[1]} return ret def _validate_filetype(filetype): """ .. versionadded:: 2017.7.0 Checks if the given filetype is a valid SELinux filetype specification. Throws an SaltInvocationError if it isn't. """ if filetype not in _SELINUX_FILETYPES: raise SaltInvocationError(f"Invalid filetype given: {filetype}") return True def _parse_protocol_port(name, protocol, port): """ .. versionadded:: 2019.2.0 Validates and parses the protocol and port/port range from the name if both protocol and port are not provided. If the name is in a valid format, the protocol and port are ignored if provided Examples: tcp/8080 or udp/20-21 """ protocol_port_pattern = r"^(tcp|udp)\/(([\d]+)\-?[\d]+)$" name_parts = re.match(protocol_port_pattern, name) if not name_parts: name_parts = re.match(protocol_port_pattern, f"{protocol}/{port}") if not name_parts: raise SaltInvocationError( 'Invalid name "{}" format and protocol and port not provided or invalid:' ' "{}" "{}".'.format(name, protocol, port) ) return name_parts.group(1), name_parts.group(2) def _context_dict_to_string(context): """ .. versionadded:: 2017.7.0 Converts an SELinux file context from a dict to a string. """ return "{sel_user}:{sel_role}:{sel_type}:{sel_level}".format(**context) def _context_string_to_dict(context): """ .. versionadded:: 2017.7.0 Converts an SELinux file context from string to dict. """ if not re.match("[^:]+:[^:]+:[^:]+:[^:]+$", context): raise SaltInvocationError( "Invalid SELinux context string: {0}. " + 'Expected "sel_user:sel_role:sel_type:sel_level"' ) context_list = context.split(":", 3) ret = {} for index, value in enumerate(["sel_user", "sel_role", "sel_type", "sel_level"]): ret[value] = context_list[index] return ret def filetype_id_to_string(filetype="a"): """ .. versionadded:: 2017.7.0 Translates SELinux filetype single-letter representation to a more human-readable version (which is also used in `semanage fcontext -l`). """ _validate_filetype(filetype) return _SELINUX_FILETYPES.get(filetype, "error") def fcontext_get_policy( name, filetype=None, sel_type=None, sel_user=None, sel_level=None ): """ .. versionadded:: 2017.7.0 Returns the current entry in the SELinux policy list as a dictionary. Returns None if no exact match was found. Returned keys are: * filespec (the name supplied and matched) * filetype (the descriptive name of the filetype supplied) * sel_user, sel_role, sel_type, sel_level (the selinux context) For a more in-depth explanation of the selinux context, go to https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security-Enhanced_Linux/chap-Security-Enhanced_Linux-SELinux_Contexts.html name filespec of the file or directory. Regex syntax is allowed. filetype The SELinux filetype specification. Use one of [a, f, d, c, b, s, l, p]. See also `man semanage-fcontext`. Defaults to 'a' (all files). CLI Example: .. code-block:: bash salt '*' selinux.fcontext_get_policy my-policy """ if filetype: _validate_filetype(filetype) re_spacer = "[ ]+" re_optional_spacer = "[ |\t]*" cmd_kwargs = { "spacer": re_spacer, "ospacer": re_optional_spacer, "filespec": re.escape(name), "sel_user": sel_user or "[^:]+", "sel_role": "[^:]+", # se_role for file context is always object_r "sel_type": sel_type or "[^:]+", "sel_level": sel_level or "[^:]+", } cmd_kwargs["filetype"] = ( "[[:alpha:] ]+" if filetype is None else filetype_id_to_string(filetype) ) cmd = ( "semanage fcontext -l | grep -E " + "'^{filespec}{spacer}{filetype}{spacer}{sel_user}:{sel_role}:{sel_type}:{sel_level}{ospacer}$'".format( **cmd_kwargs ) ) current_entry_text = __salt__["cmd.shell"](cmd, ignore_retcode=True) if current_entry_text == "": return None parts = re.match( r"^({filespec}) +([a-z ]+) (.*)$".format(**{"filespec": re.escape(name)}), current_entry_text, ) ret = { "filespec": parts.group(1).strip(), "filetype": parts.group(2).strip(), } ret.update(_context_string_to_dict(parts.group(3).strip())) return ret def fcontext_add_policy( name, filetype=None, sel_type=None, sel_user=None, sel_level=None ): """ .. versionadded:: 2019.2.0 Adds the SELinux policy for a given filespec and other optional parameters. Returns the result of the call to semanage. Note that you don't have to remove an entry before setting a new one for a given filespec and filetype, as adding one with semanage automatically overwrites a previously configured SELinux context. name filespec of the file or directory. Regex syntax is allowed. file_type The SELinux filetype specification. Use one of [a, f, d, c, b, s, l, p]. See also ``man semanage-fcontext``. Defaults to 'a' (all files). sel_type SELinux context type. There are many. sel_user SELinux user. Use ``semanage login -l`` to determine which ones are available to you. sel_level The MLS range of the SELinux context. CLI Example: .. code-block:: bash salt '*' selinux.fcontext_add_policy my-policy """ return _fcontext_add_or_delete_policy( "add", name, filetype, sel_type, sel_user, sel_level ) def fcontext_delete_policy( name, filetype=None, sel_type=None, sel_user=None, sel_level=None ): """ .. versionadded:: 2019.2.0 Deletes the SELinux policy for a given filespec and other optional parameters. Returns the result of the call to semanage. Note that you don't have to remove an entry before setting a new one for a given filespec and filetype, as adding one with semanage automatically overwrites a previously configured SELinux context. name filespec of the file or directory. Regex syntax is allowed. file_type The SELinux filetype specification. Use one of [a, f, d, c, b, s, l, p]. See also ``man semanage-fcontext``. Defaults to 'a' (all files). sel_type SELinux context type. There are many. sel_user SELinux user. Use ``semanage login -l`` to determine which ones are available to you. sel_level The MLS range of the SELinux context. CLI Example: .. code-block:: bash salt '*' selinux.fcontext_delete_policy my-policy """ return _fcontext_add_or_delete_policy( "delete", name, filetype, sel_type, sel_user, sel_level ) def _fcontext_add_or_delete_policy( action, name, filetype=None, sel_type=None, sel_user=None, sel_level=None ): """ .. versionadded:: 2019.2.0 Performs the action as called from ``fcontext_add_policy`` or ``fcontext_delete_policy``. Returns the result of the call to semanage. """ if action not in ["add", "delete"]: raise SaltInvocationError( f'Actions supported are "add" and "delete", not "{action}".' ) if "add" == action: # need to use --modify if context for name file exists, otherwise ValueError filespec = re.escape(name) cmd = f"semanage fcontext -l | grep -E '{filespec} '" current_entry_text = __salt__["cmd.shell"](cmd, ignore_retcode=True) if current_entry_text != "": action = "modify" cmd = f"semanage fcontext --{action}" # "semanage --ftype a" isn't valid on Centos 6, # don't pass --ftype since "a" is the default filetype. if filetype is not None and filetype != "a": _validate_filetype(filetype) cmd += f" --ftype {filetype}" if sel_type is not None: cmd += f" --type {sel_type}" if sel_user is not None: cmd += f" --seuser {sel_user}" if sel_level is not None: cmd += f" --range {sel_level}" cmd += " " + re.escape(name) return __salt__["cmd.run_all"](cmd) def fcontext_policy_is_applied(name, recursive=False): """ .. versionadded:: 2017.7.0 Returns an empty string if the SELinux policy for a given filespec is applied, returns string with differences in policy and actual situation otherwise. name filespec of the file or directory. Regex syntax is allowed. CLI Example: .. code-block:: bash salt '*' selinux.fcontext_policy_is_applied my-policy """ cmd = "restorecon -n -v " if recursive: cmd += "-R " cmd += re.escape(name) return __salt__["cmd.run_all"](cmd).get("stdout") def fcontext_apply_policy(name, recursive=False): """ .. versionadded:: 2017.7.0 Applies SElinux policies to filespec using `restorecon [-R] filespec`. Returns dict with changes if successful, the output of the restorecon command otherwise. name filespec of the file or directory. Regex syntax is allowed. recursive Recursively apply SELinux policies. CLI Example: .. code-block:: bash salt '*' selinux.fcontext_apply_policy my-policy """ ret = {} changes_text = fcontext_policy_is_applied(name, recursive) cmd = "restorecon -v -F " if recursive: cmd += "-R " cmd += re.escape(name) apply_ret = __salt__["cmd.run_all"](cmd) ret.update(apply_ret) if apply_ret["retcode"] == 0: changes_list = [] if changes_text.startswith("Would relabel"): changes_list = re.findall( "Would relabel (.*) from (.*) to (.*)$", changes_text, re.M ) elif changes_text.startswith("restorecon reset"): changes_list = re.findall( "restorecon reset (.*) context (.*)->(.*)$", changes_text, re.M ) else: ret["retcode"] = 1 ret["error"] = "Unrecognized response from restorecon command." return ret if changes_list: ret.update({"changes": {}}) for item in changes_list: filespec = item[0] old = _context_string_to_dict(item[1]) new = _context_string_to_dict(item[2]) intersect = {} for key, value in old.items(): if new.get(key) == value: intersect.update({key: value}) for key in intersect: del old[key] del new[key] ret["changes"].update({filespec: {"old": old, "new": new}}) return ret def port_get_policy(name, sel_type=None, protocol=None, port=None): """ .. versionadded:: 2019.2.0 Returns the current entry in the SELinux policy list as a dictionary. Returns None if no exact match was found. Returned keys are: * sel_type (the selinux type) * proto (the protocol) * port (the port(s) and/or port range(s)) name The protocol and port spec. Can be formatted as ``(tcp|udp)/(port|port-range)``. sel_type The SELinux Type. protocol The protocol for the port, ``tcp`` or ``udp``. Required if name is not formatted. port The port or port range. Required if name is not formatted. CLI Example: .. code-block:: bash salt '*' selinux.port_get_policy tcp/80 salt '*' selinux.port_get_policy foobar protocol=tcp port=80 """ (protocol, port) = _parse_protocol_port(name, protocol, port) re_spacer = "[ ]+" re_sel_type = sel_type if sel_type else r"\w+" cmd_kwargs = { "spacer": re_spacer, "sel_type": re_sel_type, "protocol": protocol, "port": port, } cmd = ( "semanage port -l | grep -E " + "'^{sel_type}{spacer}{protocol}{spacer}((.*)*)[ ]{port}($|,)'".format( **cmd_kwargs ) ) port_policy = __salt__["cmd.shell"](cmd, ignore_retcode=True) if port_policy == "": return None parts = re.match(r"^(\w+)[ ]+(\w+)[ ]+([\d\-, ]+)", port_policy) return { "sel_type": parts.group(1).strip(), "protocol": parts.group(2).strip(), "port": parts.group(3).strip(), } def port_add_policy(name, sel_type=None, protocol=None, port=None, sel_range=None): """ .. versionadded:: 2019.2.0 Adds the SELinux policy for a given protocol and port. Returns the result of the call to semanage. name The protocol and port spec. Can be formatted as ``(tcp|udp)/(port|port-range)``. sel_type The SELinux Type. Required. protocol The protocol for the port, ``tcp`` or ``udp``. Required if name is not formatted. port The port or port range. Required if name is not formatted. sel_range The SELinux MLS/MCS Security Range. CLI Example: .. code-block:: bash salt '*' selinux.port_add_policy add tcp/8080 http_port_t salt '*' selinux.port_add_policy add foobar http_port_t protocol=tcp port=8091 """ return _port_add_or_delete_policy("add", name, sel_type, protocol, port, sel_range) def port_delete_policy(name, protocol=None, port=None): """ .. versionadded:: 2019.2.0 Deletes the SELinux policy for a given protocol and port. Returns the result of the call to semanage. name The protocol and port spec. Can be formatted as ``(tcp|udp)/(port|port-range)``. protocol The protocol for the port, ``tcp`` or ``udp``. Required if name is not formatted. port The port or port range. Required if name is not formatted. CLI Example: .. code-block:: bash salt '*' selinux.port_delete_policy tcp/8080 salt '*' selinux.port_delete_policy foobar protocol=tcp port=8091 """ return _port_add_or_delete_policy("delete", name, None, protocol, port, None) def _port_add_or_delete_policy( action, name, sel_type=None, protocol=None, port=None, sel_range=None ): """ .. versionadded:: 2019.2.0 Performs the action as called from ``port_add_policy`` or ``port_delete_policy``. Returns the result of the call to semanage. """ if action not in ["add", "delete"]: raise SaltInvocationError( f'Actions supported are "add" and "delete", not "{action}".' ) if action == "add" and not sel_type: raise SaltInvocationError("SELinux Type is required to add a policy") (protocol, port) = _parse_protocol_port(name, protocol, port) cmd = f"semanage port --{action} --proto {protocol}" if sel_type: cmd += f" --type {sel_type}" if sel_range: cmd += f" --range {sel_range}" cmd += f" {port}" return __salt__["cmd.run_all"](cmd)