From 4aaa6a929180858945fb20445cceb2222b3fc306 Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 18 Sep 2026 15:21:17 +0200 Subject: [PATCH] Document workload identity federation Signed-off-by: nscuro --- .../vocabularies/DependencyTrack/accept.txt | 3 + .../create-binding.png | Bin 0 -> 30629 bytes .../create-provider.png | Bin 0 -> 52765 bytes docs/concepts/.pages | 1 + docs/concepts/access-control.md | 33 +- docs/concepts/workload-identity-federation.md | 101 +++++ docs/guides/administration/.pages | 1 + ...onfiguring-workload-identity-federation.md | 364 ++++++++++++++++++ docs/guides/upgrading/v5.2.0.md | 6 +- docs/includes/abbreviations.md | 4 + docs/reference/.pages | 1 + docs/reference/cel-expressions.md | 19 +- docs/reference/permissions.md | 10 +- docs/reference/workload-identity.md | 125 ++++++ 14 files changed, 646 insertions(+), 22 deletions(-) create mode 100644 docs/assets/images/guides/administration/configuring-workload-identity-federation/create-binding.png create mode 100644 docs/assets/images/guides/administration/configuring-workload-identity-federation/create-provider.png create mode 100644 docs/concepts/workload-identity-federation.md create mode 100644 docs/guides/administration/configuring-workload-identity-federation.md create mode 100644 docs/reference/workload-identity.md diff --git a/.vale/styles/config/vocabularies/DependencyTrack/accept.txt b/.vale/styles/config/vocabularies/DependencyTrack/accept.txt index d297cf5e..c8c452e7 100644 --- a/.vale/styles/config/vocabularies/DependencyTrack/accept.txt +++ b/.vale/styles/config/vocabularies/DependencyTrack/accept.txt @@ -138,6 +138,7 @@ Webex [Mm]isconfiguration [Mm]isconfigured [Mm]ixeway +[Mm]ulticast [Nn]amespaced? [Nn]amespaces [Nn]amespacing @@ -181,6 +182,8 @@ cutover eDirectory keysets? keytool +kubeadm +kubelet npm px sAMAccountName diff --git a/docs/assets/images/guides/administration/configuring-workload-identity-federation/create-binding.png b/docs/assets/images/guides/administration/configuring-workload-identity-federation/create-binding.png new file mode 100644 index 0000000000000000000000000000000000000000..0e107aab28985c606a7b29feaa90d25d13bc7ac2 GIT binary patch literal 30629 zcmb@t2Ut^G)Gla4kgn1Mgbx*wUX>04(nU1%5+WcX(rYLIR0t?tiXbg2p?B$_2?$7y zf)IKP7(xvtv_OD4==aaP_n-gHojWrR&y$dI_Bm&tz1LoAz3+PWn}-jy8E83aPn|l& zpsRBabn4Vu!BeMBV`$C;N9tW!g-)Ghb*{WAy@J48U3x33-&L)5BqzJ1;psPA%s5ZOyb1L{vSCBv(kT?j20?02$Jc6 z<2MSM|L=R>nQ-l-RSpN}q_r=LMilUkjI+~u3w-~4L@%gt^APLKNRv;`X~7wS%=8&u zUuWt@m~zfM$Y9E5 z4brNV9zbV~iuL9a-|NeM_1!yg&zx}BQ_YrXl@oZkFig-gO7 z8gfY6KXS5Ggpszq=TvT`*SOfrL8oFYITlf+ggC?%m7~?r$b;;_8X3e)Z~XAYRd%>x z=y7We4wIah0?hTqmX!{9LNKg7NcFhR{4HuJPFrs-BY={-J4ZO$_$sY87nLV!eK|kp z6(U~0<&ZMznXZGQfNMk>#GATJT@aBNc7jen5cS|lSj-n1JfH;}%{{;msesqELRd8>YQG_t+Pgy4H61m_Ed z*M)3$dkl^(ZYc95FV;eQT%CBD)%>3{at06_Lm8#n%F7M)(}L!@k1v&>njP^i?Kk5fb6q|_fnhu-Hp6;)gQaOFp?)bH)f4Md3^ zpy&-#ROmD0^84h@P<4xv#9>hC*Khy5npDO7$pD5wiHM^Joj%M^N(HNrY>k-V^ena$nKY|EeX?@ut z(B&D{eLs55sm&hJZ|-%gU+I{Lo?X9~M1YrPdzl{kwopzJ;@-^d$?`_BfZRgX-w)MK z@&>5H<+yBSyE@oZ?<1Wo$6G!T<1RbII3Nk2z0_U$cn9CX%?cONOs`mU@^E1dN#H8e z872g;N}==PRKWc(9bN)GP;tJul4J;vYoYKS&bPK_#W$O$$I|myI(VRyiItv|pwv&w#TCEw53wARW@_1bz2wN&BlP~~wyS})q6Xf>XAQe?eRV6blY`47@#aEX9os7C*cw}c~RH$I4LuCSFa^1OFP0B#+!oSN|2b2qoLU86N%h9q%Y zE!UqqOK}F>yls&gCnmiSM?qc19jqNonO?h+Erc?r1Q<%-+-|e>^tlC89QEs0CA^yC zA=_leQO^dnJ|3vu3z^NtZ!}Pyp@EKHsXL;Cd2$c0XHz2!ja3i#ne6)*pes;Fh<9d0 z?~}m0N^llb?M8w$G`E2(J2JUS+b~uwOp!9R{a`sQlEsoHEp9ZMpKCCl^yxOf8A9PV z^&i$)DeQc~{0;0aw0KS=Vzqg5be~e*NO&W+LY%1l6~ImWq`KF?yd6B2lrWTJue~d> zgT0P!y&TU4UJz@=f0Sc5kQ5Dby*3q0$Yq*EdAJY*DF>b?7W!v=x=b;{Qg{6Ow*uLV z?Q}k7Rz7G7-5KERUK~2I3%>F|Sj+NGx5WAJ-4L6Jof~i#O9PtFZiJ*vgys&za`0qb zs~Hp6gfF*oHG1;LtY;CeU^o*`UjB&dTy|gkr0Ss&_n^vSg;(PEEU(^x+fOJZ^@fmR zoND*tNuXNPSx8M#7e1S^XuOe62dY%3+9yfJ^agKK8Gqu4GyW00(z&Zr{>)%m%C+dX zFFUBfS{l3*r@^1C$D3j#R2*X;%#j+xFGM9|Y2lARN%PCj98d2x_z;ut|BPkFjk zuNdoEw=O5{slbzcx)pT!;9_#dDh<)o3j*&+30~dy;$6Gs>cTVqSqCyP+sHm#=%sA%fk*ZS=6WFY)bg{mH6#;#QdU|GS;JZWOeSYSpVgkFL+K64bg zb>yKz`wSegT%|gSm~2>?r=G12U>4QmgG=3q3YN&ZhmM7AU-fLZ=-w*PH_=&iI|%g3 zEK;XMzhG5OMco@h^&D(DL&@H1O3@u2C;5rOrF|vjO7rZf`8ZBYUiAEh>EN~@!-8;|J`h&7A%7HDQEX|h|rc`Ka#**e}E1``Qk}A zT#R#ji&iuF35jMt!~55jF`t)zG31i*eDH1EgAa07(IOH`RDwcZUD!JomNY)tajzNa zysxs;hqWGj&d}={K=?kp!6aD9(^!4{%p@ns{CXk3kaE>qbo^rsF`>OmI%W>%_39^+ z3K^VwYFekt5k!+0lOxQ&R;;cEox~)~8MA^m-^sSKbQtLb%%(8S1??CDE{(2(E6Wkc z?1D=THE3B?ECmaaZ9>;BgPG42oZFC)AI{S=$$`Y^9j+NsWzq6L=ScY6UT9#y(juWS`NEUp>*u!CrxS93lTbWjHEeg?X~ zB0WO@PwjQyipVGGDH#t0p?>CyK`5SWcKWpFHVK0qTE8VJ%wJD(>KFBy&478!!ml}3 z`QWB!chLN@Q&#TH->m-e8c?U5KJT=8J^V9MAQ!M&GJ#M3^U=Ejo3d)M@xxL*MacLx+xl6!RvjYYn!s*A&uSY38byDUW4=C`p@R##)L$kOaji& zQt6qK(9x7D4`}EtCBPvQB5&^>xP#AS3b}%5*rV^*>Rg_caZ+3y{VL-E*?3Pgs;s;) z^XEmo!{*PO5h8a6Y+fFyeujjMKhM3yWa*w3TB*plnb0ozTg?}>c6)#PDUX)G!{O&$ zJ_1?fPOO|0(n3cUZJo{Iv;s4^cO;jm84 zxj35~Vq27wppMDel5Q?kG`l#BROs~jr zZj?MO(loA`Dd>;u8w*uBR^!idyI-x8d-rWNow{&}KXa5EoAHFx1)MuZLWxPRp3q}9 zEAfgdWqTH8^x+!I`m#6K%Ucjx(K;6TEA0{EIL62EJ|C)%kE)ah7#bF&st4`pRi}m% zFy(VY#znV>dg~_AL;GFtb=Ii>ifg_ zdaSnmhf-$3d{o8-@3Ywuu&G?xtAYOMktmX<5_2#X8v3v`!vIqnY&J8BXACw}ryVL6 ztF)dW@KK!;p4W#;cRl)%OZ9P2A5{HtgVeG3p4T$Ff9_6V zB+J(Cu^+in#E^4mx;Dg5=Zx(SKj-J6n5*p{NODt0+@=*QVd{?BRR1MB7^eCuZ8Xv} z3)axU0`gih4Eg#GoS*xFj+$`yqK7)|t^9M4kaI7CyEdLEobhjo`?JQ;>bC5?Lq>HY;j=066MvN3ayOuHRJ_by~E@ z+1h>DBC-&o-FFH6ZSR8nQNfMrZGSDeqj%S!x%Ga`Nk$}%T+ok={Ay$-ih9k;OF90W z*5ISzkDtT(EY`9$E|2UOsFVWjLM2Yxy<^FK`*HHI3bAhzw9)RFif z@$&x?aRMWfsPI1n+uDCXc;dc>VEpg1ARTi4aD+Z^0SF=W|Kn5sKXvgxU7Regsq?%KGRtBI?i2lB4w{vKc6<6fok@L|kN&p^iw4F)M0?!BZbzq}U-xBqn-z?qsl;mhW%31GiXYU$iZ}uh7Tg@nCPYFdrT&?|XOb z_G6Ddtvj(NyhNA6HKfQ)y*O;I_?V^}A+M0BXdAM(wR_CPb8vKE+mjb;`+F-F$K@D9 z&!lo-D&xA5SMhk7j62|^NO9NiWbcu~WK~%j-{z7pKBG}qa&8YorHER$3bbT520C)r zd9In*wol#OmPk~mZ6nO&+tBR>@79bBRZ*JgIvQ1vy+zNhM$K`RLRw_a(I}I zDgRpW=zO7u%bQsCHsrEQ|92>v1allknBtqa)I?dDc(D1*5+TH;eVMuW656%nS#e1i z(snr2jgj}~D~8h%PT2h(#4Jf5=DN8Oc}ZZ~DVh&|l#iuhw}H9`bAJpsO<@6b4cb`H z@gK8U`3}iUilO*a6pd;^OxTzJL)dwL!bBBn$LZ+&AqB)kw=&izn2zO5_YlFPs=Xw4ZT&>Ba8``MlZvz>ecy@2_A;5 z>WaQSExyNnFP%HCi??7L6f6xEYBh!F)&k1H-5}kF+czUVsM$Xq$rp`a=`w(za{HRZ zS~gw=>Wq7})!^_0cYW)A$#`3G~MW5%MWLH*H;exF%XUUMR>agqu_i09Tsg(owkHGB>PhV z$En9WyejGxR7H1<-R%GgvFnmH-|> zhG+IVL3| z3zqA0G;<*uv;%i*Kb&;CJ8$e0sF*~M|B%0b8z~VF)A-X+aUz9FF90d?|@f03*AETZRw{ONK0N41eJTr5_4z)|;8q!LCl$!i>A5)mj*4#P%nJq}Kt&UgY)Ce|w;ygygQ@c)LD` zJ{HZq?O*~^wO@TH5ztB8-@E878+k%T+D^@`=Avt7Jyb*Gx_C-r???!S-0o{nS$BgX4^RfF*skSc8v`>K1 z00}t#8Ka>W*SSFpZoaE$v2?0?IMqlql_Rh@^jDq-A*nplfE~-$T|N^O?7tqg)`?+i zURQH*@+UYRv)Sw~)Y%Sgl9u`Gdv=67F01Z6;)Wi3uSMXz?{uQ?L#kAp){n|jBHpV< zTyru_4-k;*?alQCeco=y3#E_*k= zbyL^6TpMyE*KZ9~;|}R@#a?hf{B4NqfE+tcy^Cd^=8UXj>?FD_i`UC59WCYKRuMBk^ z+fBSu&cRN>(GdgV92&!&bt*hh-RmX%3=V!nk1yX8nPlx2p9#_2`@p6?*t&l-R=SL$ z&Mt?)Z~f{pG_u^=h=@BK<$6UvRG-q{R}-xEdUkv0s#ye6$TqK}f5lZZ?5?%{900Jb zf@^L3%;;5Jgh}}s9Fuyfa$V(9!LIb`baggtd1Vt;bTaPn$n4P8>Z>QqbU*9%)2AFV zKE_7(lS0b`{Bf$pxtLKmAE1mV;XJ1&ikD8Bh^B0J8CF)`PZu9*Sj4DS^!E1pc^Cv zV+8IRF7j#{i0{ILC+|8RL>-!QhyD}v`KcFw zSGypC+$-R@hSf^+gCQ2njqVg=H=lz#pU8KwyU|qTRt$oL+k62Z*qER3;kLJ#S{_>E zqnC(B#0-c?=*+t@{^)fz{hkcOq2NmWY_?q72P1mJ2BLbev+9285J9|=RJgx+LC{Gt z(U}-3Hnl%iym1>my%3=Vtv-}h;%@B5FaHCqWqbXc4?}VPeUBVwKZ7Oz`5p-3eqdId z9g#d6wnkTLJt2e4gs#VdbOev=IB3q8O&$8c3?7b!bWZz*Uy3kV1Ol(6n*UhPbQlgF zA8N)Vs6_qQM|&GB0A&X)^mbO=>D)M%zZJvChhLNp9de+I>?&I^3I4j0d{#C@v9^Ml z=7Le@BH##oRs_$d_))R^>$kpKq!;uF{dBEmu2&-^kXfkUzAT-fQpVdE;I{s3VH=&) zT>*XoF_m|w_4Uzlr?8l{P{oS62JgYNG|B(~H zcC#o!ohs{bLdXd+Zzpk5UXA9~sa{0zecX;9Xl*8JQi57zM6WB=sqKAWebWDFzBRmt z)Em^Xta_xu(fruinz0qR4Fvi|9?)RpRvbq=jSVGPO6~Q=Vq2i4#hBly;NgmgB=Y<7 zj<7YKXnh1BAZzQsK9mnmO(OHX+B$nm>BVucK&!ZGB+%iXQGa3wU`_3ORFs&tT&U8O zMyZSR3+VtElUFjqg{b^c12;`1s_Ugw+rBhCXQ{JC#wsCQ3gG-9=j9ECOEOHQe0Czn zwxfQvmBYXx80Z`-H{lhX^UFv_{WS4b%Py}WxmOJ6mi2YTmyx!e31@aLfP04mS-Szw zQcHZwl1`BKYl7AoA7x+?rki>)i%8R&zj5cwMMgo&rO^`fu^){;bJi$abFyWxvs^2x z_1OrmrO*(XVrt8&uCLOhsPWX5hA?Pu#%YtR3N^YXtP2hO@)n^u7+DiF`q8#3=r{my za~J-bm!n0ye6NXWO>M8bNf}7iw%IS}=DT+i5wOqyTSWNM@tb16lr4*EptEG#bovvB zE&jd7_tSr4BEk7@`~R(o`9FkhL59Dm@VQ!}7Xamw&;PAI)_nM{1GOuy;7JnF{zfA& z`OZm~hknGPRD6$=sJD!P?4mq7@WI|>d6w}6$4OeGt-7@TlT70NZgj{AT2Hx*juCc` zKB;T^xV#psQ=}~u^PRe-5afCf76nEHG07S9&&N(vE{cbpp${$rv9vbf^+{p zAN^k!p#LLX`}f(&*EvA|a%+((QN(h~3H4jlC}(<%rVZ6USt&`e{au~=7kerI*po%Q zt5a5m`Gj8T$ZtKyt}h9+(ovT+3&7@L-sU=SU6Jt7qfC8}tOBaG)Y$>jQyXKu`W-=G z|J?Q_2TYe?$MYE&Wa&AKImJ%mspGyf(%O6XG7f-Fw3p{g8S)u2F5MJ0=jAz?nZ!*5 zWs4Y0>HrjFviHsKq(L4t&Ka)iZxu~R)P}cW^~*}(Nz2YND%&w0yDzHRr%n^z<^3r^ zrvu=&JcKe?5!l@(*72Z(D$2VH9b2yb0K%CabdV435(n_x)OJ?t+%gL&Pi%&xV-P4Z zU_1zO;*mHbc1(WjFH}%RS)W^JE3dM5fNOecI}iXQr*@|??Z4s6js1v?>ZYdcBvp8R z$hwnxE&i1pY^Qe>aLFN;Q+E#mnoz76j}t@a-^{vm1Lm=`h%?M*q=Am8_ZH?e2pe2K zdGp(VDLs=s0Zx&cTswDQWlt!XBRHT+V3uktTnFaP4qP4HQ-CKEuw)Bv6{jPV$3YIj zuPhQ-)kuD$QE{sx5ck`de82?yq@1J=4dvvVV))d zH5jX2TUy7JA4~gfDENLuRy-L#s&5jcll>WHOw6;Cu#o#TwZ8J=rET|%Hrvh@-g?6) z`So)HV6H6<++e@nbKFy*jD&uD7t5cq120FYT#IwRjby&LMB})`N<#->0?Z=lwsY{YJHii0WFqrA~ly z#0!15KnX_Rt})q_O?@&@)s5#a{5W4pB=hTR*mjZ!ZhLp+qMqB~4xK09XjHhYEH=0G zEg1w)R>;KMYk2Y}S9UB%qxsd)(X~p8vRoS*owY{Dv`XFxd20xR;4e&-Tjeu@X#HU8_} z#4a?rSvrDeej6yVi~4a1A8lxzx!jg(5e+ba08(DqO-TH%F8RFc^FI1K;aQEG>j{em zJBdG;h#ND9WQ{FNfD0mCdf-L*?P214p(7*M6c{K_5O-z5keL|j=xF^&{0OwaEeL{~>Z^(CCGT%d7tW*HfMypR1pX zTNW}DU3JkVcP9-ph90KOF^QU=JX7-Je?)-(H9(Owrip;Fn5L6qu3x@Oc+75k%>PaS znkB}0{ah`aGs3uQKU~BlsH5!gq}Y?=K3h3uhFPlU)5;^|eDHv=S0@6Rs~G^O6_(v{ z*6ES`6Q52XXWL)gDhgypfBQA@>3{FwCjXt~rs9s3beKOxE@*#3!f%o2F`7J*>)9Qp zW`qeZTxN_Iq2{GuF84>)mKN&`Z$)}Ly3thC*<2n4AFWpSues)KC`)>^?MS!~1GaaI z#T!&(tZM6Izz|NUAfqV+VQ|8n6I0Y87J3)}Easm*f4T~>GnRYHv__}DGtJ{Sy<_LJ zSNY^ewpGVWqYVR{-!#j-TVt(EORGyWZt>a7&T_}kZEF2FE8oGGZCqj`md8Hck4-Qa^2;MLe`2fK29>(Fx z0X0$B^f0C_MUTdhKFmGvmzT&s8VxD-`0y<*o^^*GaY>8(}?9&QjPV_TGSr7^p37!eX$aUOyRnDneo}TvwQ^WG zUVq_1dqSyrxNfnyh=Hs(DWxU=rpgWbeOPgvjd?!l7YyY8$*!_0T(#D_#+ltEqGp}j z;~^w(+u!Z0+h}NAX{ovY(SWC_oo*QMZb|o+rKS(*w&7QN#aiUR^pFE5>W-+QhuK)r zZn38o<7(B}+yL97L9ULL37<})VG_og#cztz?5|{ge$v1}CYHCxLwm|lS{8iKN?=UD z#o3cbSiD8N>^P#BhmKu!%gs7cQr|E29oj$6YGnV1=<~r*=edw^=q}T9QeeLR7)PCp zp%P=YP=y$G#w=kRQnH*IAHu;nwW|e-okcNbSBjOL_0K9l+ns=pv1*YmXuN4jr}qo{*3~XYJ%Z0r0i|-)Qxy}pJ*!;T=^bScak*{RJk(+ryx7&P zA~M)G-jE!30duLVs!T)}0c88#i9oVnj<{3J^DyGR&D489sv^NkBlOIe_GXFF!!>6^ ze4M9``^QVYU>l(u#`lK^g+Cu%nXdwdn_F0m06sPjrlj!`y7ib-n1$YKg%aF+Du-g6 zmc?U-@z7K?)nFmP@YqMue&PHF#DamjoIM2t>3q|MoOTU-y5h94&Dfo;N@9P8-X*@* zcc?kO7C#;Is-AYWhqK7Nu*r$%4PUVLe zT&a;z(^N&UFW>J}=G8t9el=I1sW_*C_1T{DL~Zq-I(U27wqr2qVUgB&h~nTew@L4Q z=ZuIsctg9o$9gck^RRM2_$9aayvfM+8(P&AU7$`JWQEiSHTp(!7*u- zpLjr8gVy5>d~op~A!V{0@iV9gXN!}G=~aZ533n)kzI8L;#yP=v8BE$a z-h%LCJN-+dDkc~}{~C9~x&ls*4stLmpT!HRuS#d4W;G+`NoUjEGcI1?E%N6s`pFE6 zDrG3NOC(-1^8B`zFl1S|u_P|^VESWXgFm`-BMxSrI-yWuGnM6_1b$?G6IxC}ze-St zGTTTqOyBn(xcX~VR+$R|I7nmjfN2`mR@nPkA=B5aU(_8kwslVnwO~CgG6lfsP4s-d zG}z@(XHP`bEJoaoS}g$>jqklFDjE*)qlGLt3%(Dp7RW(_`m3M*?sJaN^Q}y@{yObV z48iO|84jeFY59u}?Np%a`M$WICTY|yE?mY2c|dmozf zcoIDR%pr8+3M%YklN{0Kz%GkJyW#O+^mN&?M==gf%pe-&bF_n2?Pt}LAZErXs`J`( zx(ebK>aVkL)Al-X_Wpy9_H|SqXWW6Ph|4IX0z428otww^Gs&Sg0Ewj8=yjG4k2p`i zNJ&sn_!b#v%AD=`!7E8W#R9O+M(v`sb?b9$WJpKYVvC|3JjTt? z=d|q=9}~Ulp@_lpOR3`v@~q6Dg6fTQ<^)u-XI&cY)$+zkQ=t^&*PQ8QWz~0z%=z59 zyTp0oo(4a2(phsV{P20SK=Z@vGAZK{F6FScI~N%XVm==!g|#2)qIHl7>Yb9o5pEFr z9Ouhx7X&*UlkV2{S#hH}0*w!i#iw)p zt*q(3g}|#I9#nctt9k8w=ta;%Tz8O-x0qz+dM8=UV_tds@`WC9)At2yKsU5%*s*Xv zmD(9FPkz8WwS#p&e%8Q}Vg48S90qckMzNP46evBEoL6R@rpRALz5ed?h+8W+%GYU& z?g1d9$jsd>{A^M}`OG6MhN|1LsJ;ZU93RboW?ZQbWqrgm7MC)Ao^|Tlj&9DEyWf71 zIverl5O(~4gp4QCaQL_A^Kh0|j{^;LaxT5!CFiroqS+8kLeJSicP@upE?k2@<%9d$ zUrkUqeN>fjhB#|B&{)uz0AdOAH?_Taj{v5PMhSCGuZq_q2yRgw3zqK&-OrpkUB4%x zJUz$>PYw_6Bd4oon3x=W2kY3Xet&L3FQ-)C4wO6!8-O^Lqk`l{&5_;AEs81mqi2Mk zb-ugYemaGNr=CzIgZ5|9xWIJ9i2UJ_4qQO_euiqt_m#E)6k??SenIH=xnvC{Z0OJ2 z%?zP&<8Ot@fP^l*?tggo^=K!`t1Ab|yCsIt-?xXS9N5cd+fkLihRiB#IfIMs|H0vA z0jZ~}BUb=A{yaN`l$gOhs;KH`<3k@*VOyKx2MK{&q`oT^;*AlETCTVlM=s`4Y4&?08Ap-Ux3D zkadH+|C+_=Y014pBcXG~X$98nO&XkrIv=fQps%lJbKFu{W=?%n{0ns2Rw<=K>u~Hu z7Axn|`N{5_P+$N)RdHdl;gZmEk$642Im-)z3vO188k+I=YbNQ#2gVmx%C2z|jOgr? zw3#t;IH)~;<=z*C*6dN2=7N$T+V)CZ{d$vbWn#8Ety;ukkMoYk2BRvU!|(|94Rm(S zv)CY2RVKdUg5WQXufgDqP}t1uM<}OC^>iv9{IkaU{ZA_}4h{Vy`%44ry&oa52MgcP zPcZXp#oCq8EUH)UkE0t6SZhFc(pSZWC`!kdyGP;#XPOi!}0YA5VR8x8wJNggt z2|FgN@xb`!7>2whZ~1r=(V$5yXuQyH9a}JaUq=^uSeQ&`{q@sraXh?w$$E+*pUW@@@>I{?@1^7I%@g^Bf)s#uT3G&)YS=$m+2J|oWtvT2&~c&I>8gX*?~DvFY5XSNCPK=mnZ>@3xyGU`0~_D zUawUe!DIiSaZi7lOvTl57<|G$LJK678y>^qz~@{%KVsowb#!vju~6C$yF`O9eErE-UwjzW!_ z@FePZaR&DhK$LQEkW;xzMhWrs``CCcu3KSeb$o9AD<-U~f2lUJcJj;H;f@W*xVZAk z2Fh@xA%j4ca+yq%5x3iWqW)onn?UC6n>@v^y&oPbY~?uuqrH|Bael;BErNz3I_cBP zoTuULjNB%iGeugE&zN3Oad>3@*$Lw0-}vZT9R#M4R`=9V?xP=Q zeomm@$|!U?&X-fg*HaFkc_A0q;3pz{jDIa@@ab+By8?~zlwpWGCwRG|K4mEAHt7pL7pHgAfH z%dQj=nWzwGaqB+HsIhY%3&i-wXL!-evrS!tmKXEH$Biwu z+`L^|R(4_j(7_VX(1F3Q%KJpr)_jG>A#p!8k|M{}O8T;EuhRN-qS9Tl^OvW0J8EJ( z{;(_L7EjKvlju+iEIB@;zgFABw4#DUW0*&|tci&8C3;K2`x1B1@|c z-|2m&m!6T=#jl@m)7-nVkPH043{aOf6w8!M%e}N}_+I--RzSzyW5cHPU~_04S&yBl zeBcZPbBe>r>(+|zQkX%3fs(6*9fx*HJ)H-APd3}PZRaHzWC5|bmsyzBfyYmJeXX{a29sdJhq_t zc$QLQ`@l5|$P@#OBo9U#F56r1C^)IX4CO2;h?I)zwh%R&2-5Df{1jkcV`!K_mUM_4 z3>{+Cd^qcGFqrRZQjGOJq5e*%*>?w7%FyTvuG|sQST*$}$_x_|hDuhp=dBkQ50Zm3 zn2x2Q{t8JBf&7>K;e&HT&h^gwbRCJYL33|jNlK?;RHdETP-*LRV4vVLKm#U{cHLQk zRe+bR6g%y3rQhA#b>D{&u{eBCeSco_qauma`*7nN?p3-Ni@8bTLu7l8Za+NC_lduW zH0d`Q^Iq{1tW6?DoMpxvKnko9(-gl5bHA6 zJ`pI@%22b&1-@%eoDp`9AHGkaFrH>THILpG$9jEeVeDMZZ)AYBjnx+6J*ZzC%0ai| zG)P6IR;VwPF?M)>^Aoilo)Mk{zY}C-pVAukD2akbGvv)pT8~JX$IaG4Hqk04Si?i+rp9?WquUgwu2FtpVn1* z+`zV;zHGHG=IocvVo)5c5=`K?wr^mp678Hz3nfUF6x}xiQ(GO1>EPp6f2GBV{gmVk zhJ;B;e!KYja>5cvF8=3^GU*d{+&&C)O;sQYVJ}Z+xYjG)9f|4O zOine3@ZD8r&v){erY6u6)I7(N-^-Ldy3)%U?_9MUEJUIw)bCa^YW1DnK3ALNdMhHi z$h)Cn>7eD2g@_RF18+Pc&IMLXV@Kr2bxD!j{7URM(mVzptQGeNBVhz_P zteYZ7OGC91gBETNQ^kJFtXM>TWL|4odFTJDLBjKK1BVB5!(IA=rYlmd_Qqc|?S5|M zZrrANrN>a-@E9r1J}PL^6eU2@rvrWY1%A`yo7{zlhn)c<^9U8lS4Y7wF2a*{zw59= z_YL!Ub*=~K(;DCfMb;a9)IB9L4g+xap~{@wsUkMk^{*eEHW1*1J_Er8T~$-AJV@KZ z+thH!T@;WJ)rgskz83t$vt)=srWd$$@Y40M4LQ)%K%+DEFfDJcKEKgZB;M<551Js? zvSOa&B^$&y?q&!jSXnA78ZDeES+m{oQ zf2g~cu^C`ia&5jXYsB~gnVyx;#>nF1P^#FgF#V3<5=LLP+zt+(OKd-d5A&K@ zX)0Dw9l8UmeEgmpQ>A)*?H6TMU$m6#Kpvi4O?irs4NmO6b&!Bxhi8>@L2ee>wX1yW zuQ(?6lWXj=Lv>#`fv6Cq2;ZlZTvXLh31-mlvt!M}jDoaek`t>^%Elp2(jM)Gj zGfUQy|Lwn-?)spw(DJy*Sz)qWX_;W=n(bsm4lt_e|1`Txv)s1JTyCWEk-|!_0L$E9 zR3MmH&wD(2o4dLrz7->DXKLT}dZB8KEPi93Vll}tx3V;-B!j8fv!nb_Zmf^G8s4`% z?_6QEZRztzATDc?L;6_zjBM^$k+YrsrU|e?4Ub2{e7XMC8Q`@5Ve7O=cS1sCH!z_DuQUad|6>`vqeDZg!?pj zSFsIH)mkrHYLOU{Yx#QXS%9~68(d{>s`)n(r&j6TR#G371c`~3+neh7eVbY#i^6UE z>PPs5QbQWD*Fji-h6?_(kBS*-F_V8L+aoonesGaUS02O(8Y0}KlA3P&eWqhS-xr5; z+B#*iF{{p>=g*$H%6Bf;LDDczUxA!18mSJUe8PSGO$=^!!BPAtxyj-{B$IY&+ommw;Ouw}9SD);3<#qoJe7|G#%cMCIdIu9sB85Bm%rIDo z&;L5u`M$HO)wadicW{Kw6kMn7whjRLZV2&wMC-9;ZB9W zI6$>Sv9AHG$NmGoTkfTRe5Y3G>awMED>v4CF;3h;a=LORbUSN1aw_ODsHiNgG=$w) z89TaifHFT{pU1YQ?tL|v2iWg3`eoK@)(3%5B+Ea#*P#Eh=B*NvrSEA7BY&r}+K4Y= zQ>(%Gr%*Dmy`eg=+0e2uOwwS3vro!M$H`BOz!{8{ZYo;ggC{@jD=dPTf)#<@F0n4o zU&4Gfp20hU%gqJm}Q zdw`W#7%!eW8z_U?V-**hG7%Y!PkPZv<7B~Nx5H!*;gnv4f4DGS?xgUbzbMkLVbAlW z!?;M!52o`n=@AJaN*h&rKr~?_Nboi6+H;c3nuojI{@OFsP1?8+|{|hYts$wO%o~~+ijHc3Tr-C z=-4Wf&y>*$4h+V_xE5zR*v}z)gnwqb17X%A;QBWCQ&ZXlB&&7~jYb?~asF%?XUA>|CAnyXaFe7mTeYi} z9~Wb(7Sjko#T3HccUKVkb0;{5y42!<WveEnJ#t~itI!^x0;xKVMU`>^S+Yp|9LL|&(>mqGRB8}J^+{Pv{%qaVIESN+xL5&6Hqo_VDB zda-?=Q|5NQr|s<5zSOq~mJLA`nR#PZ!_ExfLVBG3ZP)KiJK9oAEPzbOeHXmo4Z4GR z?0pemW?|__Yl(X8#}1OifgqDl0h^c_@Y(}CGbR)IRD1&-TaAT?)i&k3`=Ge8-gTy) zW-sw+E4F-Kvrw!~dmvgP_u{fLn4>Wxf==W>Wg3U;E8qYXWah2+bSk!f$tn7qY?jON z!SOP*(H}~3>6H6UoZ7l(@`xQgsZqy-kKdQ!w=yKVkYDmxJqPAhGNgiwvwp4#u)lKR z1>Y+uT6h%$>;mOmon)}nMtZOIgm?+{L8|~CoR*zEgeXVq{1W17rem8OfUnT2A_-%U zuJx)eBbW>ekj5#6MawQG7Ja++an5$nb@6$3_cx1a1!1^+XBL7Wfetd!gcg2cg1IEL zS}1&F?pED2oQ24i_l~8EUfl_8ZtYP;6Om|w698-e7 z!5g7sMlaa8rX(XYJWv)k&_XB2K3o%fnA&@4vb~{a4~T`$7X(YpXx}&?azCU3kq<_w z#Dc(K+qO1N$AChx6o$UtNc3(pf=bSimk*kd9MrX3@UGNKM!wdLRR4aRKphPy;N_;B0D> zv0C>ObNJvppjniU$nbt z`+=E7cvCY*e8-*|o*v+RCa0qfx0^!7KM*g*AZ-@kr)1MKjlwur9xmsFYV!Krr#Rx*UY^=T6ki?+3nxsYJe z*FzUYp($DPRjh%7fal?OQwOf!qH&vtpo;^idsV#=arHC(S8CvkG6TlO=@;V6_5#{DX+dx6Xtsq~F}n&2&DB{AO)>S~OS= zJBZ$E@8?Yu{nS6yDZm@2#j0SOQUg<+r{UnXDtiR04cRTbhO7v7l7~fzql*$izM`i~ z>*)1UW{K*wlik(EWr7`xH?>FhWgGL1EasH)acCqi;7Xz8(?@F=3EYwEVbg?Tk>=;=#Mk?xuQ`&!U|K)}oejAaX9@VaFe-Gomq{ zD4E}K*PLtN4xYq(XK!HNJh6rF&=&w1*AFt5l@NU9d7gmjLGM-KMV)Q~(cIuZ;*5T= zuSBo%L4=eA>?V~7!RryAhMn8|Y@Q5cV<$i(1FU!pk-S{APd90)#8x0wZgNoCxEvw? z2v~-!OxTqf)km%l_`I;xv@J+Cdo+UjU~xPUzFj2bKd|*AXe`b&51sGia6nol1#F!| zthqsq4UBGcmlzdye`<7f^J}uwbZD@EU!E@2zV2dQbqJw=fYQ*1l;9z&(y|U*$oZ`v z1B0pL%Ps$aoR{AYB^~x=HMdw|vrDue8KU9Y_aVNbg`6}0M|IyB*5tOWyIhJ$7Xj%Y z(hNwI8k&NXfPjSF1w^DvuK^Y5Nbgl4H0iyAQk53zB?yZeAoLzu?iXBV?|sU1?>*;Iit>dC7o)vG@&n`V_P=0z6d_p@d-d^19eSQn*fG>w z@dZ4QYA;uXs1a1HvPTG~Dps9Z=B#Pd0A+=@)}yhr#mzcA`EiWGEb`z~CsFP;+bQPy zsyIK5mA)WldJo-(qE2JBt-CtL2NL8S^#+*sL@*v%zNinARuAvixvqBPm~>&j#-;B> zMQ`-|wd57*e6}^cpT)ULdbrXtfExjHVt^9ne?~`|rbf4KYvjuuxYVC)ZY(gvp{NCx zs)OJsWcdEi(R~NysX*Z~<{*~oZfY#Qb4#glzuJyo1{F~FQ^|wNW}P7Qe@!Ut6|F^erVJCd+M9E@f~#skOwENn(Y29}SxsPGnJkVT@J?`TEl|fi z4|weK%l0fCQK>%6OdoOPG?8oOZl6`bN0W4f$HAvZT{~JEil{Ih*?CuEebTqyTnbkB zM9igzK-In-*fD#FLo}o4o-WnOQrZMVk^N8u=yolY22?Cma z7l6rkxHc?FJLtsgu-)lp+g^>pKBg_x=RJ#+F-qja@w0-<02U9GLgTfi&cHfigld{XyiBs%lNVg-=x+~#tPb;4%0mIRF#*$kaw(&BHW;5&FebW z@+|zhpZfOcB#5_-oNH8TaKt$b9bKNW(F5=b!?rPfIXcudtz#qA4NX@X5}SSts}tsl%Me|J^G1~`AoTir$rVJ;AnCMzwXiWoWeiJ z-7;>pHie~Z>>q59U3(w>4Xtye(O#j7&g`8G4e0JZw2~OSJoln9$=_rzCLbP_yrm5K zS}}V=eVi}B0>FjU0C2>3vzjnVLiWD~PfArBBg+aOJL}4POlx~(m1L##x zrf*2*-9#;MD`Th%X&=&OdGm0M&di^G0FJR`{gZTi--`Lx^Kj9vlD2%|FjzE#517#H zkdHNPRuUt7G>FR=86!@R*TO86L(?&`1W;SW)`vo1g&Qg6v=8pXcbdt{2p-)C6XM;P zn08LvWQ`O>5VC&=0W>ql#cmIuNS^V{&1N4+Dtl*Ts{Y6am>N(Y0R8q^*z5<&qxah{ zyRGv5R6;gmr(vJ~#W}59M$ZX&mPbmXF)xFSeBP zt!{hzvDCOOQ7JVNX+Uxh1C_;j?iQL4R*@EFKVy&mV<;}Vz4ET;iCd|;u8;u{z;?O) z*fMdVD_VAPUYyK?qVeR}xQ zLBcz8YKcU8)R?T&yxW+x4IG!W$7~3YAIay7}(t%2d)Cy&grs*R4!bNGllu)DtL{YES7WH9-TTL4t69<2#8Up zD_#2pl7@iGcO}WL?5hlC3Hi89%@{Nt{2-5RE6bG^)>cJ2{hR(^fyxbS7gND?Sx_<{3$Si%Y*NaXO6 zc|~W&ZhD*}52+WUgfi>IXPUNMW3vBbG!!~A{k6>LLPpIJcsJhnB#DGaQaTENh>l{x zB5<~n*$QmsI)o?^7_h6*>d6&Cbgl1y273J;Uicfna|;sH$fmvk~7^sjV3RmkCD?2X5wcm^}a(eo(lh@29>4 zAarY_;8C&3J-_!gxW+BH@{JrU8Ml^OK;NIouDGLij6Zs)N`ATNd+v>? zHxlOzBjk>%y>~LB1sw-UPfsY{-i%74XMT%11fia-3Pz!3Pa$Qpy#SMY5gmu`kuhh% z2fHn@(IVK&0XK3U2{7&kRJ@|vzicMC(gGTY06GT%0(@G?WvCaCmVq)TeErP+ zLFeu)1$3o!T7wx|;=(yDN5~_5gU(N(UX1L$x}z*%+xggQKQgIf_eu95l3igyh3Qq- zu6oN+;T0-j{WGwkC5f?}+{{Urm$%wZ56_!qqDq)0_jgyU{5J##LabHVd)h9xPv^9O z_ubXKu4+ls*lr5`RnqZ?@SRUEZ&P9rPA1_dd;vi|lDl!pe4&!uaNc|}d*yg)6aij{ z-%*n3Od}v2(fRn>KGTO#)a<48dDp|ddFoaZ&ux@|(9fs6#v&W&{up0 zRD*QjJoHzQz<5o3^;;DAyrkiGOK^G&t`VR9jcyN&X8h-n8Rpb}-j*|+F7gcNPdq1S z`NB|^kbB@}y2xu!8vZv|a21QMg~Hbnv=r#m&0Aa~a)elV5qD)`E?WeWQO99sC=p;i z2>vGMbG#?~&9<{tf?OUkw~-raPLo5GbTC1_3YIy^L=OahYs{U57l|Mfsu1*C3NA}jzi=DLO>yBr@Q$d=2?4!Qe5uhq`&~$jJ_#$ z1!VTUT)+$+DYe^jwbzHqD2_t)>ooxb00FlkuQ7;3M2l=Dj8E=N)^84)-=kURwr8kNZOIkg% zUIeNTY;K8~YV?!}Q5lpXj#4tMGHdZ89g+KF7h%+&KR6-Xw>~baNqRlzY2F~o;pmU} z2VoUBydgirKAB&!Qb(uq292KkOWBRW`F#)Ir)Snxtu&(M{qg+H=Mi#J`8NOdHOlf~ zIJd}SV2J^+7}!tZPeOokiQ5nVO$1mkISFV^%;KfBo_f0HQT-c$dYC&Oj7H~2-Lm?a zsZW@$ma5(0-?RZ^r;1<9l1)xxT2dGMeF=cV9)F&2JNkMf;AlnNX2~q`-U@L8w zCky;*oJn1bB@5kQ{lUkgX{jd0*4iJ~_p$(s4Gj zgokd=n?4>4F=4>!?7*Y|To(V!h4b<^zc9JGzgSjxFT~44q9|FR9YQiVS1SIuQL(fkMgo;^~O!9B)-?@V(BqF=(Li9qRgM#NA&8Cp>vOTA_%M_(yG z(8n`4kZ2QEmE*26q1mq8S$;E%HxbmLJIf@p)o_0>oQRsLR%kmA?a^ahf5wmFCZ3Cq z@`h;M1?qUmri`X!OfsURoO%xnd6IM(;bErQc<(IBeR-L>=6!i#UfSBJ0NWHNrK1O6 zzUrlmY_P-C{qof(U2-Zn&q5|#Y>+ZQRjOWn(zF?i-TXyR%&wjC)#(oBBXBpKM!>yK zX7~XcH(pmav7X(wB)=6wr1n&VBrN-B`IB2kB-ae!1ouRKt-V5TxypH5Cm+>V1lv4+ zWt7e8;3WU!yI6s^Cf0kqY5Sl7AmpSfx}+U%FX12S{4(s*%^wsu&G>R=uHP(uGU+iU zFdwGHm=lL6m}@IJ^_?wDhV_FE$3#mfZ*%97<16^nklc3{Qhw&&=)_lHYA18%yW#og zcV~=AXDUtSSsu+UZSP>r*yYe4OvVugoB9Kb$qNNGJ}C-Efws4aZEoQIb3qs^g=8jbFb=|~8 zJ;n1QsgwFf;?Cm`aTIkKh)m&v-f%A1$nR{L=D%V9evM>CMhctSX7MDco#UwNYi0T&3xcHF^c_+ zer~?`gZ;x1G8?_SZkC|wMQ3kmK1E*|$W)<$RRnw#Wmx0L z#>e22Cp0Uv$6{o%82%n+{Q~QU8Yrm)~hFm zwWh1FQ*p9>JV;?~aejjZk^D;Qapd7DBi#~4uQqCax8=I%Q1ev3NL2_718x1teg8?W z%a-dVow3P?r-FrQp!bTG?kJUzQS0Yb4$EcE{Ecy42dlpO>uzRZt9KDpwJ0>f$WWPb^E%Ec->I(#HuerK@&pF zL-m!ac0NLhY*ZE74V9AaUGLqY7D&ibX{KXtK^;30>@P?ima2_&)SNwu)DDP zA~ApLH;5%#iucxJSg-{-@si8*N+QSZ9-mLT+Or}B9>{2)=bXn|#+{H~uf=0CRn3`L zZq=yk++}sxP}h`w9tnT`YR@Hzl!399${^hlsfm_lDGI<3v)-PeKd*iZ4gfr|nLbAK(R`bium*DbDAXE4=u zDzUOc&pj*A?4iHhrgl~`EK7Zt5J7KpW$665u5oFYXzsol4@|3!l>VMg>Ky5me;gK}ZN`k{^FP-P0g5e{9idYu^2xu&e>v z?dQ=IQaR!lH*i(TJ`TPvJ`n}ty^s$S4 z+yrnN%L^R@wP7x%j3LUSpnJ73%_D0eg5wE){488v=R9MCu=HN}Ej`Y%De6H;>3X+E z>cXrjWZu^kMkO)c)o>@&Tweqf&el?4>|T_RcQRMZ0JvXo#gaA^jd(F3*-F>XG^LiM}ZpU-F*X#=6luUMmyB*u7_5h`Lhm z#%Iv!b)v!J`jAm=8P~4&CoVBLerow(KQ^+5+%nyxPBCy-MzZxfn&t0N_^|l;uYrxT z%#LwqiE(Zu4@U)NWd}424>wg$^N5J<%K`EqH?xboFJ$rk91fC9Q7N zw}j;S_Ks$$p0elf%&fqYn93aTTtW8C&dhEOrq?f}b+0$vr)8y87u^!C6_uIoOfLGd zn@I}l2`Dn`Y~fajXDt1ah`wcss%-8_x9D@8wG|#mAN1qq6;W-D$%U4`vs2<9(uh!XEOJErrn-p-WQ@N{l z3nI#RgU$_{0gH=2ze@ndk2%ojo|G|hk?t%Bf5GfE2V3-?KZ3)!=xuA{h?Bh>hjQ;G1Mhm4FlzD=nPzj}{9f_Zpv<2fi=pPFdK z#%7(xe>}&BWnkahGPj>C20)AdmE7^Rahx zEG;H$?A6zemF-ul__%!FJKGqKGSrna<^a9^!naD%$@Bgyw&-!}vSIT3%LkK9j|BFR zEz^;T(gq34QJ>jzxjc;04YHiy=ea{uOGRGC{>f#dwzKM~+2HiPXOlsQO`y2XNA0LS zRjorUkg~5=qA&d%uv5&>nE7i`vvt~k&Rmi+`qB}DB`xtkKfT`FXnt1seSg#bYONp3 zGiR_Q(jIq?*Lh_~xzG8VINec6q({nG@oo|E(bxLcryyK}!izRj~0i>GkB^GT=Y#D3EIUFTm(9 zkt|9Vb!S}QMe7eQcw3tC9=pG`Wk0Y;Q(F8W2XWfT@#c2enriE8sp_+9*CKKH5GfL7tX({_U@C|AG)T%tH-Flpg*-` zs=MA3{jtk1_)xP>Po&`ft6*O~=RJ9}b} z$CRUkZEu^Ly!x}8s;F9;gpAcYec2;aB)W#N?Y3YiI|_7&p=R$g3tyCR#rFyM`xF_u zwTJONr96<&L+pcg_;Ze@#7KGkgw5<`g@DYY{aP0@SZusB^2QcB%0;zpHqJwK^=Y}3 zB)r!i>J=Ny$79M-A!uX;#Dfr0iXUbES!KOVIap|5)^ie*Tcj(<9V!pP%&P^H_ z8(-T<&45+9-FNz_D7}Uqgn;k)${FI0y6Pcb&m$~edFknAp1YmWy}W%Z`efr>lDx!k zq<1sUQ{rnEzo80k%`boeYocD8{;4FRI7zo=TlM1RT7yjhC+J_u>>9xGY`POIo%E;EF>`FEc*l`9zxGQ>Xxx{XjBC8d>B{*ey@KK}CdJcIo zb?odF_lxX6Up+~B&QW43&RES9mRM7|Nf6&xTYqG%kW^dQS=qFbFe00!hdS;2VlRln(Ck_Kc%YO?c`$T0yjF17jW@n0ApE^kUC&EZOYxYzG%Fs6wWf^Lv@>^L zP#0`v*%dOK>~!b@18=?Cc3w{59{Y>XDSoQW9O(6wOLyp4iGT#*i7Li zrZmZIj4i$L^WS5)zxVrzLKtr5vG5r4SX`~n8lHSrSfT^{hsT12S0;!R32B8Wr_AGu zPs3WaYEY4>57^>?-{!gbGaVjJ@#1$w*ZKb&wi>l*x*i32H0uRP^;(}1+Ls#9tHE}@ z^Nbg;shTQ|8}36i2(&DmBlCHVvc%#m^r^4bML1i|gp+eb>IuPO#cEu^PXT6h0UynJ zy*i)8natGF-;4H<;fXcBbT+j6i;&2B!Cw^d<)0!IX>)k5=4bQr{cv`e3T@Hw8%1Nw zh{mhQ7Y;o~8C|z5R<^_kP8l%K)ITkIj3|2b5_(C2bPBft@OmQ~IN~d~5*Psw4A{b& zFrLvx^?`*pm&B?dZr;O`4I z+t=Gf3-QfTFREx`E^zS9>jr20LGOB{!i=Ty|C504Kf*7r>>x70^stPu3RM~ZDHa*eAi+G~a~ zCzev!dF+V>csUzEyzdk=Ya2d0|pmumc8#d8JS~%H%+A@4tv<rMFk3c`>Et1-E?0`wMbs^99ENA8HDiALM(+Ctab_%d1El=iTo|Judca90s}4cV_B zKX}pKVm;rBKhoT^t==>%r#aiCLdCqENPnc}1 z59U&P>7Ujn`AZ9i8W}sNaxmVlc97xzchjHX^%A?d3~eJ;<@<%1a?G8(SmRrpK_}%# z^&4jT9U(c_pR%pUV6>F|&-XRgDv?|2D3dxN7}lz-EG{xx{e8O?Z>y!76m}@InVpEkWi5*s5|%0v%K!Gt!glhfU~=YR%dkIshextWSl zPDK;?b;Sh~$uQnF)|R98io+}@`vW#keBB?n=Qvrsw@bK#YxBzl6^ySA(rCd(^0C`C zb(=0bMH@}3i8ngfQ5m~;sZKiks9hF%XB+aE#w+xg`O6*hOMyQhp{t9~6Vx;H&iq-O zFUOLYN|L<#!uTd2s;#Y#*d5)qqcPJm(BxRRp87v`ERCJ74Ycr=l%}=D9k9o=UDE{kfs|e>cRM)k{Y> z^ZEI)+(=sUF@;B7TG0pdDHJng4l`7?Zlz<+1nEu39Fxp4AHJhs?_65icU0X@b}_sF zw@E!b-o~92%yy=dip+7wyB@#&{DUz?<{}g@Qo+SuiIHAmvRqwben~!RNN-Wqhowoc zDQ)s) zg24Kf6Nf8U_B8Wlvd+`SI(vt9&O0t5Qu*5}R=Bak@$e;9_K4`Gv3px8cgM?UwLwWl z3eGeTvxbHu2mkU+nJ@@p4kIk~Z64dQsZ7m79zDzp?}_texn#s6o`?veW>0PJV^!{9 zil_KZfJolW>!(Z-mx+CALj5PgM zazu-1`qP?LKhHvxeX~%i=F%f8{cWO*m$T@RSo~J~vLYjYnl3gxNls_?4i{!NDmI#C z2W9B&o3RY*-4+W(ve0}jpMu4T|UR^$wEt7#&yAbWajv|XVlie)- z1KZMHkIG6Y@R>|db4b*T4pxx5?R(9efeCchLyp~(u|@SGHK->x(D9$A+!U z)1+!o>A)IZ;@BRaQS}$Uds&^KrdaS_y@%LgRUkb*{foMO9EBl^rM?s43^wUoeb#aCQz+`jim*$g z|J{bDe?5Qy=P+4H@P8YxOB7=(e{ci}KDo-}Qr-U<$yKExUh^x+lHWl{?mq_fWUuu% zC?gki{lAO*{hlax4z64-rq0|5?X_jpjGkLRHL=@hemq-Cze28hQG$1Slp2X z{${fdYj#CXsB%EO%)DobOhl@010OHkDK3Hc!||V7Ie|=RZK@;d#sh5RR1mbJX@mcu0v(Yv zZ0qj7`17EA(Jwf!g;ZMFz6BX^5N7Y8-o)gpHEl6oVU4;@S~ll+(MW4O*P*VK*7BjYSu`AuJY#|)kgi3>>xkB+F^RElk1Vfe)6S@ipmC7(;!7aM z8h$XX6c-O8l>lwRzK7bfe10Jf=^eyp5I8wsk=Urw$$lsGidj@i#H4X-aE;w?ODwtP z0QkhPO#~8?iNu+aPM+R6wz5My7_RG3*JEIE@P%<5!v;cb3j6GMDX1TbA9U6vetZ2x zr-`Y2+^TkJ^V6wM6NBeVsXHC6ag=KfxI7rHQ60kpLO1@h-DIQJDIO|)0qcKPv(*&>grIX3xJj(?0q_Riig*Sqg6p5e7X z?Iw=WBrZDoAiCVH@vwC5@KMyiw4{&!?xZi1EO_I^gGb|A5G=JuQ`e H@&11SnC~mv literal 0 HcmV?d00001 diff --git a/docs/assets/images/guides/administration/configuring-workload-identity-federation/create-provider.png b/docs/assets/images/guides/administration/configuring-workload-identity-federation/create-provider.png new file mode 100644 index 0000000000000000000000000000000000000000..375eda562dae17c781472159e3113144cbf05e78 GIT binary patch literal 52765 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 literal 0 HcmV?d00001 diff --git a/docs/concepts/.pages b/docs/concepts/.pages index 91150b96..846b3335 100644 --- a/docs/concepts/.pages +++ b/docs/concepts/.pages @@ -4,6 +4,7 @@ nav: - About projects: projects.md - About tags: tags.md - About access control: access-control.md + - About workload identity federation: workload-identity-federation.md - About vulnerability data sources: about-vulnerability-data-sources.md - About KEV data sources: about-kev-data-sources.md - About vulnerability findings: vulnerability-findings.md diff --git a/docs/concepts/access-control.md b/docs/concepts/access-control.md index 0c7ff6cf..e3481ad0 100644 --- a/docs/concepts/access-control.md +++ b/docs/concepts/access-control.md @@ -18,7 +18,7 @@ Four types of users exist: | Managed User | A local account created and managed within Dependency-Track. | | LDAP User | Authenticated via an external LDAP directory. See [Configuring LDAP](../guides/administration/configuring-ldap.md). | | OIDC User | Authenticated via an OpenID Connect identity provider. See [Configuring OIDC](../guides/administration/configuring-oidc.md). | -| Service Account | A non-human account for automation. Authenticates with its own API keys. Available in 5.2.0 and later. See [Service accounts](#service-accounts). | +| Service Account | A non-human account for automation. Authenticates with its own API keys, or through workload identity federation. Available in 5.2.0 and later. See [Service accounts](#service-accounts). | All user types share the same permission model. The authentication mechanism determines how the system verifies a user's identity, not what they can access. @@ -33,10 +33,19 @@ A service account is the identity of a CI pipeline, scanner, script, or other automated tool. Like any other user, it holds permissions directly or through team membership, and appears in team member lists. -A service account can't log in. It authenticates with API keys that it owns. -Each request made with such a key acts as the service account, so audit records -show its username. Suspending a service account stops all its API keys from working -until you lift the suspension. Deleting it also deletes its API keys. +A service account can't log in. It authenticates in one of two ways: + +* **Workload identity federation.** The workload exchanges a short-lived token + issued by its own platform, such as GitHub Actions, GitLab CI, Kubernetes, or + SPIFFE, for a session of the service account. See + [About workload identity federation](workload-identity-federation.md). +* **API keys** that the service account owns. Each request made with such a key + acts as the service account. + +Either way, audit records show the service account's username. Suspending a service +account stops all its API keys and blocks new token exchanges, until you lift the +suspension. It also ends sessions created through federation at once. Deleting the +account also deletes its API keys and its workload identity bindings. Service account usernames always start with `svc:`, for example `svc:ci-pipeline`. No other user can have a username with that prefix. This prevents a service account @@ -49,6 +58,13 @@ Compared to team API keys, service accounts: * Give each automated tool a distinct identity, even when tools share a team. * Take permissions directly, so narrowing one tool's permissions doesn't need a dedicated team. * Support suspension, which blocks access without deleting API keys. +* Can authenticate without any stored credential, through workload identity federation. + +!!! tip "The intended path for automation" + + Service accounts combined with [workload identity federation](workload-identity-federation.md) + are the strategic replacement for teams that exist only to hold a long-lived API key. + Team API keys remain supported. Start new integrations on service accounts. ## Teams @@ -65,8 +81,9 @@ exceptions that don't justify a dedicated team. Team API keys belong to a team and carry the same permissions as that team. They authenticate automated access (CI/CD pipelines, integrations) without associating requests with a -specific identity. In 5.2.0 and later, [service accounts](#service-accounts) offer an -alternative with their own identity and permissions. +specific identity. In 5.2.0 and later, [service accounts](#service-accounts) supersede them: +they carry their own identity and permissions, and can authenticate without a stored +credential at all. Deleting a team also deletes its API keys and any project access assignments made through it. It keeps projects, user accounts, and API keys owned by service @@ -205,5 +222,7 @@ and holders of `PORTFOLIO_ACCESS_CONTROL_BYPASS` until one is assigned. * [Permissions reference](../reference/permissions.md) for the full permissions table and default teams. +* [About workload identity federation](workload-identity-federation.md) for how + service accounts authenticate without stored credentials. * [About projects](projects.md) for how Dependency-Track models projects, hierarchies, and collection projects. diff --git a/docs/concepts/workload-identity-federation.md b/docs/concepts/workload-identity-federation.md new file mode 100644 index 00000000..bb7d1d53 --- /dev/null +++ b/docs/concepts/workload-identity-federation.md @@ -0,0 +1,101 @@ +# About workload identity federation + +!!! note "Available in 5.2.0 and later" + + Earlier versions authenticate automation with team API keys only. + +Workload identity federation lets a workload authenticate to Dependency-Track with a token +that its own platform issued, instead of an API key that someone created and stored. +A GitHub Actions job, a GitLab CI job, a Kubernetes pod, or a SPIFFE workload already holds a short-lived, +signed token that states its identity. Dependency-Track verifies that token and, +if it matches a rule an administrator configured, hands back a session for a [service account](access-control.md#service-accounts). + +The workload stores no Dependency-Track credential. + +## Why it exists + +An API key is a static secret with a long life. Someone creates it, copies it into a CI system, +and has to remember to delete it when it leaks and to rotate it before it expires. +Nothing about the key says which pipeline uses it, and a key that escapes the pipeline keeps +working from anywhere until a human notices. + +A platform token has none of those properties. It exists for minutes, it names the exact repository, +branch, namespace, or workload that received it, and nobody can mint one outside that context. + +Service accounts and workload identity federation together are the intended replacement for +automation that runs on team API keys: + +* The **service account** gives the automation an identity of its own, with its own permissions and its own audit trail. +* **Workload identity federation** gives that identity a credential nobody has to store. + +## How an exchange works + +```mermaid +sequenceDiagram + participant W as Workload
(CI job, k8s pod) + participant P as Platform
(GitHub, GitLab, Kubernetes, SPIRE) + participant D as Dependency-Track + + W->>P: Request a token for audience X + P-->>W: Signed token (sub, aud, exp, ...) + W->>D: Exchange token, naming a provider and a service account + D->>P: Fetch the issuer's public signing keys + D->>D: Verify signature, issuer, audience, expiry + D->>D: Match subject and condition against the bindings
of that service account + D-->>W: Session token for the service account + W->>D: Upload a BOM with the session token +``` + +The workload names both the provider and the service account it wants to act as. + +## Providers and bindings + +A **workload identity provider** says which issuer to trust. It holds the issuer, +the audience that tokens must carry, where the issuer's public signing keys come from, +and how long the sessions it creates last. Providers are instance-wide and only administrators manage them. + +A **workload identity binding** says which subject of that provider may act as which service account. +It belongs to a service account, names a provider, and matches a subject, either exactly or by prefix. +An optional condition, written in [CEL](../reference/cel-expressions.md), +narrows the match further by looking at any claim in the token. + +Bindings can't point at a human user. + +### Subjects and conditions + +A binding needs a subject even when it carries a condition, and the reason is the shape of shared issuers. +GitHub and GitLab.com issue tokens to anyone with an account, and let the requester choose the audience. +The audience isolates nothing there. Only the subject names the organization, +so a binding that matched on the condition alone would be open to every other tenant of that platform. + +Conditions exist because platforms don't put everything into the subject. +[GitLab CI](https://docs.gitlab.com/ci/secrets/id_token_authentication/), for example, +keeps the deployment environment in a separate claim, out of reach of a subject prefix. + +A condition is part of the security boundary, in that one that matches more tokens than intended +lets more workloads act as the service account. + +## The resulting session + +The exchange returns the same kind of opaque session token that the login endpoints issue, +carrying all permissions of the service account. + +The session lasts for the provider's configured lifetime, which can't exceed 24 hours. +The subject token proves the workload's identity at the moment of the exchange, +and short platform tokens make that gap deliberate. + +[GitLab CI](https://docs.gitlab.com/ci/secrets/id_token_authentication/) and +[SPIRE](https://spiffe.io/docs/latest/deploying/spire_server/) issue tokens that expire after five minutes by default, +and a CI job that uploads a BOM and waits for its analysis takes longer than that. + +A session outlives the token it came from, so deleting a binding or a provider doesn't end +sessions already created. Suspending the service account does, at once. + +## Further reading + +* [Configuring workload identity federation](../guides/administration/configuring-workload-identity-federation.md) + for the procedure and per-platform configuration. +* [Workload identity reference](../reference/workload-identity.md) for subject matching + rules, the condition environment, and token requirements. +* [About access control](access-control.md#service-accounts) for how service accounts relate + to users, teams, and permissions. diff --git a/docs/guides/administration/.pages b/docs/guides/administration/.pages index bed2ae90..4e004853 100644 --- a/docs/guides/administration/.pages +++ b/docs/guides/administration/.pages @@ -16,6 +16,7 @@ nav: - configuring-observability.md - configuring-ldap.md - configuring-oidc.md + - configuring-workload-identity-federation.md - managing-notification-templates.md - debugging-notifications.md - configuring-project-retention.md diff --git a/docs/guides/administration/configuring-workload-identity-federation.md b/docs/guides/administration/configuring-workload-identity-federation.md new file mode 100644 index 00000000..699bd771 --- /dev/null +++ b/docs/guides/administration/configuring-workload-identity-federation.md @@ -0,0 +1,364 @@ +# Configuring workload identity federation + +!!! note "Available in 5.2.0 and later" + + Earlier versions authenticate automation with team API keys only. + +Workload identity federation lets a CI job, a Kubernetes pod, or any other workload exchange a token +issued by its own platform for a Dependency-Track session, so that the workload stores no +API key. For the model behind it, see +[About workload identity federation](../../concepts/workload-identity-federation.md). + +Creating providers and bindings requires the `ACCESS_MANAGEMENT` permission, or its fine-grained variants. + +## Before you start + +Collect the following from your platform: + +* The **issuer**, for OpenID Connect providers. For SPIFFE, the **trust domain** instead. +* Whether Dependency-Track can reach the issuer's discovery document over HTTPS. + If it can't, you need the issuer's public signing keys as a JSON Web Key Set. +* The exact `sub` claim of the tokens the workload receives. Print one during a trial run + rather than guessing, because platforms change the format. + +Pick an **audience** that's unique to this Dependency-Track instance, such as its URL. +The same audience goes into every provider and into every token request the workload makes. + +## Create a service account + +Under **Administration > Access Management > Service Accounts**, create the account the +workload acts as and grant it only the permissions the workload needs, +such as `BOM_UPLOAD` and `PROJECT_CREATION_UPLOAD`. + +Give the workload its own account rather than reusing one. +The username appears in audit records, and suspending it cuts off exactly one workload. + +Don't create an API key for it. + +## Register a workload identity provider + +Go to **Administration > Access Management > Workload Identity Providers** and select **Create**. + +![Create provider modal](../../assets/images/guides/administration/configuring-workload-identity-federation/create-provider.png) + +Fill in the form: + +| Field | What to enter | +|:---------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------| +| **Name** | A short name such as `github-actions`. Workloads send this name when they exchange a token, and it can't change later. | +| **Type** | **OpenID Connect** for CI systems, cloud platforms, and Kubernetes. **SPIFFE** for SPIRE and other SPIFFE implementations. It can't change later. | +| **Issuer** | The exact value of the token's `iss` claim. For SPIFFE, the bare trust domain, such as `example.org`. | +| **Audience** | The value the token's `aud` claim must contain. | +| **Signing Keys** | Where the public keys come from. See [Choose a key source](#choose-a-key-source). | +| **Session Lifetime** | How long sessions from this provider last, in seconds. Between 60 and 86400, and 3600 by default. | + +Dependency-Track fetches the keys while saving, and refuses the provider if that fails. + +Register the same issuer twice if two groups of workloads need different session lifetimes or different audiences. +A binding has no lifetime of its own. + +### Choose a key source + +**Discover from the issuer's OpenID Connect configuration** is the default for OpenID Connect +providers, and the right choice whenever Dependency-Track can reach the issuer. +It resolves the key set from the issuer's [discovery document](../../reference/workload-identity.md#key-fetching). + +**Fetch from a JWKS URL** skips discovery. Use it for SPIFFE bundle endpoints, +and for issuers whose discovery document isn't reachable but whose key set is. + +**Provide a JWK Set** stores the keys in Dependency-Track and fetches nothing. +Use it for issuers Dependency-Track can't reach at all, mainly self-managed Kubernetes clusters. +Inline keys are never refreshed, so update the provider whenever the issuer rotates its keys. + +Fetched URLs must use `https`. An issuer inside your network works, subject to the +[outbound fetch constraints](../../reference/workload-identity.md#outbound-fetch-constraints). + +## Bind a subject to the service account + +Go back to **Administration > Access Management > Service Accounts**, +expand the service account, and select the plus icon under **Workload Identity Bindings**. + +![Create binding modal](../../assets/images/guides/administration/configuring-workload-identity-federation/create-binding.png) + +| Field | What to enter | +|:--------------|:------------------------------------------------------------------------------------------------------------------------------------| +| **Provider** | The provider you registered. | +| **Subject** | The exact `sub` claim to accept, or a prefix of one. See [subject matching](../../reference/workload-identity.md#subject-matching). | +| **Condition** | Optional [CEL](../../reference/cel-expressions.md) expression over the token's claims, available as the `claims` map. | + +On a shared issuer, make the subject prefix include at least your organization. +See [Subjects and conditions](../../concepts/workload-identity-federation.md#subjects-and-conditions). + +A service account can hold more than one binding. Dependency-Track uses the first that +matches, in [matching order](../../reference/workload-identity.md#matching-order). + +To revoke access, delete the binding. Sessions already issued stay valid until they expire. +To cut those off too, suspend the service account. + +## Exchange the token in the workload + +The workload posts its platform token to `/api/v2/oauth/token`, a [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693) +token exchange endpoint. The request needs no authentication of its own. + +```bash +ACCESS_TOKEN=$(curl -sSf -X POST \ + "https://dependency-track.example.com/api/v2/oauth/token" \ + -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ + -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ + -d "subject_token=${PLATFORM_TOKEN}" \ + -d "workload_identity_provider=github-actions" \ + -d "service_account=ci-pipeline" \ + | jq -r .access_token) +``` + +Use the result as a bearer token: + +```bash +curl -sSf -X POST "https://dependency-track.example.com/api/v1/bom" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -F "project=${PROJECT_UUID}" \ + -F "bom=@bom.json" +``` + +Exchange once per job and reuse the session for the rest of it. The session lives for the provider's session lifetime, +and no endpoint renews it. For the full parameter list and the error responses, +see the operations tagged `OAuth` in the [REST API v2 reference](../../reference/api/v2.md). + +## Platform examples + +Replace `https://dependency-track.example.com` with your instance URL, and `acme-inc` with your organization. + + +### GitHub Actions + + +GitHub Actions issues OpenID Connect ID tokens from a single issuer shared by every account +on GitHub.com, so the subject prefix has to carry your organization. +GitHub's [OIDC reference](https://docs.github.com/en/actions/reference/security/oidc) lists +the claims and their current formats. + +Provider: + +| Field | Value | +|:-------------|:--------------------------------------------------------| +| Name | `github-actions` | +| Type | OpenID Connect | +| Issuer | `https://token.actions.githubusercontent.com` | +| Audience | `https://dependency-track.example.com` | +| Signing Keys | Discover from the issuer's OpenID Connect configuration | + +Binding, for every repository in the organization: + +| Field | Value | +|:----------|:------------------| +| Subject | `repo:acme-inc/*` | +| Condition | (none) | + +!!! warning "Two subject formats exist" + + Repositories created after 2026-07-15, and older ones that opt in, + use an [immutable subject](https://docs.github.com/en/actions/reference/security/oidc#immutable-subject-claims) + that carries numeric IDs, such as `repo:acme-inc@123456/app@456789:ref:refs/heads/main`. + A prefix of `repo:acme-inc/*` doesn't match it. Use `repo:acme-inc@123456/*` for those repositories, + and add a second binding if your organization has both formats in use. + +The job needs the `id-token: write` permission, and requests the token for your audience: + +```yaml title=".github/workflows/upload-bom.yml" +jobs: + upload-bom: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - name: Upload BOM + env: + DT_URL: https://dependency-track.example.com + run: | + PLATFORM_TOKEN=$(curl -sSf \ + -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ + "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${DT_URL}" | jq -r .value) + # Exchange PLATFORM_TOKEN as shown above. +``` + +`ACTIONS_ID_TOKEN_REQUEST_URL` already carries a query string, which is why the audience is preceded by `&`. + +### GitLab CI + +GitLab CI issues ID tokens from the URL of the GitLab instance. +On GitLab.com every account shares that issuer, so the subject prefix has to carry your group. +GitLab's [ID token authentication](https://docs.gitlab.com/ci/secrets/id_token_authentication/) guide +lists the claims and their current formats. + +Provider: + +| Field | Value | +|:-------------|:--------------------------------------------------------| +| Name | `gitlab` | +| Type | OpenID Connect | +| Issuer | `https://gitlab.com`, or the URL of your instance | +| Audience | `https://dependency-track.example.com` | +| Signing Keys | Discover from the issuer's OpenID Connect configuration | + +The job declares the token and the audience it wants: + +```yaml title=".gitlab-ci.yml" +upload-bom: + id_tokens: + DT_TOKEN: + aud: https://dependency-track.example.com + script: + - ./upload-bom.sh # Exchanges $DT_TOKEN as shown in "Exchange the token in the workload". +``` + +#### Matching on the environment requires a condition + +The GitLab subject is `project_path:{group}/{project}:ref_type:{type}:ref:{branch}`. +It only names the project and the git ref. The deployment environment lives in separate claims, +so a rule such as "only the job that deploys to production" **doesn't fit into a subject at all**. +Write it as a condition: + +| Field | Value | +|:----------|:-------------------------------------------------------------------------------| +| Subject | `project_path:acme-inc/*` | +| Condition | `claims.environment == "production" && claims.environment_protected == "true"` | + +!!! note + + GitLab encodes the boolean-looking claims `environment_protected` and `ref_protected` as the + strings `"true"` and `"false"`, so `claims.environment_protected == true` never matches. + +The `environment` claim only appears on jobs that declare an `environment:`. A condition that +reads a missing claim raises an error, and a binding whose condition errors doesn't match, +so this binding rejects every job that isn't a deployment. + +### Kubernetes + +A pod authenticates with a [projected service account token][k8s-sa]. +The subject is `system:serviceaccount:{namespace}:{name}`. + +A cluster set up with `kubeadm` issues tokens from the cluster-internal issuer +`https://kubernetes.default.svc.cluster.local`. That name only resolves inside the cluster, +so Dependency-Track can't run discovery against it. Store the keys inline instead: + +```shell +kubectl get --raw /openid/v1/jwks +``` + +Provider: + +| Field | Value | +|:-------------|:------------------------------------------------------| +| Name | `k8s-prod` | +| Type | OpenID Connect | +| Issuer | The cluster's issuer, exactly as it appears in `iss` | +| Audience | `https://dependency-track.example.com` | +| Signing Keys | Provide a JWK Set, pasting the output of that command | + +Where Dependency-Track can reach the cluster's discovery document, as with many managed offerings, +use the cluster's issuer URL with discovery and skip the manual key set. + +Binding, for one service account: + +| Field | Value | +|:--------|:----------------------------------------| +| Subject | `system:serviceaccount:ci:bom-uploader` | + +The pod asks for a token with the right audience through a projected volume: + +```yaml +spec: + serviceAccountName: bom-uploader + containers: + - name: upload-bom + image: acme-inc/upload-bom:1.0.0 + volumeMounts: + - name: dt-token + mountPath: /var/run/secrets/dependency-track + readOnly: true + volumes: + - name: dt-token + projected: + sources: + - serviceAccountToken: + audience: https://dependency-track.example.com + expirationSeconds: 3600 + path: token +``` + +The container reads the token from `/var/run/secrets/dependency-track/token` and sends it as `subject_token`. +The kubelet refreshes the file, so read it right before each exchange. + +### SPIFFE and SPIRE + +SPIFFE workloads present [JWT-SVIDs][spiffe-jwt-svid], which may omit the `iss` claim and +carry their trust domain in the subject instead. Register them with type **SPIFFE**. + +Provider: + +| Field | Value | +|:-------------|:----------------------------------------------------------------------| +| Name | `spire` | +| Type | SPIFFE | +| Issuer | `example.org`, the bare trust domain without `spiffe://` | +| Audience | `https://dependency-track.example.com` | +| Signing Keys | Fetch from a JWKS URL, pointing at the trust domain's bundle endpoint | + +Only bundle endpoints with a publicly trusted certificate work, +which is the `https_web` profile of [SPIFFE federation][spiffe-federation]. +For a trust domain without such an endpoint, export the bundle and store it inline: + +```shell +spire-server bundle show -format spiffe +``` + +Binding: + +| Field | Value | +|:----------|:----------------------------| +| Subject | `spiffe://example.org/ci/*` | +| Condition | (none) | + +A SPIFFE prefix must end with `/`. Changing the trust domain of the provider stops every one of +its bindings from matching, so update their subjects afterwards. + +The workload fetches its JWT-SVID for the audience from the [SPIRE agent][spire-agent]: + +```shell +spire-agent api fetch jwt -audience https://dependency-track.example.com +``` + +## Verify the setup + +Run the exchange once from the workload, then confirm the identity that its session carries: + +```bash +curl -sSf "https://dependency-track.example.com/api/v1/user/self" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" +``` + +The `username` in the response is the service account, prefixed with `svc:`. + +The binding's **Last used** timestamp updates on every successful exchange, +which is the quickest way to tell whether a binding is the one that matched. + +## Troubleshooting + +The exchange endpoint returns the same `invalid_request` error whatever went wrong, +so it never names the failing check. The API server log records every refused exchange as a security event, +including the token's claims when the token verified but no binding matched. + +## See also + +* [About workload identity federation](../../concepts/workload-identity-federation.md) for + the model behind providers, bindings, and sessions. +* [Workload identity reference](../../reference/workload-identity.md) for subject matching + rules, the condition environment, and token requirements. +* [REST API v2 reference](../../reference/api/v2.md) for request and response schemas. +* [Permissions](../../reference/permissions.md) for what to grant the service account. + +[k8s-sa]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +[spiffe-federation]: https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md +[spiffe-jwt-svid]: https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md +[spire-agent]: https://spiffe.io/docs/latest/deploying/spire_agent/ diff --git a/docs/guides/upgrading/v5.2.0.md b/docs/guides/upgrading/v5.2.0.md index 6d51e476..1b30dacb 100644 --- a/docs/guides/upgrading/v5.2.0.md +++ b/docs/guides/upgrading/v5.2.0.md @@ -1,8 +1,8 @@ # Upgrading to v5.2.0 -* **Service accounts reserve the `svc:` username prefix**. This release adds [service accounts][access-control-svc], - a user type for CI pipelines, scripts, and other automation. Their usernames start with `svc:`, - and no other user can have a username with that prefix, in any letter case. +* **Service accounts reserve the `svc:` username prefix**. This release adds + [service accounts][access-control-svc]. Existing users whose username starts with `svc:`, + in any letter case, collide with that reservation. If such a user exists, the database migration fails and its error message lists the affected users. To find them before you upgrade, run this query against the database: diff --git a/docs/includes/abbreviations.md b/docs/includes/abbreviations.md index 88dcf7da..6d1f23d3 100644 --- a/docs/includes/abbreviations.md +++ b/docs/includes/abbreviations.md @@ -10,6 +10,8 @@ *[GHSA]: GitHub Security Advisory *[IdP]: Identity Provider *[JVM]: Java Virtual Machine +*[JWKS]: JSON Web Key Set +*[JWT]: JSON Web Token *[KEK]: Key Encryption Key *[KEV]: Known Exploited Vulnerabilities *[LDAP]: Lightweight Directory Access Protocol @@ -23,5 +25,7 @@ *[SARIF]: Static Analysis Results Interchange Format *[SBOM]: Software Bill of Materials *[SLO]: Service Level Objectives +*[SPIFFE]: Secure Production Identity Framework for Everyone +*[SVID]: SPIFFE Verifiable Identity Document *[VDR]: Vulnerability Disclosure Report *[VEX]: Vulnerability Exploitability eXchange diff --git a/docs/reference/.pages b/docs/reference/.pages index deeb1273..4d34fe76 100644 --- a/docs/reference/.pages +++ b/docs/reference/.pages @@ -14,6 +14,7 @@ nav: - CEL expressions: cel-expressions.md - Access control: - Permissions: permissions.md + - Workload identity: workload-identity.md - Integrations: - Badges: badges.md - File formats: file-formats.md diff --git a/docs/reference/cel-expressions.md b/docs/reference/cel-expressions.md index 2b89caa7..58d98044 100644 --- a/docs/reference/cel-expressions.md +++ b/docs/reference/cel-expressions.md @@ -1,6 +1,6 @@ # CEL Expressions -Dependency-Track uses the [Common Expression Language] (CEL) in two places: +Dependency-Track uses the [Common Expression Language] (CEL) in three places: * **Policy conditions**, evaluated against components or vulnerabilities to drive policy violations and analyses. See @@ -8,12 +8,16 @@ Dependency-Track uses the [Common Expression Language] (CEL) in two places: * **Notification filters**, evaluated against notification subjects to control which notifications are dispatched. See [Filter expressions](notifications/filter-expressions.md). +* **Workload identity binding conditions**, evaluated against the claims of a + verified platform token to decide whether a workload may act as a service + account. Available in 5.2.0 and later. See + [Workload identity](workload-identity.md#condition-environment). -The two contexts share the syntax described on this page, but expose **different +The contexts share the syntax described on this page, but expose **different variables, types, and custom functions**. The custom functions documented for policies (for example `depends_on`, `spdx_expr_allows`) are not available to -notification filters, and the variables differ entirely. Refer to the -context-specific page for the available inputs and functions. +notification filters or binding conditions, and the variables differ entirely. +Refer to the context-specific page for the available inputs and functions. ## Syntax @@ -33,7 +37,8 @@ Both contexts have access to the [standard definitions] of the CEL specification The policy context registers additional custom functions (`depends_on`, `spdx_expr_allows`, and so on); see [Condition expressions](policies/condition-expressions.md#function-reference). -Notification filters do not register any custom functions. +Notification filters and workload identity binding conditions do not register any custom +functions. ## Optional field checking @@ -46,8 +51,8 @@ before accessing it: has(obj.field) && obj.field == "value" ``` -The pattern applies in both contexts. The exact root variables (`component`, `subject`, -and so on) are documented on the context-specific reference pages linked above. +The `has()` macro works in all three contexts. The exact root variables (`component`, +`subject`, `claims`, and so on) are documented on the context-specific reference pages. [C-style languages]: https://en.wikipedia.org/wiki/List_of_C-family_programming_languages [CEL strings extension]: https://github.com/google/cel-spec/blob/master/doc/extensions/strings.md diff --git a/docs/reference/permissions.md b/docs/reference/permissions.md index 189db1d7..73eafdcd 100644 --- a/docs/reference/permissions.md +++ b/docs/reference/permissions.md @@ -64,11 +64,11 @@ and [Managing project versions](../guides/user/managing-project-versions.md). | Permission | Description | |:------------------------------|:--------------------------------------------------------------------| -| `ACCESS_MANAGEMENT` | Manage users, service accounts, teams, permissions, and API keys. | -| `ACCESS_MANAGEMENT_CREATE` | Create users, service accounts, teams, and API keys. | -| `ACCESS_MANAGEMENT_READ` | Read users, service accounts, teams, and API keys. | -| `ACCESS_MANAGEMENT_UPDATE` | Update users, service accounts, teams, and API keys. | -| `ACCESS_MANAGEMENT_DELETE` | Delete users, service accounts, teams, and API keys. | +| `ACCESS_MANAGEMENT` | Manage users, service accounts, teams, permissions, API keys, and workload identity. | +| `ACCESS_MANAGEMENT_CREATE` | Create users, service accounts, teams, API keys, workload identity providers, and bindings. | +| `ACCESS_MANAGEMENT_READ` | Read users, service accounts, teams, API keys, workload identity providers, and bindings. | +| `ACCESS_MANAGEMENT_UPDATE` | Update users, service accounts, teams, API keys, and workload identity providers. | +| `ACCESS_MANAGEMENT_DELETE` | Delete users, service accounts, teams, API keys, workload identity providers, and bindings. | ### System configuration diff --git a/docs/reference/workload-identity.md b/docs/reference/workload-identity.md new file mode 100644 index 00000000..232bcde0 --- /dev/null +++ b/docs/reference/workload-identity.md @@ -0,0 +1,125 @@ +# Workload identity + +!!! note "Available in 5.2.0 and later" + + Earlier versions authenticate automation with team API keys only. + +For the model, see [About workload identity federation](../concepts/workload-identity-federation.md). +For the procedure, see [Configuring workload identity federation](../guides/administration/configuring-workload-identity-federation.md). + +Field constraints, request bodies, responses, and the permission each operation requires are +in the [REST API v2 reference](api/v2.md), under the operations tagged `Workload Identity Providers`, `OAuth`, +and the workload identity binding operations tagged `Service Accounts`. +This page describes the behavior behind those operations. + +## Provider types + +| Type | `iss` claim | `sub` claim | Key source | +|:---------|:---------------------------------|:-------------------------------------------|:------------------------------------| +| `OIDC` | Must equal the provider's issuer | Any value | Discovery, a key set URL, or inline | +| `SPIFFE` | Ignored | Must start with `spiffe:///` | A bundle endpoint URL, or inline | + +The issuer of a `SPIFFE` provider is a bare trust domain, such as `example.org`. +A key set URL on a `SPIFFE` provider is the trust domain's bundle endpoint, +which must use the `https_web` profile of SPIFFE federation. + +## Key fetching + +Discovery reads `/.well-known/openid-configuration`, requires the document to name the same issuer, +and stores the `jwks_uri` it names. Changing the issuer of an `OIDC` provider that fetches its keys from +a URL re-runs discovery and replaces the stored URL, unless the update names a key source itself. + +Dependency-Track caches fetched key sets and rate-limits how often it refreshes them per provider. +When a refresh fails, it keeps using the last fetched key set for up to one hour. + +### Outbound fetch constraints + +Dependency-Track fetches remote documents when you save a provider, +and during an exchange when a key is missing from the cache. Every fetch obeys the following: + +* Only `https` URLs, without user information. +* After resolving the hostname, Dependency-Track refuses loopback, link-local, wildcard, + and multicast addresses. Private ranges stay allowed. When a configured HTTP proxy handles the destination, + Dependency-Track skips the address check, because the proxy resolves the name. +* Dependency-Track doesn't follow redirects. +* A response must arrive complete within 10 seconds, and must not exceed 1 MiB. +* Response bodies never appear in API responses or logs. + +## Subject matching + +A subject is either an exact value or a prefix followed by `*`. +The prefix must be non-empty and must end with `:` or `/`. + +| Subject | Matches | +|:----------------------------------------|:--------------------------------------------------| +| `repo:acme-inc/app:ref:refs/heads/main` | That exact `sub` claim. | +| `repo:acme-inc/*` | Every `sub` starting with `repo:acme-inc/`. | +| `repo:acme-inc/app*` | Rejected. The prefix doesn't end with `:` or `/`. | +| `spiffe://example.org/ci/*` | Every SPIFFE ID under `spiffe://example.org/ci/`. | +| `spiffe://example.org/ci*` | Rejected. A SPIFFE prefix must end with `/`. | + +For `SPIFFE` providers, the subject must lie within the provider's trust domain. + +To match a prefix that ends mid-segment, use an exact subject prefix plus a condition, +such as `claims.sub.startsWith("repo:acme-inc/app-")`. + +## Condition environment + +Conditions are [CEL](cel-expressions.md) expressions. +Dependency-Track compiles and type-checks them when you save the binding. +They see one variable: + +| Variable | Type | Contents | +|:---------|:-------------------|:------------------------------------------| +| `claims` | `map(string, dyn)` | The claims of the verified subject token. | + +The [CEL strings extension](https://github.com/google/cel-spec/blob/master/doc/extensions/strings.md) is available. +Dependency-Track registers no custom functions here. + +An expression must return a boolean. Because claim values are dynamic, +`claims.admin` alone doesn't compile. Write `claims.admin == true` instead. + +At exchange time, a condition that raises an error, for example because a claim is absent, doesn't match. +Guard optional claims with `has(claims.name)` where the binding should still match tokens that omit them. +Claim values keep their JSON types, and platforms that encode booleans as strings need string comparisons. + +```js +claims.ref == "refs/heads/main" + +claims.environment == "production" && claims.environment_protected == "true" + +claims.sub.startsWith("repo:acme-inc/app-") +``` + +## Matching order + +Dependency-Track evaluates only the bindings of the service account that the request names, +and only those belonging to the named provider. It tries them from oldest to newest and uses +the first whose subject and condition both match. Duplicate and overlapping bindings do no harm, +because the resulting session is the same either way. + +## Subject token requirements + +A subject token must be a signed JWT that meets these requirements: + +| Property | Value | +|:--------------------|:---------------------------------------------------------------------------------------| +| Header `typ` | `JWT`, `JOSE`, `at+jwt`, or absent. | +| Signature algorithm | `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `ES256`, `ES384`, or `ES512`. | +| Required claims | `exp` and `sub`. | +| `aud` | Must contain the provider's audience. | +| `iss` | Must equal the provider's issuer for `OIDC` providers. Ignored for `SPIFFE` providers. | + +Dependency-Track refuses symmetric and unsigned tokens. It accepts keys marked for `jwt-svid` use, +as SPIFFE bundles mark them, alongside keys marked for signature use. + +## Audit events + +The API server logs the following as security events: + +* Creating, updating, and deleting providers and bindings. +* Successful exchanges, naming the provider, the service account, the matched binding, + the token's subject, and its `jti` if it has one. +* Refused exchanges. When the token verified but no binding matched, the log also carries the token's claims. + +Subject tokens are never logged.