From bc7aaa21d0839ccb8e218bd8bedbf2fab8edbf51 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 20 Aug 2023 11:36:52 +0100 Subject: [PATCH 01/36] [release] v0.10.0-unstable --- .circleci/config.yml | 18 ++ .gitignore | 7 +- build.sh | 1 + changelog.md | 6 + client/src/api/constellation.tsx | 25 ++ client/src/api/downloadButton.jsx | 28 ++ client/src/api/index.jsx | 3 + .../src/assets/images/icons/constellation.png | Bin 0 -> 8708 bytes client/src/menu-items/pages.jsx | 10 +- client/src/pages/constellation/addDevice.jsx | 147 +++++++++ client/src/pages/constellation/index.jsx | 81 +++++ client/src/routes/MainRoutes.jsx | 5 + client/src/utils/indexs.js | 4 +- dockerfile | 2 +- dockerfile.arm64 | 3 +- package.json | 9 +- src/background.go | 4 +- src/configapi/get.go | 1 + src/constellation/api_devices_create.go | 129 ++++++++ src/constellation/api_devices_index.go | 18 ++ src/constellation/api_devices_list.go | 83 +++++ src/constellation/index.go | 42 +++ src/constellation/nebula.go | 283 ++++++++++++++++++ src/constellation/nebula_default.go | 95 ++++++ src/httpServer.go | 2 + src/index.go | 3 + src/user/create.go | 3 - src/utils/certificates.go | 26 +- src/utils/types.go | 83 ++++- src/utils/utils.go | 8 +- 30 files changed, 1099 insertions(+), 30 deletions(-) create mode 100644 client/src/api/constellation.tsx create mode 100644 client/src/api/downloadButton.jsx create mode 100644 client/src/assets/images/icons/constellation.png create mode 100644 client/src/pages/constellation/addDevice.jsx create mode 100644 client/src/pages/constellation/index.jsx create mode 100644 src/constellation/api_devices_create.go create mode 100644 src/constellation/api_devices_index.go create mode 100644 src/constellation/api_devices_list.go create mode 100644 src/constellation/index.go create mode 100644 src/constellation/nebula.go create mode 100644 src/constellation/nebula_default.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 661a5c8..758474e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,6 +53,24 @@ jobs: command: | curl -s -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=$MAX_TOKEN&suffix=tar.gz" -o GeoLite2-Country.tar.gz tar -xzf GeoLite2-Country.tar.gz --strip-components 1 --wildcards "*.mmdb" + + - run: + name: Download and Extract ARM Nebula Binary + command: | + curl -LO https://github.com/slackhq/nebula/releases/download/v1.7.2/nebula-linux-arm64.tar.gz + tar -xzvf nebula-linux-arm64.tar.gz + + - run: + name: Rename ARM Nebula Binary + command: | + mv nebula nebula-arm + mv nebula-cert nebula-cert-arm + + - run: + name: Download and Extract Nebula Binary + command: | + curl -LO https://github.com/slackhq/nebula/releases/download/v1.7.2/nebula-linux-amd64.tar.gz + tar -xzvf nebula-linux-amd64.tar.gz - run: name: Build UI diff --git a/.gitignore b/.gitignore index 6f546b6..d347d46 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,9 @@ todo.txt LICENCE tokens.json .vscode -GeoLite2-Country.mmdb \ No newline at end of file +GeoLite2-Country.mmdb +zz_test_config +nebula-arm +nebula-arm-cert +nebula +nebula-cert \ No newline at end of file diff --git a/build.sh b/build.sh index bd48565..57b3348 100644 --- a/build.sh +++ b/build.sh @@ -18,6 +18,7 @@ echo " ---- Build complete, copy assets ----" cp -r static build/ cp -r GeoLite2-Country.mmdb build/ +cp nebula-arm-cert nebula-cert nebula-arm nebula build/ cp -r Logo.png build/ mkdir build/images cp client/src/assets/images/icons/cosmos_gray.png build/cosmos_gray.png diff --git a/changelog.md b/changelog.md index 4e3d8dc..d3d2ed4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,12 @@ +<<<<<<< HEAD ## Version 0.9.20 - 0.9.21 - Add option to disable CORS hardening (with empty value) +======= +## Version 0.10.0 + - Added Constellation + - DNS Challenge is now used for all certificates when enabled +>>>>>>> b8a9e71 ([release] v0.10.0-unstable) ## Version 0.9.19 - Add country whitelist option to geoblocker - No countries blocked by default anymore diff --git a/client/src/api/constellation.tsx b/client/src/api/constellation.tsx new file mode 100644 index 0000000..717f78a --- /dev/null +++ b/client/src/api/constellation.tsx @@ -0,0 +1,25 @@ +import wrap from './wrap'; + +function list() { + return wrap(fetch('/cosmos/api/constellation/devices', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + }, + })) +} + +function addDevice(device) { + return wrap(fetch('/cosmos/api/constellation/devices', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(device), + })) +} + +export { + list, + addDevice, +}; \ No newline at end of file diff --git a/client/src/api/downloadButton.jsx b/client/src/api/downloadButton.jsx new file mode 100644 index 0000000..814e6a7 --- /dev/null +++ b/client/src/api/downloadButton.jsx @@ -0,0 +1,28 @@ +import { Button } from "@mui/material"; + +export const DownloadFile = ({ filename, content, label }) => { + const downloadFile = () => { + // Create a blob with the content + const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); + + // Create a link element + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = filename; + + // Append the link to the document (needed for Firefox) + document.body.appendChild(link); + + // Simulate a click to start the download + link.click(); + + // Cleanup the DOM by removing the link element + document.body.removeChild(link); + } + + return ( + + ); +} \ No newline at end of file diff --git a/client/src/api/index.jsx b/client/src/api/index.jsx index 492bc78..3472e51 100644 --- a/client/src/api/index.jsx +++ b/client/src/api/index.jsx @@ -3,6 +3,7 @@ import * as _users from './users'; import * as _config from './config'; import * as _docker from './docker'; import * as _market from './market'; +import * as _constellation from './constellation'; import * as authDemo from './authentication.demo'; import * as usersDemo from './users.demo'; @@ -211,6 +212,7 @@ let users = _users; let config = _config; let docker = _docker; let market = _market; +let constellation = _constellation; if(isDemo) { auth = authDemo; @@ -232,6 +234,7 @@ export { config, docker, market, + constellation, getStatus, newInstall, isOnline, diff --git a/client/src/assets/images/icons/constellation.png b/client/src/assets/images/icons/constellation.png new file mode 100644 index 0000000000000000000000000000000000000000..d507a4440935cd473c50b1ae9138e9bbd483b189 GIT binary patch literal 8708 zcmbVy2UHVlxAxGx^xi_~D3H(zUAlA#p!AT0A|;XDJ1Bw_5e1cALd604V5xZ+O7d=PUp~40VOy^toxE4?|)+#1JTqBU&uL1BOI`y>KqMr^A(G+}lA;hfn6wN`QWo^r4~&EJLOH=qHMIT$!yPGs zU3`47FmZ8ze}6H5NimF)4)pXv1c-Wi^Zvtw2HG3x<%;!j z#dv~#dqg;5e0`L_xKRJh!2|oZTTk!5c)~Fz9)Q4#L&YG!bNa&&h5XwN>+9wI2RI5T zj&?_Tpgn!OakkLEZLuyGAB?vP=HIyf`|^JRz;SC}@HfW4jK#y_ZwPN6O+Q>3e-ZL8 zt-axaShTn)+8g8Rg+y!m;lkwooedVI?uAD9V7%ZMjQc+-W&DqjK@w78G9V#KS5Fki z-&^>fCZIJCK4>NI@2cVIB?^^=L!mG!85mUNDg+9HK>jo}z@S{60{^?I1WW=7laTyR zrZ~-@5I%_i)fk0@Ibpm!5V(Y0JrK@lajd5^81y%dFm;SO#tR3Gi%#;7C=3i>x}M%X z2u~zhS3?Pm%UR6T6$Nud$s?p>5Hg|&c}ZDOd08naZiIk}N=Zq}O3TYT%E?J0{xM$z zgY^Af+28a3R5=QR#QFFSR0tF)C5x7l6_rCFWJRTAq@+dVrRC&t#GvJ&C?{#8jJ(W0 zycv19;%+pA`#*dA9Tf`a5h8(>fkI`ZMG=xX1v@%QNr}p%B$1*>CnQQ*79uSzEh+t5 z&3_04rsL|3D_!7U_lFt!&R-*USI{4tfFY2-wXOt4{w^{a1^(-_>%ZXR|AhS4e18`- z&guV9{6EpXF-|`I2rsmnGmg9eRb1l#Hh7GuAKL4m!p9=K5V#(L_VQK&J9%L|KnN_> z-4%)WovXN?C+e>|_wVX}d@!K@(8|BX@kaRl&lLUNB=cwP|DB@$BKrRyivFS>>4NZd zM&mlPIQVxm;=fzjpT!sdue$i-+CR0uf0*NX>F?p+T^x7t_j3U4iF56R>+)|(=Enel zJy2Ie4IVJHWlItOH$S{II4u-RnM>2<;d>OpK!k_Dy^SEx2j_;n3*9e!_bMUO zcLF`jm|Bwu0#%BDIIowG@f6aI1e0d+r3qgKixHCXA?6-Ek>&aNYg;zb6*G4-J37=L zk!5%{Nk8X8wBsT!yt~S`Q{Vn`PPwGPWdQ0;ejD9(av3jv1a$UfDre3eT|0`_s5JMY zqO^qJIfNBAj_x1^lFGsHw%Ov=BDFr0l;&9gM0B~alH`?}e%-_4)!P-3Y6B6*b7a6W zi;?U|Tseii{LRVLi7Ljv$4~L{O6Y-hmYpH4-by{Ef!TIx=9kfgwQMXjwS6%BA_aTZ>z<6I`onh5=|%B22`6lT#MyHuke@pO zt|e!o@!Dnp)@>9wq3)(~O$5&=Pb1>uf=b{bV-pj3)zyS(G=E81sfMpF`RZYyg00Srqxk&}p?R8Q?LFfAV`t}bWIJvgD)~7zR+u6-wu_+xNKVCUJ%!hF@ zG(7K9P5GRP*DBwZsRE^(i~M-nk_vtE+K{a1Efas@BGJhcV;h^8rH9wy&~7&=wu`65 zMMn=15iVBas(LuyFLz44!xz1N8+(LJ&4;+?Ctjah!ac41-?5+kYU>1`QyGuul80|O+Bi{wHKCZv?4 zYa{LYrU-Fs3KmicQAU7mhT(NlRyYQX3@g!lmMCbOot+(b@a<)9U!PNuT~MElfZ=5t>)=@ zrP``|<%9p#t5*q0?t*1xxMm%f>fb@2=Y!)y%8HC^BBviUqXc&JSOHNh)JIQ07Kj9C9+az2QCs2c z>;%ro&)H2>{Iy7EgUW;|bx?VI+8@X-KsLlcXgwU=SP9f#GvEz_53+2wAaYQ`NLtT2 zMN12d`ztF2a)JADA|eByKJ|_kPz3$lh>nQ~8&uwv_}sJY@ck3JdXFifnlR|^^QNnL zaH=Kq1hrCBROE1Zp;VvojH<@!LDCHw85vTtD;>FN>a_Huvv94YB)2)WNCp6hxBAgC zle^I6V@Q!{{BoZ*c70eG4u7<4$YV4l#v+G>obvs+TToCSth_H+yCzA)P$LLfq25Oq zP{rw!HpZT;Ol=_>FUDS?xZX97-n%}a6fEEXNh4m7cuGp>&3&s(h>;yf$Ek*=EeG~! z-GMMh*8gYk+soC7tlmNWonf#&cRCF%ZS3OFgC(wLG8blRBMF+q1hNQ=SL)$jOz+f&^g^_G~c7oHZfw6x8K6&mfe7G&9U2wyVfixua3=;H1 zPD87k<-5p3hKyn(qPK4Vm-J}ao}>5^R@Sd?JbqCtT06g#+7H8A3jtH6ntap`88Ehf z)%{*+HQ=b6a>Y(+E-BA_c@j#WXRN_afuAkw0P2-u2Bv1-zs^3)I1M=SU2{e|JA5DC zF@eJ!-VAbM>jdrW=%ek(K0Nqx8bl=%Zh6nrPG8;Eyx`OP^7rq_ot?U*)MhLcQ98sC zl-@wD!CGVeG;+NVa!d&GhUR!fDW+@BBbu6QC~7Cm_u8+T+^mE!h`w|&B^t<)i@w|v zg7zH|5fdXfC*w(}$Yi?(5iKE=j@E2`lr43UTXz{my{`_G$ z8H90=AZXJx0~Tr_1JGYyhv>Dvw`cubrz9J%3(?2~-x; zix=clid`ubNeineFXxP#3FeNYV(+U;hIsOO#T& z$GKTZ_bG{5Jv3EPn`elrZX1pG6zNbL1NvJ;PSSPj$Tg z3ZIJtLUQm-s8xl=tavac$3CLXs;npu9*Tzz{nEiY?8xxC#@0j-s&@jEC3j(Fx@!-A5 zOKW*toBdqoI7^fNylsrcncd1+N9}$+dsS>5K}1;NeB0Ao7fJzsn4P&C(YImALNXtT zc0y+eW|wW>pv`zJt|<-J+;T;W^wG0Opa_S!*wz^XW-@poY(VE3t6orU?mGDWxhHSM zQjG>Vx}> zj-^J5yJosy2&nb%g2DYDo_4@^oC)}G%3r(LGi1Y+yrtsNz1ZYR&2ueT{h(oPJ#K{uIkij)B1FWLEHTg{BM6+le)t$xfCoW6 zRhT%3&pQjRY}}6K9}=nz&}Df)myzt7vjSSxr%K)+D}pYE?6#={EhU88FZo4hX!ypU zC~!}$XAD5lJQ2G!>lyw1%HW~PYfeXv4bw{vPx2xDY7pg1OR>42b9|#!wi}D&f+44Y zn2V!_u`gIt#BgE6QJoWLCDzZUyu%YD28v98O5d02<)L)qyVZpI)Y#Ul#1m>m>Q+nr zm6rW>`2q?#_b&oB_p77INt=%Rh{rGF)yL*FVhha#hGOdKl85U?_(nIM;pNI6l$WFL zaTj$z7M*8D%BF)yJ9kf_CyBgs4EHjez6d#Mmc7bH!(b`9#(}f+S z)ZvM&#uWC6glB}`XB)-4`ITD*AKxR62=ZXv$V%3mIxZM=-BI2SNWZnw`JCcwg~RH7 zILD;r6|<+;;2bx6@dt9u9nPJf?9S|0(Twtek=TvsD#9a12)n8uFF}gkHCgwLjsyB8fnh!20gpGyh=i#TegwZ7LQ6KNUYn8Qlk#HJZQ5xA7 z^^1}UKjMaUtX4A0?wCh2|FqfM?HyuYDx53Oo8RnERZT$-VvHoGYpKuUNcLf-^>?$R28Me50 z*wGZa30>ea%C4Fy@zs$JX4R&)^h&V4%|Ij6)AKzj9}`Zkcd~iOHe%fzQY`-9{1>8Q{;PGO>4S;6>ssqYJl3SSF3YHf=GKI3j5{nY=N-2&=Ld9`^S{d|=&UBw=C^!rK)hbopw^W6$WNsxzH490WokLp&dmPO|cm7sI=*3uS}nXTORPjJ&T}yW887 zQn4>~*|wMI^D{w(L2;#CetvEJ%o7>ZKxV&q=8#Z!FF%d3qw{*GZD673ayug*=gSej z7p9Kg*X_^FqDfezcfv;q2uK2#zMzGVZ>wj2!P7&29dA0cJir5~H*h^xEeyUq zFz2?T^jd>U4^eE+ShVBuJ=QeL9^}>vKFI&nK_XJQxhGj7Bpd#7>=~wMf(#%ZAdZz= zO6|E0z4MlPqK}pN_M5L064~Vnsqn4`+TJ@lY(hW#oSWsBUmAiGs>V0(Bp3oS8eR?3 z{sY}c`>WbMxYf{Sas~TUpb^tb9euqC-s(NhyN`7OcBXQI{vgM3LzfCMZ{g8P+VW?0RJN@^l zPsmV+Pk?1-cY~MzYfuZ9uC@p(i%4a1!=u248z!$p`RGaMBpLL@#$;6vzdk-~SIMUN zsbzXyP-KereCM#Wx#Qjb1^&n?Un*(h%ETy$Y(qN5RP4Hg5X(aYViSCqMf$t|KQx2* zS-`i3F~5OdX6biAjAX@ViD##YX5ZOU={Xlp*!lBQZ@!|b;V&~^wzw&=F4k8Z?OGRl z>}nJftAO})mS%HSD{BG~29Lx~llxQcE_CpdmaK@TCs7^QRtG$ z`V#BD1;rG5g^dyOMCi~&=7@tu3$iT^{r+}=)`@2^O zqUh(pNMZA(F>!EGO}+x8&2fruKb@ydIGIA-lZ?Su#+!5k9DpN3tm*pUx33H#a*{u% zN9hJQrMl5Yfof7R7f$)toKzzSjL5q>VFwd3v{VK87kK?9EY_>nDusTWh5%~*7Kv}M zY>WF}{g>*qvpW1*Ze}U1fp2q%pVYAE=WtFg9r*MTa>{!z)!&&*Z+*M_o&Gu@o@!z@ zO(}8l_-irSO^Dl#hb6O0YoLDlkfztn)CzSh)_rANg4|9A<6cM|2W0P0R<)@9_7Agc zT1%;=k@F|kib>qBmM&Uu`u8TBU1nzXY}wpu z$v|27)n24Xkx0nij!j61jW)8Q51&}0TJGx2%4G9$!xkoLNLH|xEVlc(s`B{iz|cW} za1Ddo#WP0$Ro2g0o;mh*k$_jYw`y(mKr|{>n3IFynzLzZYD-R{^;5W$JEiKvy$p*N zu@vvf+33t?u)u?N0|Jcc$)aREqeCK<4u%q8bGJl>@IV7K$hMG=9K8Xcp?sPbpiJJ(Qn(O_@vGs$Q|f`|?L8*q{aSfxgO z20<2JSmeNi(0}x6;8VeitND%c-hfoByRK3Ar2(GN%-Do$z}nE5E{D2qYst-KLn?Yw ze9nG$qrA6!Wx5a4yiL-w1)zrX+AdE?SkXRk%B?eYTglQ>9$UjW>pLdVCEk}YEa5>{ z62^bcsJ7XZO+1DmcT;c^&1e||9x8nM=GtQmDc#6hN&>AV$bLSfHOb=ETrP=C>>q*; zq6hj)S3RFT(rv8|Xg0cnd)>QFZ2ABwil2Tabbp;2^wlw|y+ZwNFpoq(85dXW+2yxD zu-7Q=B_K7V?Ed$I{HP$Yk%KQ1wWpSYvlU+>X`_a&Fi}Rq3}~x}bja_8TY&hjT<)6T zk$v9z$-UmEUV$o-HDKZ?W`YQe=F#*dTX@lD&F;@!l!1D05dG1;J=iaeOZPsV1er-4-TZ-KQR#T%bifHSj(k)lZTS;6x~w@EQSiyjG@BRnh>(Mwxi4dW zjf_cvp!KuVo@=e?@%$-W*3z5Zc*q<~GxCX3(pAYpmyZ~0TD5-8VU#iKuqgj}m-V}Q zi&9-pyj-@|78ZJ&-9;UMUMWI=xSL2!QW&dhY!ksIof9Mw)rb_OJ((Y~m}QfE8J5!- zNTU4W9qYG1pG~pa;#rmhW+FgCnt{_@f4njHUa{NIbNa~*q59^Q$B4zIw1*`P7z`aY zz{kG(Y&qx)eX*QE{R#Wn#LV^cwN-;UR3%M`Ra>$Kpz^5}kjyW>y9=bhs- z>{X%N_^l2!#-Cg=H(18r)k-2!$0pHT{g%jp;kb>>29cL02WfKi0Y4R~yXN{_3zy{~%o&Q z&7b~F>w?jku@a5?ckgPt&a_z%DkdI1BLJdq^gL^fGjGDi!0y9zSxoL4EluirFa~+z z)(DYdRqAAectCU!V3JiEBbz4WGN8~Cte!3vmm5k|C?s`jWE_l~vg8PZg%DJ#Ek zYSzoU=lS5I@k>w!^!;Z?eQ*3$A#*GvArU2^APcF8LPMxoZqJi%r=JRXptIbgYzIen zRfp5nr5~t(0@dNdE4>Q!PbG(aC32uSj%R!^zQ0|6ou*T6c8$LNb>O46g?n-heyl*(1os#@u%h* z^bvWf#J>10cnvD^L$%q0or|R zY-D7F1KAydsm0C!wuuo@f$hhqN)N0ODpX(tZmeInpJbl?DguCv$7b(dsSY!F{WPu+ zPA$9*GT~-pi(4IMX%Lgc?ke(ldn-6rWUFAI=)km zp9G%NY1-K-hv-yAeLHE5aHww#mO`Gz)x604>~iwK_*_}X)amv2R60O^jb}#RD(n6< z8G}h-F+)(()Fa0*MzulyH(U%1_lHv;T;0A%{U-`%c2gIh?>ZaX2TRxcnzitpcT7E> z5R?Wo4A^E^^hfd|2_gy!bcxbmli&6bmF4n1d*vIIIaA%6QDD2ZwbfJQi+7>M0Z65) zi}|lqd}-R|5YcFBOGD5%vpM-V^k;Ek+oCG)kp_ z`Z_PJ>jAA<0O76R&^P>exxDyl<8#ao@xVH%YKrda^=quaV^YG*F|iU}Ky||*MfW7m wR|?P}qLk2U(B)(W9J?4beojx*1DDjw6I&0y*%naz{!T+z(@^8}b%&_`2P6-IQ2+n{ literal 0 HcmV?d00001 diff --git a/client/src/menu-items/pages.jsx b/client/src/menu-items/pages.jsx index 335fbe6..a2e4068 100644 --- a/client/src/menu-items/pages.jsx +++ b/client/src/menu-items/pages.jsx @@ -1,5 +1,6 @@ // assets import { ProfileOutlined, PicLeftOutlined, SettingOutlined, NodeExpandOutlined, AppstoreOutlined} from '@ant-design/icons'; +import ConstellationIcon from '../assets/images/icons/constellation.png' // icons const icons = { @@ -7,7 +8,6 @@ const icons = { ProfileOutlined, SettingOutlined }; - // ==============================|| MENU ITEMS - EXTRA PAGES ||============================== // const pages = { @@ -29,6 +29,14 @@ const pages = { url: '/cosmos-ui/config-url', icon: icons.NodeExpandOutlined, }, + { + id: 'constellation', + title: 'Constellation', + type: 'item', + url: '/cosmos-ui/constellation', + icon: () => , + + }, { id: 'users', title: 'Users', diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx new file mode 100644 index 0000000..85de900 --- /dev/null +++ b/client/src/pages/constellation/addDevice.jsx @@ -0,0 +1,147 @@ +// material-ui +import { Alert, Button, Stack, TextField } from '@mui/material'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import * as React from 'react'; +import { useState } from 'react'; +import ResponsiveButton from '../../components/responseiveButton'; +import { PlusCircleFilled } from '@ant-design/icons'; +import { Formik } from 'formik'; +import * as yup from 'yup'; +import * as API from '../../api'; +import { CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; +import { DownloadFile } from '../../api/downloadButton'; + +const AddDeviceModal = ({ config, isAdmin, refreshConfig, devices }) => { + const [openModal, setOpenModal] = useState(false); + const [isDone, setIsDone] = useState(null); + + return <> + setOpenModal(false)}> + { + return API.constellation.addDevice(values).then(({data}) => { + setIsDone(data); + refreshConfig(); + }).catch((err) => { + setErrors(err.response.data); + }); + }} + > + {(formik) => ( +
+ Manually Add Device + + {isDone ? + +

+ Device added successfully! + Download the private and public keys to your device along side the config and network certificate to connect: +

+ + + + + + + +
+
: + +

Manually add a device to the constellation. It is recommended that you use the Cosmos app instead. Use this form to add another Nebula device manually

+
+ + + + + + + + +
+ {formik.errors && formik.errors.length > 0 && + {formik.errors.map((err) => { + return
{err}
+ })}
+
} +
+
+
+
+
} + + + + + +
+ + )} +
+
+ + { + setIsDone(null); + setOpenModal(true); + }} + variant="contained" + startIcon={} + > + Manually Add Device + + ; +}; + +export default AddDeviceModal; diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx new file mode 100644 index 0000000..d0550bb --- /dev/null +++ b/client/src/pages/constellation/index.jsx @@ -0,0 +1,81 @@ +import React from "react"; +import { useEffect, useState } from "react"; +import * as API from "../../api"; +import AddDeviceModal from "./addDevice"; +import PrettyTableView from "../../components/tableView/prettyTableView"; +import { DeleteButton } from "../../components/delete"; +import { CloudOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; +import IsLoggedIn from "../../isLoggedIn"; + +export const ConstellationIndex = () => { + const [isAdmin, setIsAdmin] = useState(false); + const [config, setConfig] = useState(null); + const [devices, setDevices] = useState(null); + + const refreshConfig = async () => { + let configAsync = await API.config.get(); + setConfig(configAsync.data); + setIsAdmin(configAsync.isAdmin); + setDevices((await API.constellation.list()).data || []); + }; + + useEffect(() => { + refreshConfig(); + }, []); + + const getIcon = (r) => { + if (r.deviceName.toLowerCase().includes("mobile") || r.deviceName.toLowerCase().includes("phone")) { + return + } + else if (r.deviceName.toLowerCase().includes("laptop") || r.deviceName.toLowerCase().includes("computer")) { + return + } else if (r.deviceName.toLowerCase().includes("desktop")) { + return + } else if (r.deviceName.toLowerCase().includes("tablet")) { + return + } else { + return + } + } + + return <> + + {devices && config && <> + r.deviceName} + buttons={[ + + ]} + columns={[ + { + title: '', + field: getIcon, + }, + { + title: 'Device Name', + field: (r) => {r.deviceName}, + }, + { + title: 'Owner', + field: (r) => {r.nickname}, + }, + { + title: 'Constellation IP', + screenMin: 'md', + field: (r) => r.ip, + }, + { + title: '', + clickable: true, + field: (r) => { + return { + alert("caca") + }}> + } + } + ]} + /> + } + +}; \ No newline at end of file diff --git a/client/src/routes/MainRoutes.jsx b/client/src/routes/MainRoutes.jsx index 503e179..5c67c26 100644 --- a/client/src/routes/MainRoutes.jsx +++ b/client/src/routes/MainRoutes.jsx @@ -15,6 +15,7 @@ import ContainerIndex from '../pages/servapps/containers'; import NewDockerServiceForm from '../pages/servapps/containers/newServiceForm'; import OpenIdList from '../pages/openid/openid-list'; import MarketPage from '../pages/market/listing'; +import { ConstellationIndex } from '../pages/constellation'; // render - dashboard @@ -44,6 +45,10 @@ const MainRoutes = { path: '/cosmos-ui/dashboard', element: }, + { + path: '/cosmos-ui/constellation', + element: + }, { path: '/cosmos-ui/servapps', element: diff --git a/client/src/utils/indexs.js b/client/src/utils/indexs.js index cf7a756..8caa9b5 100644 --- a/client/src/utils/indexs.js +++ b/client/src/utils/indexs.js @@ -1,3 +1,5 @@ +import { Button } from "@mui/material"; + export const randomString = (length) => { let text = ""; const possible = @@ -45,4 +47,4 @@ export const redirectToLocal = (url) => { throw new Error("URL must be local"); } window.location.href = url; -} \ No newline at end of file +} diff --git a/dockerfile b/dockerfile index ccbe859..e7949ce 100644 --- a/dockerfile +++ b/dockerfile @@ -29,7 +29,7 @@ WORKDIR /app COPY build/cosmos build/cosmos-arm64 ./ # Copy other resources -COPY build/cosmos_gray.png build/Logo.png build/GeoLite2-Country.mmdb build/meta.json ./ +COPY build/* ./ COPY static ./static # Run the respective binary based on the BINARY_NAME diff --git a/dockerfile.arm64 b/dockerfile.arm64 index 2a313e2..79b1f58 100644 --- a/dockerfile.arm64 +++ b/dockerfile.arm64 @@ -13,7 +13,8 @@ RUN apt-get update \ WORKDIR /app -COPY build/cosmos build/cosmos_gray.png build/Logo.png build/GeoLite2-Country.mmdb build/meta.json ./ + +COPY build/* ./ COPY static ./static CMD ["./cosmos"] diff --git a/package.json b/package.json index bcbcb59..aa4b7dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.9.21", + "version": "0.10.0-unstable", "description": "", "main": "test-server.js", "bugs": { @@ -63,13 +63,12 @@ "scripts": { "client": "vite", "client-build": "vite build --base=/cosmos-ui/", - "start": "env CONFIG_FILE=./config_dev.json EZ=UTC ACME_STAGING=true build/cosmos", + "start": "env COSMOS_CONFIG_FOLDER=/mnt/e/work/Cosmos-Server/zz_test_config/ CONFIG_FILE=./config_dev.json EZ=UTC ACME_STAGING=true build/cosmos", "build": "sh build.sh", "dev": "npm run build && npm run start", "dockerdevbuild": "sh build.sh && docker build -f dockerfile.local --tag cosmos-dev .", - "dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run -d -p 7200:443 -p 80:80 -p 443:443 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev", - "dockerdev": "npm run dockerdevbuild && npm run dockerdevrun", - "dockerdevclient": "npm run client-build && npm run dockerdevbuild && npm run dockerdevrun", + "dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run --cap-add NET_ADMIN -d -p 7200:443 -p 80:80 -p 443:443 -p 4242:4242 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev", + "dockerdev": "npm run client-build && npm run dockerdevbuild && npm run dockerdevrun", "demo": "vite build --base=/cosmos-ui/ --mode demo", "devdemo": "vite --mode demo" }, diff --git a/src/background.go b/src/background.go index fc5eda4..b096d0f 100644 --- a/src/background.go +++ b/src/background.go @@ -54,7 +54,7 @@ func UploadBackground(w http.ResponseWriter, req *http.Request) { } // create a new file in the config directory - dst, err := os.Create("/config/background" + ext) + dst, err := os.Create(utils.CONFIGFOLDER + "background" + ext) if err != nil { utils.HTTPError(w, "Error creating destination file", http.StatusInternalServerError, "FILE004") return @@ -99,7 +99,7 @@ func GetBackground(w http.ResponseWriter, req *http.Request) { if(req.Method == "GET") { // get the background image - bg, err := ioutil.ReadFile("/config/background." + ext) + bg, err := ioutil.ReadFile(utils.CONFIGFOLDER + "background." + ext) if err != nil { utils.HTTPError(w, "Error reading background image", http.StatusInternalServerError, "FILE003") return diff --git a/src/configapi/get.go b/src/configapi/get.go index fac06ea..7af53bc 100644 --- a/src/configapi/get.go +++ b/src/configapi/get.go @@ -42,6 +42,7 @@ func ConfigApiGet(w http.ResponseWriter, req *http.Request) { "data": config, "updates": utils.UpdateAvailable, "hostname": os.Getenv("HOSTNAME"), + "isAdmin": isAdmin, }) } else { utils.Error("SettingGet: Method not allowed" + req.Method, nil) diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go new file mode 100644 index 0000000..014132c --- /dev/null +++ b/src/constellation/api_devices_create.go @@ -0,0 +1,129 @@ +package constellation + +import ( + "net/http" + "encoding/json" + "go.mongodb.org/mongo-driver/mongo" + + "github.com/azukaar/cosmos-server/src/utils" +) + +type DeviceCreateRequestJSON struct { + Nickname string `json:"nickname",validate:"required,min=3,max=32,alphanum"` + DeviceName string `json:"deviceName",validate:"required,min=3,max=32,alphanum"` + IP string `json:"ip",validate:"required,ipv4"` + PublicKey string `json:"publicKey",omitempty` +} + +func DeviceCreate(w http.ResponseWriter, req *http.Request) { + + if(req.Method == "POST") { + var request DeviceCreateRequestJSON + err1 := json.NewDecoder(req.Body).Decode(&request) + if err1 != nil { + utils.Error("ConstellationDeviceCreation: Invalid User Request", err1) + utils.HTTPError(w, "Device Creation Error", + http.StatusInternalServerError, "DC001") + return + } + + errV := utils.Validate.Struct(request) + if errV != nil { + utils.Error("DeviceCreation: Invalid User Request", errV) + utils.HTTPError(w, "Device Creation Error: " + errV.Error(), + http.StatusInternalServerError, "DC002") + return + } + + nickname := utils.Sanitize(request.Nickname) + deviceName := utils.Sanitize(request.DeviceName) + + if utils.AdminOrItselfOnly(w, req, nickname) != nil { + return + } + + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") + if errCo != nil { + utils.Error("Database Connect", errCo) + utils.HTTPError(w, "Database", http.StatusInternalServerError, "DB001") + return + } + + device := utils.Device{} + + utils.Debug("ConstellationDeviceCreation: Creating Device " + deviceName) + + err2 := c.FindOne(nil, map[string]interface{}{ + "DeviceName": deviceName, + }).Decode(&device) + + if err2 == mongo.ErrNoDocuments { + cert, key, err := generateNebulaCert(deviceName, request.IP, false) + + if err != nil { + utils.Error("DeviceCreation: Error while creating Device", err) + utils.HTTPError(w, "Device Creation Error: " + err.Error(), + http.StatusInternalServerError, "DC001") + return + } + + _, err3 := c.InsertOne(nil, map[string]interface{}{ + "Nickname": nickname, + "DeviceName": deviceName, + "PublicKey": cert, + "PrivateKey": key, + "IP": request.IP, + }) + + if err3 != nil { + utils.Error("DeviceCreation: Error while creating Device", err3) + utils.HTTPError(w, "Device Creation Error: " + err.Error(), + http.StatusInternalServerError, "DC004") + return + } + + // read configYml from config/nebula.yml + configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml") + if err != nil { + utils.Error("DeviceCreation: Error while reading config", err) + utils.HTTPError(w, "Device Creation Error: " + err.Error(), + http.StatusInternalServerError, "DC005") + return + } + + capki, err := getCApki() + if err != nil { + utils.Error("DeviceCreation: Error while reading ca.crt", err) + utils.HTTPError(w, "Device Creation Error: " + err.Error(), + http.StatusInternalServerError, "DC006") + return + } + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + "data": map[string]interface{}{ + "Nickname": nickname, + "DeviceName": deviceName, + "PublicKey": cert, + "PrivateKey": key, + "IP": request.IP, + "Config": configYml, + "CA": capki, + }, + }) + } else if err2 == nil { + utils.Error("DeviceCreation: Device already exists", nil) + utils.HTTPError(w, "Device name already exists", http.StatusConflict, "DC002") + return + } else { + utils.Error("DeviceCreation: Error while finding device", err2) + utils.HTTPError(w, "Device Creation Error: " + err2.Error(), + http.StatusInternalServerError, "DC001") + return + } + } else { + utils.Error("DeviceCreation: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} \ No newline at end of file diff --git a/src/constellation/api_devices_index.go b/src/constellation/api_devices_index.go new file mode 100644 index 0000000..38b2a12 --- /dev/null +++ b/src/constellation/api_devices_index.go @@ -0,0 +1,18 @@ +package constellation + +import ( + "net/http" + "github.com/azukaar/cosmos-server/src/utils" +) + +func ConstellationAPIDevices(w http.ResponseWriter, req *http.Request) { + if (req.Method == "GET") { + DeviceList(w, req) + } else if (req.Method == "POST") { + DeviceCreate(w, req) + } else { + utils.Error("UserRoute: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} \ No newline at end of file diff --git a/src/constellation/api_devices_list.go b/src/constellation/api_devices_list.go new file mode 100644 index 0000000..c2efc0f --- /dev/null +++ b/src/constellation/api_devices_list.go @@ -0,0 +1,83 @@ +package constellation + +import ( + "net/http" + "encoding/json" + + + "github.com/azukaar/cosmos-server/src/utils" +) + +func DeviceList(w http.ResponseWriter, req *http.Request) { + // Check for GET method + if req.Method != "GET" { + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP002") + return + } + + if utils.LoggedInOnly(w, req) != nil { + return + } + + isAdmin := utils.IsAdmin(req) + + // Connect to the collection + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") + if errCo != nil { + utils.Error("Database Connect", errCo) + utils.HTTPError(w, "Database", http.StatusInternalServerError, "DB001") + return + } + + var devices []utils.Device + + // Check if user is an admin + if isAdmin { + // If admin, get all devices + cursor, err := c.Find(nil, map[string]interface{}{}) + if err != nil { + utils.Error("DeviceList: Error fetching devices", err) + utils.HTTPError(w, "Error fetching devices", http.StatusInternalServerError, "DL001") + return + } + defer cursor.Close(nil) + + if err = cursor.All(nil, &devices); err != nil { + utils.Error("DeviceList: Error decoding devices", err) + utils.HTTPError(w, "Error decoding devices", http.StatusInternalServerError, "DL002") + return + } + + // Remove the private key from the response + for i := range devices { + devices[i].PrivateKey = "" + } + } else { + // If not admin, get user's devices based on their nickname + nickname := req.Header.Get("x-cosmos-user") + cursor, err := c.Find(nil, map[string]interface{}{"Nickname": nickname}) + if err != nil { + utils.Error("DeviceList: Error fetching devices", err) + utils.HTTPError(w, "Error fetching devices", http.StatusInternalServerError, "DL003") + return + } + defer cursor.Close(nil) + + if err = cursor.All(nil, &devices); err != nil { + utils.Error("DeviceList: Error decoding devices", err) + utils.HTTPError(w, "Error decoding devices", http.StatusInternalServerError, "DL004") + return + } + + // Remove the private key from the response + for i := range devices { + devices[i].PrivateKey = "" + } + } + + // Respond with the list of devices + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + "data": devices, + }) +} diff --git a/src/constellation/index.go b/src/constellation/index.go new file mode 100644 index 0000000..07426ba --- /dev/null +++ b/src/constellation/index.go @@ -0,0 +1,42 @@ +package constellation + +import ( + "github.com/azukaar/cosmos-server/src/utils" + "os" +) + +func Init() { + // if Constellation is enabled + if utils.GetMainConfig().ConstellationConfig.Enabled { + InitConfig() + + utils.Log("Initializing Constellation module...") + + // check if ca.crt exists + if _, err := os.Stat(utils.CONFIGFOLDER + "ca.crt"); os.IsNotExist(err) { + utils.Log("Constellation: ca.crt not found, generating...") + // generate ca.crt + generateNebulaCACert("Cosmos - " + utils.GetMainConfig().HTTPConfig.Hostname) + } + + // check if cosmos.crt exists + if _, err := os.Stat(utils.CONFIGFOLDER + "cosmos.crt"); os.IsNotExist(err) { + utils.Log("Constellation: cosmos.crt not found, generating...") + // generate cosmos.crt + generateNebulaCert("cosmos", "192.168.201.0/24", true) + } + + // export nebula.yml + utils.Log("Constellation: exporting nebula.yml...") + ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml") + + // start nebula + utils.Log("Constellation: starting nebula...") + err := startNebulaInBackground() + if err != nil { + utils.Error("Constellation: error while starting nebula", err) + } + + utils.Log("Constellation module initialized") + } +} \ No newline at end of file diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go new file mode 100644 index 0000000..227b331 --- /dev/null +++ b/src/constellation/nebula.go @@ -0,0 +1,283 @@ +package constellation + +import ( + "github.com/azukaar/cosmos-server/src/utils" + "os/exec" + "os" + "fmt" + "errors" + "runtime" + "sync" + "gopkg.in/yaml.v2" + "strings" + "io/ioutil" + "strconv" +) + +var ( + process *exec.Cmd + processMux sync.Mutex +) + +func binaryToRun() string { + if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { + return "./nebula-arm" + } + return "./nebula" +} + +func startNebulaInBackground() error { + processMux.Lock() + defer processMux.Unlock() + + if process != nil { + return errors.New("nebula is already running") + } + + process = exec.Command(binaryToRun(), "-config", utils.CONFIGFOLDER + "nebula.yml") + + process.Stderr = os.Stderr + + if utils.LoggingLevelLabels[utils.GetMainConfig().LoggingLevel] == utils.DEBUG { + process.Stdout = os.Stdout + } else { + process.Stdout = nil + } + + // Start the process in the background + if err := process.Start(); err != nil { + return err + } + + utils.Log(fmt.Sprintf("%s started with PID %d\n", binaryToRun(), process.Process.Pid)) + return nil +} + +func stop() error { + processMux.Lock() + defer processMux.Unlock() + + if process == nil { + return errors.New("nebula is not running") + } + + if err := process.Process.Kill(); err != nil { + return err + } + process = nil + utils.Log("Stopped nebula.") + return nil +} + +func restart() error { + if err := stop(); err != nil { + return err + } + return startNebulaInBackground() +} + +func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath string) error { + // Combine defaultConfig and overwriteConfig + finalConfig := NebulaDefaultConfig + + finalConfig.StaticHostMap = map[string][]string{ + "192.168.201.0": []string{utils.GetMainConfig().HTTPConfig.Hostname + ":4242"}, + } + + // Marshal the combined config to YAML + yamlData, err := yaml.Marshal(finalConfig) + if err != nil { + return err + } + + // Write YAML data to the specified file + yamlFile, err := os.Create(outputPath) + if err != nil { + return err + } + defer yamlFile.Close() + + _, err = yamlFile.Write(yamlData) + if err != nil { + return err + } + + return nil +} + +func getYAMLClientConfig(name, configPath string) (string, error) { + utils.Log("Exporting YAML config for " + name + " with file " + configPath) + + // Read the YAML config file + yamlData, err := ioutil.ReadFile(configPath) + if err != nil { + return "", err + } + + // Unmarshal the YAML data into a map interface + var configMap map[string]interface{} + err = yaml.Unmarshal(yamlData, &configMap) + if err != nil { + return "", err + } + + // set lightHouse to false + if lighthouseMap, ok := configMap["lighthouse"].(map[interface{}]interface{}); ok { + lighthouseMap["am_lighthouse"] = false + + lighthouseMap["hosts"] = []string{ + "192.168.201.0", + } + } else { + return "", errors.New("lighthouse not found in nebula.yml") + } + + if pkiMap, ok := configMap["pki"].(map[interface{}]interface{}); ok { + pkiMap["ca"] = "ca.crt" + pkiMap["cert"] = name + ".crt" + pkiMap["key"] = name + ".key" + } else { + return "", errors.New("pki not found in nebula.yml") + } + + // export configMap as YML + yamlData, err = yaml.Marshal(configMap) + if err != nil { + return "", err + } + + return string(yamlData), nil +} + +func getCApki() (string, error) { + // read config/ca.crt + caCrt, err := ioutil.ReadFile(utils.CONFIGFOLDER + "ca.crt") + if err != nil { + return "", err + } + + return string(caCrt), nil +} + +func killAllNebulaInstances() error { + processMux.Lock() + defer processMux.Unlock() + + cmd := exec.Command("ps", "-e", "-o", "pid,command") + output, err := cmd.CombinedOutput() + if err != nil { + return err + } + + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if strings.Contains(line, binaryToRun()) { + fields := strings.Fields(line) + if len(fields) > 1 { + pid := fields[0] + pidInt, _ := strconv.Atoi(pid) + process, err := os.FindProcess(pidInt) + if err != nil { + return err + } + err = process.Kill() + if err != nil { + return err + } + utils.Log(fmt.Sprintf("Killed Nebula instance with PID %s\n", pid)) + } + } + } + + return nil +} + +func generateNebulaCert(name, ip string, saveToFile bool) (string, string, error) { + // Run the nebula-cert command + cmd := exec.Command(binaryToRun() + "-cert", + "sign", + "-ca-crt", utils.CONFIGFOLDER + "ca.crt", + "-ca-key", utils.CONFIGFOLDER + "ca.key", + "-name", name, + "-ip", ip, + ) + utils.Debug(cmd.String()) + + cmd.Stderr = os.Stderr + + if utils.LoggingLevelLabels[utils.GetMainConfig().LoggingLevel] == utils.DEBUG { + cmd.Stdout = os.Stdout + } else { + cmd.Stdout = nil + } + + cmd.Run() + + if cmd.ProcessState.ExitCode() != 0 { + return "", "", fmt.Errorf("nebula-cert exited with an error, check the Cosmos logs") + } + + // Read the generated certificate and key files + certPath := fmt.Sprintf("./%s.crt", name) + keyPath := fmt.Sprintf("./%s.key", name) + + utils.Debug("Reading certificate from " + certPath) + utils.Debug("Reading key from " + keyPath) + + certContent, errCert := ioutil.ReadFile(certPath) + if errCert != nil { + return "", "", fmt.Errorf("failed to read certificate file: %s", errCert) + } + + keyContent, errKey := ioutil.ReadFile(keyPath) + if errKey != nil { + return "", "", fmt.Errorf("failed to read key file: %s", errKey) + } + + if saveToFile { + cmd = exec.Command("mv", certPath, utils.CONFIGFOLDER + name + ".crt") + utils.Debug(cmd.String()) + cmd.Run() + cmd = exec.Command("mv", keyPath, utils.CONFIGFOLDER + name + ".key") + utils.Debug(cmd.String()) + cmd.Run() + } else { + // Delete the generated certificate and key files + if err := os.Remove(certPath); err != nil { + return "", "", fmt.Errorf("failed to delete certificate file: %s", err) + } + + if err := os.Remove(keyPath); err != nil { + return "", "", fmt.Errorf("failed to delete key file: %s", err) + } + } + + return string(certContent), string(keyContent), nil +} + +func generateNebulaCACert(name string) (error) { + // Run the nebula-cert command to generate CA certificate and key + cmd := exec.Command(binaryToRun() + "-cert", "ca", "-name", "\""+name+"\"") + + utils.Debug(cmd.String()) + + cmd.Stderr = os.Stderr + + if utils.LoggingLevelLabels[utils.GetMainConfig().LoggingLevel] == utils.DEBUG { + cmd.Stdout = os.Stdout + } else { + cmd.Stdout = nil + } + + if err := cmd.Run(); err != nil { + return fmt.Errorf("nebula-cert error: %s", err) + } + + // copy to /config/ca.* + cmd = exec.Command("mv", "./ca.crt", utils.CONFIGFOLDER + "ca.crt") + cmd.Run() + cmd = exec.Command("mv", "./ca.key", utils.CONFIGFOLDER + "ca.key") + cmd.Run() + + return nil +} \ No newline at end of file diff --git a/src/constellation/nebula_default.go b/src/constellation/nebula_default.go new file mode 100644 index 0000000..1264ff7 --- /dev/null +++ b/src/constellation/nebula_default.go @@ -0,0 +1,95 @@ +package constellation + +import ( + "github.com/azukaar/cosmos-server/src/utils" +) + +var NebulaDefaultConfig utils.NebulaConfig + +func InitConfig() { + NebulaDefaultConfig = utils.NebulaConfig { + PKI: struct { + CA string `yaml:"ca"` + Cert string `yaml:"cert"` + Key string `yaml:"key"` + }{ + CA: utils.CONFIGFOLDER + "ca.crt", + Cert: utils.CONFIGFOLDER + "cosmos.crt", + Key: utils.CONFIGFOLDER + "cosmos.key", + }, + StaticHostMap: map[string][]string{ + + }, + Lighthouse: struct { + AMLighthouse bool `yaml:"am_lighthouse"` + Interval int `yaml:"interval"` + Hosts []string `yaml:"hosts"` + }{ + AMLighthouse: true, + Interval: 60, + Hosts: []string{}, + }, + Listen: struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + }{ + Host: "0.0.0.0", + Port: 4242, + }, + Punchy: struct { + Punch bool `yaml:"punch"` + }{ + Punch: true, + }, + Relay: struct { + AMRelay bool `yaml:"am_relay"` + UseRelays bool `yaml:"use_relays"` + }{ + AMRelay: false, + UseRelays: true, + }, + TUN: struct { + Disabled bool `yaml:"disabled"` + Dev string `yaml:"dev"` + DropLocalBroadcast bool `yaml:"drop_local_broadcast"` + DropMulticast bool `yaml:"drop_multicast"` + TxQueue int `yaml:"tx_queue"` + MTU int `yaml:"mtu"` + Routes []string `yaml:"routes"` + UnsafeRoutes []string `yaml:"unsafe_routes"` + }{ + Disabled: false, + Dev: "nebula1", + DropLocalBroadcast: false, + DropMulticast: false, + TxQueue: 500, + MTU: 1300, + Routes: nil, + UnsafeRoutes: nil, + }, + Logging: struct { + Level string `yaml:"level"` + Format string `yaml:"format"` + }{ + Level: "info", + Format: "text", + }, + Firewall: struct { + OutboundAction string `yaml:"outbound_action"` + InboundAction string `yaml:"inbound_action"` + Conntrack utils.NebulaConntrackConfig `yaml:"conntrack"` + Outbound []utils.NebulaFirewallRule `yaml:"outbound"` + Inbound []utils.NebulaFirewallRule `yaml:"inbound"` + }{ + OutboundAction: "drop", + InboundAction: "drop", + Conntrack: utils.NebulaConntrackConfig{ + TCPTimeout: "12m", + UDPTimeout: "3m", + DefaultTimeout: "10m", + }, + Outbound: nil, + Inbound: nil, + }, + } +} \ No newline at end of file diff --git a/src/httpServer.go b/src/httpServer.go index 1e9c2c9..49e7713 100644 --- a/src/httpServer.go +++ b/src/httpServer.go @@ -9,6 +9,7 @@ import ( "github.com/azukaar/cosmos-server/src/docker" "github.com/azukaar/cosmos-server/src/authorizationserver" "github.com/azukaar/cosmos-server/src/market" + "github.com/azukaar/cosmos-server/src/constellation" "github.com/gorilla/mux" "strconv" "time" @@ -331,6 +332,7 @@ func InitServer() *mux.Router { srapi.HandleFunc("/api/background", UploadBackground) srapi.HandleFunc("/api/background/{ext}", GetBackground) + srapi.HandleFunc("/api/constellation/devices", constellation.ConstellationAPIDevices) if(!config.HTTPConfig.AcceptAllInsecureHostname) { srapi.Use(utils.EnsureHostname) diff --git a/src/index.go b/src/index.go index bc1a1b8..4e9fc2d 100644 --- a/src/index.go +++ b/src/index.go @@ -9,6 +9,7 @@ import ( "github.com/azukaar/cosmos-server/src/utils" "github.com/azukaar/cosmos-server/src/authorizationserver" "github.com/azukaar/cosmos-server/src/market" + "github.com/azukaar/cosmos-server/src/constellation" ) func main() { @@ -44,5 +45,7 @@ func main() { authorizationserver.Init() + constellation.Init() + StartServer() } diff --git a/src/user/create.go b/src/user/create.go index 9e6b38b..b2e2f1c 100644 --- a/src/user/create.go +++ b/src/user/create.go @@ -2,12 +2,9 @@ package user import ( "net/http" - // "io" - // "os" "encoding/json" "go.mongodb.org/mongo-driver/mongo" "time" - // "golang.org/x/crypto/bcrypt" "github.com/azukaar/cosmos-server/src/utils" ) diff --git a/src/utils/certificates.go b/src/utils/certificates.go index 9ea6dd0..5e19a5c 100644 --- a/src/utils/certificates.go +++ b/src/utils/certificates.go @@ -181,20 +181,20 @@ func DoLetsEncrypt() (string, string) { } err = client.Challenge.SetDNS01Provider(provider) - } + } else { + err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", config.HTTPConfig.HTTPPort)) + if err != nil { + Error("LETSENCRYPT_HTTP01", err) + LetsEncryptErrors = append(LetsEncryptErrors, err.Error()) + return "", "" + } - err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", config.HTTPConfig.HTTPPort)) - if err != nil { - Error("LETSENCRYPT_HTTP01", err) - LetsEncryptErrors = append(LetsEncryptErrors, err.Error()) - return "", "" - } - - err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", config.HTTPConfig.HTTPSPort)) - if err != nil { - Error("LETSENCRYPT_TLS01", err) - LetsEncryptErrors = append(LetsEncryptErrors, err.Error()) - return "", "" + err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", config.HTTPConfig.HTTPSPort)) + if err != nil { + Error("LETSENCRYPT_TLS01", err) + LetsEncryptErrors = append(LetsEncryptErrors, err.Error()) + return "", "" + } } // New users will need to register diff --git a/src/utils/types.go b/src/utils/types.go index 1f6d9f3..7b1d85d 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -90,6 +90,7 @@ type Config struct { MarketConfig MarketConfig HomepageConfig HomepageConfig ThemeConfig ThemeConfig + ConstellationConfig ConstellationConfig } type HomepageConfig struct { @@ -205,4 +206,84 @@ type MarketConfig struct { type MarketSource struct { Name string Url string -} \ No newline at end of file +} + +type ConstellationConfig struct { + Enabled bool + NebulaConfig NebulaConfig +} + +type NebulaFirewallRule struct { + Port string `yaml:"port"` + Proto string `yaml:"proto"` + Host string `yaml:"host"` + Groups []string `yaml:"groups,omitempty"omitempty"` +} + +type NebulaConntrackConfig struct { + TCPTimeout string `yaml:"tcp_timeout"` + UDPTimeout string `yaml:"udp_timeout"` + DefaultTimeout string `yaml:"default_timeout"` +} + +type NebulaConfig struct { + PKI struct { + CA string `yaml:"ca"` + Cert string `yaml:"cert"` + Key string `yaml:"key"` + } `yaml:"pki"` + + StaticHostMap map[string][]string `yaml:"static_host_map"` + + Lighthouse struct { + AMLighthouse bool `yaml:"am_lighthouse"` + Interval int `yaml:"interval"` + Hosts []string `yaml:"hosts"` + } `yaml:"lighthouse"` + + Listen struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + } `yaml:"listen"` + + Punchy struct { + Punch bool `yaml:"punch"` + } `yaml:"punchy"` + + Relay struct { + AMRelay bool `yaml:"am_relay"` + UseRelays bool `yaml:"use_relays"` + } `yaml:"relay"` + + TUN struct { + Disabled bool `yaml:"disabled"` + Dev string `yaml:"dev"` + DropLocalBroadcast bool `yaml:"drop_local_broadcast"` + DropMulticast bool `yaml:"drop_multicast"` + TxQueue int `yaml:"tx_queue"` + MTU int `yaml:"mtu"` + Routes []string `yaml:"routes"` + UnsafeRoutes []string `yaml:"unsafe_routes"` + } `yaml:"tun"` + + Logging struct { + Level string `yaml:"level"` + Format string `yaml:"format"` + } `yaml:"logging"` + + Firewall struct { + OutboundAction string `yaml:"outbound_action"` + InboundAction string `yaml:"inbound_action"` + Conntrack NebulaConntrackConfig `yaml:"conntrack"` + Outbound []NebulaFirewallRule `yaml:"outbound"` + Inbound []NebulaFirewallRule `yaml:"inbound"` + } `yaml:"firewall"` +} + +type Device struct { + DeviceName string `json:"deviceName",validate:"required,min=3,max=32,alphanum"` + Nickname string `json:"nickname",validate:"required,min=3,max=32,alphanum"` + PublicKey string `json:"publicKey",omitempty` + PrivateKey string `json:"privateKey",omitempty` + IP string `json:"ip",validate:"required,ipv4"` +} diff --git a/src/utils/utils.go b/src/utils/utils.go index 6177a0c..a64f1d7 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -37,6 +37,8 @@ var ReBootstrapContainer func(string) error var LetsEncryptErrors = []string{} +var CONFIGFOLDER = "/config/" + var DefaultConfig = Config{ LoggingLevel: "INFO", NewInstall: true, @@ -193,6 +195,10 @@ func LoadBaseMainConfig(config Config) { if os.Getenv("COSMOS_SERVER_COUNTRY") != "" { MainConfig.ServerCountry = os.Getenv("COSMOS_SERVER_COUNTRY") } + if os.Getenv("COSMOS_CONFIG_FOLDER") != "" { + Log("Overwriting config folder with " + os.Getenv("COSMOS_CONFIG_FOLDER")) + CONFIGFOLDER = os.Getenv("COSMOS_CONFIG_FOLDER") + } if MainConfig.DockerConfig.DefaultDataPath == "" { MainConfig.DockerConfig.DefaultDataPath = "/usr" @@ -219,7 +225,7 @@ func GetConfigFileName() string { configFile := os.Getenv("CONFIG_FILE") if configFile == "" { - configFile = "/config/cosmos.config.json" + configFile = CONFIGFOLDER + "cosmos.config.json" } return configFile From f127e5a1463c06ecab68090bbb154283be9f4788 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 20 Aug 2023 11:49:12 +0100 Subject: [PATCH 02/36] [release] v0.10.0-unstable1 --- client/src/index.css | 2 +- package.json | 2 +- src/utils/utils.go | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/index.css b/client/src/index.css index 3c6d9ab..e7d1479 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -124,7 +124,7 @@ } .loading-image { - background: url('/assets/images/icons/cosmos_gray.png') no-repeat center center; + background: url('assets/images/icons/cosmos_gray.png') no-repeat center center; } .raw-table table { diff --git a/package.json b/package.json index aa4b7dd..649a5f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable", + "version": "0.10.0-unstable1", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/utils/utils.go b/src/utils/utils.go index a64f1d7..cf4103d 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -62,6 +62,9 @@ var DefaultConfig = Config{ Sources: []MarketSource{ }, }, + ConstellationConfig: ConstellationConfig{ + Enabled: true, + }, } func FileExists(path string) bool { From 8e83fde02ef5c5912f0b6786aa815609bf785e88 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Tue, 22 Aug 2023 15:39:06 +0100 Subject: [PATCH 03/36] [release] v0.10.0-unstable2 --- client/src/index.css | 3 +- .../src/pages/config/routes/routeoverview.jsx | 4 +- .../src/pages/config/users/formShortcuts.jsx | 2 +- client/src/pages/constellation/addDevice.jsx | 75 ++++++++++++++++--- client/src/pages/constellation/index.jsx | 52 ++++++++++++- package.json | 2 +- src/configapi/set.go | 2 + src/constellation/api_devices_create.go | 5 +- src/constellation/index.go | 2 +- src/constellation/nebula.go | 47 ++++++++---- 10 files changed, 158 insertions(+), 36 deletions(-) diff --git a/client/src/index.css b/client/src/index.css index e7d1479..26dece5 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -123,8 +123,9 @@ align-items: center; } -.loading-image { +.loading-image:empty { background: url('assets/images/icons/cosmos_gray.png') no-repeat center center; + background-size: contain; } .raw-table table { diff --git a/client/src/pages/config/routes/routeoverview.jsx b/client/src/pages/config/routes/routeoverview.jsx index b17ca2b..f7829b1 100644 --- a/client/src/pages/config/routes/routeoverview.jsx +++ b/client/src/pages/config/routes/routeoverview.jsx @@ -1,7 +1,7 @@ import * as React from 'react'; import MainCard from '../../../components/MainCard'; import RestartModal from '../users/restart'; -import { Chip, Divider, Stack, useMediaQuery } from '@mui/material'; +import { Checkbox, Chip, Divider, FormControlLabel, Stack, useMediaQuery } from '@mui/material'; import HostChip from '../../../components/hostChip'; import { RouteMode, RouteSecurity } from '../../../components/routeComponents'; import { getFaviconURL } from '../../../utils/routes'; @@ -9,6 +9,8 @@ import * as API from '../../../api'; import { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons"; import IsLoggedIn from '../../../isLoggedIn'; import { redirectToLocal } from '../../../utils/indexs'; +import { CosmosCheckbox } from '../users/formShortcuts'; +import { Field } from 'formik'; const info = { backgroundColor: 'rgba(0, 0, 0, 0.1)', diff --git a/client/src/pages/config/users/formShortcuts.jsx b/client/src/pages/config/users/formShortcuts.jsx index 78d9c43..850bf8e 100644 --- a/client/src/pages/config/users/formShortcuts.jsx +++ b/client/src/pages/config/users/formShortcuts.jsx @@ -206,7 +206,7 @@ export const CosmosCollapse = ({ children, title }) => { export function CosmosFormDivider({title}) { return - + {title && } } diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 85de900..22b47dc 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -12,20 +12,56 @@ import { PlusCircleFilled } from '@ant-design/icons'; import { Formik } from 'formik'; import * as yup from 'yup'; import * as API from '../../api'; -import { CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; +import { CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; import { DownloadFile } from '../../api/downloadButton'; +import QRCode from 'qrcode'; -const AddDeviceModal = ({ config, isAdmin, refreshConfig, devices }) => { +const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { const [openModal, setOpenModal] = useState(false); const [isDone, setIsDone] = useState(null); + const canvasRef = React.useRef(null); + + let firstIP = "192.168.201.1/24"; + if (devices && devices.length > 0) { + const isIpFree = (ip) => { + return devices.filter((d) => d.ip === ip).length === 0; + } + let i = 1; + let j = 201; + while (!isIpFree(firstIP)) { + i++; + if (i > 254) { + i = 0; + j++; + } + firstIP = "192.168." + j + "." + i + "/24"; + } + } + + const renderCanvas = (data) => { + if (!canvasRef.current) return setTimeout(() => { + renderCanvas(data); + }, 500); + + QRCode.toCanvas(canvasRef.current, JSON.stringify(data), + { + width: 600, + color: { + dark: "#000", + light: '#fff' + } + }, function (error) { + if (error) console.error(error) + }) + } return <> setOpenModal(false)}> { return API.constellation.addDevice(values).then(({data}) => { setIsDone(data); refreshConfig(); + renderCanvas(data); }).catch((err) => { setErrors(err.response.data); }); @@ -43,16 +80,30 @@ const AddDeviceModal = ({ config, isAdmin, refreshConfig, devices }) => { > {(formik) => (
- Manually Add Device + Add Device {isDone ?

Device added successfully! - Download the private and public keys to your device along side the config and network certificate to connect: + Download scan the QR Code from the Cosmos app or download the relevant + files to your device along side the config and network certificate to + connect:

+ +
+ +
+ + + + {
: -

Manually add a device to the constellation. It is recommended that you use the Cosmos app instead. Use this form to add another Nebula device manually

+

Add a device to the constellation using either the Cosmos or Nebula client

{ label="Owner" formik={formik} // disabled={!isAdmin} - options={[ - ["admin", "admin"] - ]} + options={ + users.map((u) => { + return [u.nickname, u.nickname] + }) + } /> { variant="contained" startIcon={} > - Manually Add Device + Add Device ; }; diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index d0550bb..7c447d1 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -6,10 +6,16 @@ import PrettyTableView from "../../components/tableView/prettyTableView"; import { DeleteButton } from "../../components/delete"; import { CloudOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; import IsLoggedIn from "../../isLoggedIn"; +import { Button, CircularProgress, Stack } from "@mui/material"; +import { CosmosCheckbox, CosmosFormDivider } from "../config/users/formShortcuts"; +import MainCard from "../../components/MainCard"; +import { Formik } from "formik"; +import { LoadingButton } from "@mui/lab"; export const ConstellationIndex = () => { const [isAdmin, setIsAdmin] = useState(false); const [config, setConfig] = useState(null); + const [users, setUsers] = useState(null); const [devices, setDevices] = useState(null); const refreshConfig = async () => { @@ -17,6 +23,7 @@ export const ConstellationIndex = () => { setConfig(configAsync.data); setIsAdmin(configAsync.isAdmin); setDevices((await API.constellation.list()).data || []); + setUsers((await API.users.list()).data || []); }; useEffect(() => { @@ -40,12 +47,48 @@ export const ConstellationIndex = () => { return <> - {devices && config && <> + {(devices && config && users) ? <> + +
+ + + { + let newConfig = { ...config }; + newConfig.ConstellationConfig.Enabled = values.Enabled; + return API.config.set(newConfig); + }} + > + {(formik) => ( + + + + + Save + + + + )} + + + +
+ r.deviceName} buttons={[ - + ]} columns={[ { @@ -76,6 +119,9 @@ export const ConstellationIndex = () => { } ]} /> - } +
+ :
+ +
} }; \ No newline at end of file diff --git a/package.json b/package.json index 649a5f8..11dd3e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable1", + "version": "0.10.0-unstable2", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/configapi/set.go b/src/configapi/set.go index 85b5624..a2aad63 100644 --- a/src/configapi/set.go +++ b/src/configapi/set.go @@ -5,6 +5,7 @@ import ( "encoding/json" "github.com/azukaar/cosmos-server/src/utils" "github.com/azukaar/cosmos-server/src/authorizationserver" + "github.com/azukaar/cosmos-server/src/constellation" ) func ConfigApiSet(w http.ResponseWriter, req *http.Request) { @@ -43,6 +44,7 @@ func ConfigApiSet(w http.ResponseWriter, req *http.Request) { utils.DisconnectDB() authorizationserver.Init() utils.RestartHTTPServer() + constellation.RestartNebula() json.NewEncoder(w).Encode(map[string]interface{}{ "status": "OK", diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index 014132c..f18c6f3 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -16,7 +16,6 @@ type DeviceCreateRequestJSON struct { } func DeviceCreate(w http.ResponseWriter, req *http.Request) { - if(req.Method == "POST") { var request DeviceCreateRequestJSON err1 := json.NewDecoder(req.Body).Decode(&request) @@ -58,7 +57,8 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { }).Decode(&device) if err2 == mongo.ErrNoDocuments { - cert, key, err := generateNebulaCert(deviceName, request.IP, false) + + cert, key, err := generateNebulaCert(deviceName, request.IP, request.PublicKey, false) if err != nil { utils.Error("DeviceCreation: Error while creating Device", err) @@ -71,7 +71,6 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { "Nickname": nickname, "DeviceName": deviceName, "PublicKey": cert, - "PrivateKey": key, "IP": request.IP, }) diff --git a/src/constellation/index.go b/src/constellation/index.go index 07426ba..58299c1 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -23,7 +23,7 @@ func Init() { if _, err := os.Stat(utils.CONFIGFOLDER + "cosmos.crt"); os.IsNotExist(err) { utils.Log("Constellation: cosmos.crt not found, generating...") // generate cosmos.crt - generateNebulaCert("cosmos", "192.168.201.0/24", true) + generateNebulaCert("cosmos", "192.168.201.0/24", "", true) } // export nebula.yml diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 227b331..bb5d306 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -58,7 +58,7 @@ func stop() error { defer processMux.Unlock() if process == nil { - return errors.New("nebula is not running") + return nil } if err := process.Process.Kill(); err != nil { @@ -69,11 +69,9 @@ func stop() error { return nil } -func restart() error { - if err := stop(); err != nil { - return err - } - return startNebulaInBackground() +func RestartNebula() { + stop() + Init() } func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath string) error { @@ -192,15 +190,36 @@ func killAllNebulaInstances() error { return nil } -func generateNebulaCert(name, ip string, saveToFile bool) (string, string, error) { +func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, error) { // Run the nebula-cert command - cmd := exec.Command(binaryToRun() + "-cert", - "sign", - "-ca-crt", utils.CONFIGFOLDER + "ca.crt", - "-ca-key", utils.CONFIGFOLDER + "ca.key", - "-name", name, - "-ip", ip, - ) + var cmd *exec.Cmd + + if(PK == "") { + cmd = exec.Command(binaryToRun() + "-cert", + "sign", + "-ca-crt", utils.CONFIGFOLDER + "ca.crt", + "-ca-key", utils.CONFIGFOLDER + "ca.key", + "-name", name, + "-ip", ip, + ) + } else { + // write PK to temp.cert + err := ioutil.WriteFile("./temp.cert", []byte(PK), 0644) + if err != nil { + return "", "", fmt.Errorf("failed to write temp.cert: %s", err) + } + cmd = exec.Command(binaryToRun() + "-cert", + "sign", + "-ca-crt", utils.CONFIGFOLDER + "ca.crt", + "-ca-key", utils.CONFIGFOLDER + "ca.key", + "-name", name, + "-ip", ip, + "-in-pub", "./temp.cert", + ) + // delete temp.cert + defer os.Remove("./temp.cert") + } + utils.Debug(cmd.String()) cmd.Stderr = os.Stderr From bb11cbbe962207ae2ff94a215662e30590a56ead Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sat, 26 Aug 2023 16:23:37 +0100 Subject: [PATCH 04/36] [release] v0.10.0-unstable3 --- client/src/pages/constellation/addDevice.jsx | 4 ++-- package.json | 2 +- src/constellation/api_devices_create.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 22b47dc..8d09d25 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -111,12 +111,12 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { /> Date: Sun, 27 Aug 2023 10:47:54 +0100 Subject: [PATCH 05/36] [release] v0.10.0-unstable4 --- package.json | 2 +- readme.md | 1 - src/constellation/index.go | 6 +++++- src/constellation/nebula_default.go | 16 ++++++++++++++-- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3a6fe6a..c84ddfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable3", + "version": "0.10.0-unstable4", "description": "", "main": "test-server.js", "bugs": { diff --git a/readme.md b/readme.md index eb3ce03..1d141e7 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,6 @@

null null Clayton Stone -Billy Das Seraph91P

diff --git a/src/constellation/index.go b/src/constellation/index.go index 58299c1..0d83c0a 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -28,8 +28,12 @@ func Init() { // export nebula.yml utils.Log("Constellation: exporting nebula.yml...") - ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml") + err := ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml") + if err != nil { + utils.Error("Constellation: error while exporting nebula.yml", err) + } + // start nebula utils.Log("Constellation: starting nebula...") err := startNebulaInBackground() diff --git a/src/constellation/nebula_default.go b/src/constellation/nebula_default.go index 1264ff7..d2ae7d4 100644 --- a/src/constellation/nebula_default.go +++ b/src/constellation/nebula_default.go @@ -88,8 +88,20 @@ func InitConfig() { UDPTimeout: "3m", DefaultTimeout: "10m", }, - Outbound: nil, - Inbound: nil, + Outbound: []utils.NebulaFirewallRule { + utils.NebulaFirewallRule { + Host: "any", + Port: "any", + Proto: "any", + }, + }, + Inbound: []utils.NebulaFirewallRule { + utils.NebulaFirewallRule { + Host: "any", + Port: "any", + Proto: "any", + }, + }, }, } } \ No newline at end of file From 84335be6742a0be3e54535c8b87940faa2934844 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 27 Aug 2023 10:52:34 +0100 Subject: [PATCH 06/36] [release] v0.10.0-unstable4 --- src/constellation/index.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constellation/index.go b/src/constellation/index.go index 0d83c0a..6cf6e33 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -36,7 +36,7 @@ func Init() { // start nebula utils.Log("Constellation: starting nebula...") - err := startNebulaInBackground() + err = startNebulaInBackground() if err != nil { utils.Error("Constellation: error while starting nebula", err) } From da94e1dfeea17bfa7323a8d91105b3006e669cd1 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 27 Aug 2023 13:23:56 +0100 Subject: [PATCH 07/36] [release] v0.10.0-unstable5 --- package.json | 2 +- src/constellation/nebula.go | 3 +++ src/constellation/nebula_default.go | 4 +++- src/utils/types.go | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c84ddfb..f1ec91b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable4", + "version": "0.10.0-unstable5", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index bb5d306..6b99a44 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -78,6 +78,9 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st // Combine defaultConfig and overwriteConfig finalConfig := NebulaDefaultConfig + //... + + // add the hostnames finalConfig.StaticHostMap = map[string][]string{ "192.168.201.0": []string{utils.GetMainConfig().HTTPConfig.Hostname + ":4242"}, } diff --git a/src/constellation/nebula_default.go b/src/constellation/nebula_default.go index d2ae7d4..ae45c6b 100644 --- a/src/constellation/nebula_default.go +++ b/src/constellation/nebula_default.go @@ -38,14 +38,16 @@ func InitConfig() { }, Punchy: struct { Punch bool `yaml:"punch"` + Respond bool `yaml:"respond"` }{ Punch: true, + Respond: true, }, Relay: struct { AMRelay bool `yaml:"am_relay"` UseRelays bool `yaml:"use_relays"` }{ - AMRelay: false, + AMRelay: true, UseRelays: true, }, TUN: struct { diff --git a/src/utils/types.go b/src/utils/types.go index 7b1d85d..2974d80 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -248,6 +248,7 @@ type NebulaConfig struct { Punchy struct { Punch bool `yaml:"punch"` + Respond bool `yaml:"respond"` } `yaml:"punchy"` Relay struct { From ff90bdf51e886b0a6a9122ba10b75ef947d9472d Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 27 Aug 2023 13:37:12 +0100 Subject: [PATCH 08/36] [release] v0.10.0-unstable6 --- package.json | 2 +- src/constellation/nebula.go | 10 +++++++--- src/utils/types.go | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f1ec91b..3a9f808 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable5", + "version": "0.10.0-unstable6", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 6b99a44..75c42b1 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -78,9 +78,6 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st // Combine defaultConfig and overwriteConfig finalConfig := NebulaDefaultConfig - //... - - // add the hostnames finalConfig.StaticHostMap = map[string][]string{ "192.168.201.0": []string{utils.GetMainConfig().HTTPConfig.Hostname + ":4242"}, } @@ -141,6 +138,13 @@ func getYAMLClientConfig(name, configPath string) (string, error) { return "", errors.New("pki not found in nebula.yml") } + if relayMap, ok := configMap["relay"].(map[interface{}]interface{}); ok { + relayMap["am_relay"] = false + relayMap["relays"] = []string{"192.168.201.0"} + } else { + return "", errors.New("relay not found in nebula.yml") + } + // export configMap as YML yamlData, err = yaml.Marshal(configMap) if err != nil { diff --git a/src/utils/types.go b/src/utils/types.go index 2974d80..96e8b5e 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -254,6 +254,7 @@ type NebulaConfig struct { Relay struct { AMRelay bool `yaml:"am_relay"` UseRelays bool `yaml:"use_relays"` + relays []string `yaml:"relays"` } `yaml:"relay"` TUN struct { From 735874006c361be0dc122b9917dc619d0faedf70 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 27 Aug 2023 15:21:13 +0100 Subject: [PATCH 09/36] [release] v0.10.0-unstable7 --- client/src/api/constellation.tsx | 30 ++++++++ client/src/components/apiModal.jsx | 94 ++++++++++++++++++++++++ client/src/pages/constellation/index.jsx | 86 +++++++++++++--------- package.json | 2 +- readme.md | 2 +- src/constellation/api_nebula.go | 75 +++++++++++++++++++ src/constellation/nebula.go | 23 ++++-- src/constellation/nebula_default.go | 2 + src/httpServer.go | 3 + src/utils/types.go | 2 +- 10 files changed, 277 insertions(+), 42 deletions(-) create mode 100644 client/src/components/apiModal.jsx create mode 100644 src/constellation/api_nebula.go diff --git a/client/src/api/constellation.tsx b/client/src/api/constellation.tsx index 717f78a..61b0923 100644 --- a/client/src/api/constellation.tsx +++ b/client/src/api/constellation.tsx @@ -19,7 +19,37 @@ function addDevice(device) { })) } +function restart() { + return wrap(fetch('/cosmos/api/constellation/restart', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + })) +} + +function getConfig() { + return wrap(fetch('/cosmos/api/constellation/config', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + })) +} + +function getLogs() { + return wrap(fetch('/cosmos/api/constellation/logs', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + })) +} + export { list, addDevice, + restart, + getConfig, + getLogs, }; \ No newline at end of file diff --git a/client/src/components/apiModal.jsx b/client/src/components/apiModal.jsx new file mode 100644 index 0000000..ceba699 --- /dev/null +++ b/client/src/components/apiModal.jsx @@ -0,0 +1,94 @@ +// material-ui +import { LoadingButton } from '@mui/lab'; +import { Button } from '@mui/material'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import * as React from 'react'; +import { useEffect, useState } from 'react'; + +const preStyle = { + backgroundColor: '#000', + color: '#fff', + padding: '10px', + borderRadius: '5px', + overflow: 'auto', + maxHeight: '500px', + maxWidth: '100%', + width: '100%', + margin: '0', + position: 'relative', + fontSize: '12px', + fontFamily: 'monospace', + whiteSpace: 'pre-wrap', + wordWrap: 'break-word', + wordBreak: 'break-all', + lineHeight: '1.5', + boxShadow: '0 0 10px rgba(0,0,0,0.5)', + border: '1px solid rgba(255,255,255,0.1)', + boxSizing: 'border-box', + marginBottom: '10px', + marginTop: '10px', + marginLeft: '0', + marginRight: '0', + display: 'block', + textAlign: 'left', + verticalAlign: 'baseline', + opacity: '1', +} + +const ApiModal = ({ callback, label }) => { + const [openModal, setOpenModal] = useState(false); + const [content, setContent] = useState(""); + const [loading, setLoading] = useState(true); + + const getContent = async () => { + setLoading(true); + let content = await callback(); + setContent(content.data); + setLoading(false); + }; + + useEffect(() => { + if (openModal) + getContent(); + }, [openModal]); + + return <> + setOpenModal(false)}> + Refresh Page + + +
+                  {content}
+                
+
+
+ + { + getContent(); + }}>Refresh + + +
+ + + +}; + +export default ApiModal; diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index 7c447d1..f11d2d3 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -11,6 +11,7 @@ import { CosmosCheckbox, CosmosFormDivider } from "../config/users/formShortcuts import MainCard from "../../components/MainCard"; import { Formik } from "formik"; import { LoadingButton } from "@mui/lab"; +import ApiModal from "../../components/apiModal"; export const ConstellationIndex = () => { const [isAdmin, setIsAdmin] = useState(false); @@ -48,28 +49,45 @@ export const ConstellationIndex = () => { return <> {(devices && config && users) ? <> - +
{ let newConfig = { ...config }; newConfig.ConstellationConfig.Enabled = values.Enabled; + newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay; return API.config.set(newConfig); }} > {(formik) => (
- + + + + + + + + {
r.deviceName} - buttons={[ - - ]} - columns={[ - { - title: '', - field: getIcon, - }, - { - title: 'Device Name', - field: (r) => {r.deviceName}, - }, - { - title: 'Owner', - field: (r) => {r.nickname}, - }, - { - title: 'Constellation IP', - screenMin: 'md', - field: (r) => r.ip, - }, - { + data={devices} + getKey={(r) => r.deviceName} + buttons={[ + + ]} + columns={[ + { title: '', - clickable: true, - field: (r) => { - return { - alert("caca") - }}> - } + field: getIcon, + }, + { + title: 'Device Name', + field: (r) => {r.deviceName}, + }, + { + title: 'Owner', + field: (r) => {r.nickname}, + }, + { + title: 'Constellation IP', + screenMin: 'md', + field: (r) => r.ip, + }, + { + title: '', + clickable: true, + field: (r) => { + return { + alert("caca") + }}> } - ]} + } + ]} />
:
diff --git a/package.json b/package.json index 3a9f808..bef8454 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable6", + "version": "0.10.0-unstable7", "description": "", "main": "test-server.js", "bugs": { diff --git a/readme.md b/readme.md index 1d141e7..d7219bb 100644 --- a/readme.md +++ b/readme.md @@ -150,7 +150,7 @@ docker run -d -p 80:80 -p 443:443 --privileged --name cosmos-server -h cosmos-s in this command, `-v /:/mnt/host` is optional and allow to manage folders from Cosmos, you can remove it if you don't want it but you will have to create your container's bind folders manually. -`--privileged` is also optional, but it is required if you use hardening software like AppArmor or SELinux, as they restrict access to the docker socket. +`--privileged` is also optional, but it is required to use Constellation. It is also required if you use hardening software like AppArmor or SELinux, as they restrict access to the docker socket. Once installed, simply go to `http://your-server-ip` and follow the instructions of the setup wizard. diff --git a/src/constellation/api_nebula.go b/src/constellation/api_nebula.go new file mode 100644 index 0000000..38be4a5 --- /dev/null +++ b/src/constellation/api_nebula.go @@ -0,0 +1,75 @@ +package constellation + +import ( + "net/http" + "encoding/json" + "io/ioutil" + + + "github.com/azukaar/cosmos-server/src/utils" +) + +func API_GetConfig(w http.ResponseWriter, req *http.Request) { + if utils.AdminOnly(w, req) != nil { + return + } + + if(req.Method == "GET") { + // read utils.CONFIGFOLDER + "nebula.yml" + config, err := ioutil.ReadFile(utils.CONFIGFOLDER + "nebula.yml") + + if err != nil { + utils.Error("SettingGet: error while reading nebula.yml", err) + utils.HTTPError(w, "Error while reading nebula.yml", http.StatusInternalServerError, "HTTP002") + return + } + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + "data": string(config), + }) + } else { + utils.Error("SettingGet: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} + +func API_Restart(w http.ResponseWriter, req *http.Request) { + if utils.AdminOnly(w, req) != nil { + return + } + + if(req.Method == "GET") { + RestartNebula() + + utils.Log("Constellation: nebula restarted") + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + }) + } else { + utils.Error("SettingGet: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} + +func API_GetLogs(w http.ResponseWriter, req *http.Request) { + if utils.AdminOnly(w, req) != nil { + return + } + + if(req.Method == "GET") { + + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + "data": logBuffer.String(), + }) + } else { + utils.Error("SettingGet: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} \ No newline at end of file diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 75c42b1..93b6a04 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -12,8 +12,12 @@ import ( "strings" "io/ioutil" "strconv" + "io" + "bytes" ) +var logBuffer bytes.Buffer + var ( process *exec.Cmd processMux sync.Mutex @@ -34,14 +38,16 @@ func startNebulaInBackground() error { return errors.New("nebula is already running") } - process = exec.Command(binaryToRun(), "-config", utils.CONFIGFOLDER + "nebula.yml") + process = exec.Command(binaryToRun(), "-config", utils.CONFIGFOLDER+"nebula.yml") + + // Set up multi-writer for stderr + process.Stderr = io.MultiWriter(&logBuffer, os.Stderr) - process.Stderr = os.Stderr - if utils.LoggingLevelLabels[utils.GetMainConfig().LoggingLevel] == utils.DEBUG { - process.Stdout = os.Stdout + // Set up multi-writer for stdout if in debug mode + process.Stdout = io.MultiWriter(&logBuffer, os.Stdout) } else { - process.Stdout = nil + process.Stdout = io.MultiWriter(&logBuffer) } // Start the process in the background @@ -82,12 +88,19 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st "192.168.201.0": []string{utils.GetMainConfig().HTTPConfig.Hostname + ":4242"}, } + finalConfig.Relay.AMRelay = overwriteConfig.NebulaConfig.Relay.AMRelay + // Marshal the combined config to YAML yamlData, err := yaml.Marshal(finalConfig) if err != nil { return err } + // delete nebula.yml if exists + if _, err := os.Stat(outputPath); err == nil { + os.Remove(outputPath) + } + // Write YAML data to the specified file yamlFile, err := os.Create(outputPath) if err != nil { diff --git a/src/constellation/nebula_default.go b/src/constellation/nebula_default.go index ae45c6b..8b3abe8 100644 --- a/src/constellation/nebula_default.go +++ b/src/constellation/nebula_default.go @@ -46,9 +46,11 @@ func InitConfig() { Relay: struct { AMRelay bool `yaml:"am_relay"` UseRelays bool `yaml:"use_relays"` + Relays []string `yaml:"relays"` }{ AMRelay: true, UseRelays: true, + Relays: []string{}, }, TUN: struct { Disabled bool `yaml:"disabled"` diff --git a/src/httpServer.go b/src/httpServer.go index 49e7713..76c2fc9 100644 --- a/src/httpServer.go +++ b/src/httpServer.go @@ -333,6 +333,9 @@ func InitServer() *mux.Router { srapi.HandleFunc("/api/background/{ext}", GetBackground) srapi.HandleFunc("/api/constellation/devices", constellation.ConstellationAPIDevices) + srapi.HandleFunc("/api/constellation/restart", constellation.API_Restart) + srapi.HandleFunc("/api/constellation/config", constellation.API_GetConfig) + srapi.HandleFunc("/api/constellation/logs", constellation.API_GetLogs) if(!config.HTTPConfig.AcceptAllInsecureHostname) { srapi.Use(utils.EnsureHostname) diff --git a/src/utils/types.go b/src/utils/types.go index 96e8b5e..8a9df92 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -254,7 +254,7 @@ type NebulaConfig struct { Relay struct { AMRelay bool `yaml:"am_relay"` UseRelays bool `yaml:"use_relays"` - relays []string `yaml:"relays"` + Relays []string `yaml:"relays"` } `yaml:"relay"` TUN struct { From 9206dbade897c734bf4e70ed8f12b66ca0d13640 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Mon, 28 Aug 2023 00:12:48 +0100 Subject: [PATCH 10/36] [release] v0.10.0-unstable8 --- .gitignore | 1 + package.json | 4 +- src/constellation/api_DNS.go | 196 +++++++++++++++++++++++++++++++++++ src/constellation/nebula.go | 14 ++- src/index.go | 2 + src/utils/types.go | 5 + src/utils/utils.go | 4 +- 7 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 src/constellation/api_DNS.go diff --git a/.gitignore b/.gitignore index d347d46..c627498 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ LICENCE tokens.json .vscode GeoLite2-Country.mmdb +dns-blacklist.txt zz_test_config nebula-arm nebula-arm-cert diff --git a/package.json b/package.json index bef8454..242a941 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable7", + "version": "0.10.0-unstable8", "description": "", "main": "test-server.js", "bugs": { @@ -67,7 +67,7 @@ "build": "sh build.sh", "dev": "npm run build && npm run start", "dockerdevbuild": "sh build.sh && docker build -f dockerfile.local --tag cosmos-dev .", - "dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run --cap-add NET_ADMIN -d -p 7200:443 -p 80:80 -p 443:443 -p 4242:4242 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev", + "dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run --cap-add NET_ADMIN -d -p 7200:443 -p 80:80 -p 53:53 -p 443:443 -p 4242:4242 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev", "dockerdev": "npm run client-build && npm run dockerdevbuild && npm run dockerdevrun", "demo": "vite build --base=/cosmos-ui/ --mode demo", "devdemo": "vite --mode demo" diff --git a/src/constellation/api_DNS.go b/src/constellation/api_DNS.go new file mode 100644 index 0000000..53dd059 --- /dev/null +++ b/src/constellation/api_DNS.go @@ -0,0 +1,196 @@ +package constellation + +import ( + "time" + "strconv" + "strings" + "io/ioutil" + "fmt" + + "github.com/miekg/dns" + "github.com/azukaar/cosmos-server/src/utils" +) + +var DNSBlacklist = []string{} + +func externalLookup(client *dns.Client, r *dns.Msg, serverAddr string) (*dns.Msg, time.Duration, error) { + rCopy := r.Copy() // Create a copy of the request to forward + rCopy.Id = dns.Id() // Assign a new ID for the forwarded request + + // Enable DNSSEC + rCopy.SetEdns0(4096, true) + rCopy.CheckingDisabled = false + rCopy.MsgHdr.AuthenticatedData = true + + return client.Exchange(rCopy, serverAddr) +} + +func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { + config := utils.GetMainConfig() + DNSFallback := config.ConstellationConfig.DNSFallback + + if DNSFallback == "" { + DNSFallback = "8.8.8.8:53" + } + + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + + customHandled := false + + // []string hostnames + hostnames := utils.GetAllHostnames(false, true) + originalHostname := hostnames[0] + + specialQuery := false + + // if lighthouse-cosmos.constellation is the query, return originalHostname's external lookup + for i, q := range r.Question { + if strings.HasSuffix(q.Name, "lighthouse-cosmos.constellation.") { + utils.Debug("DNS Overwrite lighthouse-cosmos.constellation with " + originalHostname) + + // Create a deep copy of the original request. + modifiedRequest := r.Copy() + + client := new(dns.Client) + + // Modify only the copied request. + modifiedRequest.Question[i].Name = originalHostname + "." + + externalResponse, time, err := externalLookup(client, modifiedRequest, DNSFallback) + if err != nil { + utils.Error("Failed to forward query:", err) + return + } + utils.Debug("DNS Forwarded DNS query to "+DNSFallback+" in " + time.String()) + + for _, rr := range externalResponse.Answer { + if aRecord, ok := rr.(*dns.A); ok { + // 2. Replace the hostname with "lighthouse-cosmos.constellation". + modifiedString := fmt.Sprintf("lighthouse-cosmos.constellation. A %s", aRecord.A.String()) + + // 3. Convert the string back into a dns.RR. + newRR, err := dns.NewRR(modifiedString) + if err != nil { + utils.Error("Failed to convert string into dns.RR:", err) + return + } + + // Replace the response RR with the new RR. + r.Answer = append(r.Answer, newRR) + } + } + + m = r + + specialQuery = true + } + } + + if !specialQuery { + // Overwrite local hostnames with Constellation IP + for _, q := range r.Question { + utils.Debug("DNS Question " + q.Name) + for _, hostname := range hostnames { + if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA { + utils.Debug("DNS Overwrite " + hostname + " with 192.168.201.0") + rr, _ := dns.NewRR(q.Name + " A 192.168.201.0") + m.Answer = append(m.Answer, rr) + customHandled = true + } + } + } + + if !customHandled { + // map[string]string customEntries + customDNSEntries := config.ConstellationConfig.CustomDNSEntries + + // Overwrite local hostnames with custom entries + for _, q := range r.Question { + for hostname, ip := range customDNSEntries { + if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA { + utils.Debug("DNS Overwrite " + hostname + " with " + ip) + rr, _ := dns.NewRR(q.Name + " A " + ip) + m.Answer = append(m.Answer, rr) + customHandled = true + } + } + } + } + + if !customHandled { + // Block blacklisted domains + for _, q := range r.Question { + for _, hostname := range DNSBlacklist { + if strings.HasSuffix(q.Name, hostname + ".") { + if q.Qtype == dns.TypeA { + utils.Debug("DNS Block " + hostname) + rr, _ := dns.NewRR(q.Name + " A 0.0.0.0") + m.Answer = append(m.Answer, rr) + } + + customHandled = true + } + } + } + } + + // If not custom handled, use external DNS + if !customHandled { + client := new(dns.Client) + externalResponse, time, err := externalLookup(client, r, DNSFallback) + if err != nil { + utils.Error("Failed to forward query:", err) + return + } + utils.Debug("DNS Forwarded DNS query to "+DNSFallback+" in " + time.String()) + + externalResponse.Id = r.Id + + m = externalResponse + } + } + + w.WriteMsg(m) +} + +func InitDNS() { + config := utils.GetMainConfig() + DNSPort := config.ConstellationConfig.DNSPort + DNSBlockBlacklist := config.ConstellationConfig.DNSBlockBlacklist + + if DNSPort == "" { + DNSPort = "53" + } + + if DNSBlockBlacklist { + DNSBlacklist = []string{} + blacklistPath := utils.CONFIGFOLDER + "dns-blacklist.txt" + + utils.Log("Loading DNS blacklist from " + blacklistPath) + + fileExist := utils.FileExists(blacklistPath) + if fileExist { + DNSBlacklistRaw, err := ioutil.ReadFile(blacklistPath) + if err != nil { + utils.Error("Failed to load DNS blacklist", err) + } else { + DNSBlacklist = strings.Split(string(DNSBlacklistRaw), "\n") + utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains to block") + } + } else { + utils.Log("No DNS blacklist found") + } + } + + if(config.ConstellationConfig.DNS) { + dns.HandleFunc(".", handleDNSRequest) + server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"} + + utils.Log("Starting DNS server on :" + DNSPort) + if err := server.ListenAndServe(); err != nil { + utils.Fatal("Failed to start server: %s\n", err) + } + } +} diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 93b6a04..4cc7678 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -85,7 +85,10 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st finalConfig := NebulaDefaultConfig finalConfig.StaticHostMap = map[string][]string{ - "192.168.201.0": []string{utils.GetMainConfig().HTTPConfig.Hostname + ":4242"}, + "192.168.201.0": []string{ + "lighthouse-cosmos.constellation:4242", + utils.GetMainConfig().HTTPConfig.Hostname + ":4242", + }, } finalConfig.Relay.AMRelay = overwriteConfig.NebulaConfig.Relay.AMRelay @@ -132,6 +135,15 @@ func getYAMLClientConfig(name, configPath string) (string, error) { return "", err } + if staticHostMap, ok := configMap["static_host_map"].(map[interface{}]interface{}); ok { + staticHostMap["192.168.201.0"] = []string{ + "lighthouse-cosmos.constellation:4242", + utils.GetMainConfig().HTTPConfig.Hostname + ":4242", + } + } else { + return "", errors.New("static_host_map not found in nebula.yml") + } + // set lightHouse to false if lighthouseMap, ok := configMap["lighthouse"].(map[interface{}]interface{}); ok { lighthouseMap["am_lighthouse"] = false diff --git a/src/index.go b/src/index.go index 4e9fc2d..c4dc2ab 100644 --- a/src/index.go +++ b/src/index.go @@ -45,6 +45,8 @@ func main() { authorizationserver.Init() + constellation.InitDNS() + constellation.Init() StartServer() diff --git a/src/utils/types.go b/src/utils/types.go index 8a9df92..f8aab17 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -210,6 +210,11 @@ type MarketSource struct { type ConstellationConfig struct { Enabled bool + DNS bool + DNSPort string + DNSFallback string + DNSBlockBlacklist bool + CustomDNSEntries map[string]string NebulaConfig NebulaConfig } diff --git a/src/utils/utils.go b/src/utils/utils.go index cf4103d..6e7ed5a 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -63,7 +63,9 @@ var DefaultConfig = Config{ }, }, ConstellationConfig: ConstellationConfig{ - Enabled: true, + Enabled: false, + DNS: true, + DNSFallback: "8.8.8.8:53", }, } From 0142c6fb1167be7ed962abdfcf0650981faf26b8 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Mon, 28 Aug 2023 00:32:00 +0100 Subject: [PATCH 11/36] [release] v0.10.0-unstable9 --- package.json | 2 +- src/constellation/{api_DNS.go => DNS.go} | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) rename src/constellation/{api_DNS.go => DNS.go} (94%) diff --git a/package.json b/package.json index 242a941..b1410e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable8", + "version": "0.10.0-unstable9", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/api_DNS.go b/src/constellation/DNS.go similarity index 94% rename from src/constellation/api_DNS.go rename to src/constellation/DNS.go index 53dd059..b95127c 100644 --- a/src/constellation/api_DNS.go +++ b/src/constellation/DNS.go @@ -185,12 +185,14 @@ func InitDNS() { } if(config.ConstellationConfig.DNS) { - dns.HandleFunc(".", handleDNSRequest) - server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"} + go (func() { + dns.HandleFunc(".", handleDNSRequest) + server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"} - utils.Log("Starting DNS server on :" + DNSPort) - if err := server.ListenAndServe(); err != nil { - utils.Fatal("Failed to start server: %s\n", err) - } + utils.Log("Starting DNS server on :" + DNSPort) + if err := server.ListenAndServe(); err != nil { + utils.Fatal("Failed to start server: %s\n", err) + } + })() } } From 04795045580533ea6efea226990b14910d13da57 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Mon, 28 Aug 2023 10:55:19 +0100 Subject: [PATCH 12/36] [release] v0.10.0-unstable10 --- client/src/pages/constellation/addDevice.jsx | 2 +- package.json | 2 +- src/constellation/DNS.go | 4 ++-- src/constellation/index.go | 2 +- src/constellation/nebula.go | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 8d09d25..91cb749 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -21,7 +21,7 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { const [isDone, setIsDone] = useState(null); const canvasRef = React.useRef(null); - let firstIP = "192.168.201.1/24"; + let firstIP = "192.168.201.2/24"; if (devices && devices.length > 0) { const isIpFree = (ip) => { return devices.filter((d) => d.ip === ip).length === 0; diff --git a/package.json b/package.json index b1410e5..9d4fe70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable9", + "version": "0.10.0-unstable10", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/DNS.go b/src/constellation/DNS.go index b95127c..2d85f7b 100644 --- a/src/constellation/DNS.go +++ b/src/constellation/DNS.go @@ -94,8 +94,8 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { utils.Debug("DNS Question " + q.Name) for _, hostname := range hostnames { if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA { - utils.Debug("DNS Overwrite " + hostname + " with 192.168.201.0") - rr, _ := dns.NewRR(q.Name + " A 192.168.201.0") + utils.Debug("DNS Overwrite " + hostname + " with 192.168.201.1") + rr, _ := dns.NewRR(q.Name + " A 192.168.201.1") m.Answer = append(m.Answer, rr) customHandled = true } diff --git a/src/constellation/index.go b/src/constellation/index.go index 6cf6e33..0697166 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -23,7 +23,7 @@ func Init() { if _, err := os.Stat(utils.CONFIGFOLDER + "cosmos.crt"); os.IsNotExist(err) { utils.Log("Constellation: cosmos.crt not found, generating...") // generate cosmos.crt - generateNebulaCert("cosmos", "192.168.201.0/24", "", true) + generateNebulaCert("cosmos", "192.168.201.1/24", "", true) } // export nebula.yml diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 4cc7678..750babc 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -85,7 +85,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st finalConfig := NebulaDefaultConfig finalConfig.StaticHostMap = map[string][]string{ - "192.168.201.0": []string{ + "192.168.201.1": []string{ "lighthouse-cosmos.constellation:4242", utils.GetMainConfig().HTTPConfig.Hostname + ":4242", }, @@ -136,7 +136,7 @@ func getYAMLClientConfig(name, configPath string) (string, error) { } if staticHostMap, ok := configMap["static_host_map"].(map[interface{}]interface{}); ok { - staticHostMap["192.168.201.0"] = []string{ + staticHostMap["192.168.201.1"] = []string{ "lighthouse-cosmos.constellation:4242", utils.GetMainConfig().HTTPConfig.Hostname + ":4242", } @@ -149,7 +149,7 @@ func getYAMLClientConfig(name, configPath string) (string, error) { lighthouseMap["am_lighthouse"] = false lighthouseMap["hosts"] = []string{ - "192.168.201.0", + "192.168.201.1", } } else { return "", errors.New("lighthouse not found in nebula.yml") @@ -165,7 +165,7 @@ func getYAMLClientConfig(name, configPath string) (string, error) { if relayMap, ok := configMap["relay"].(map[interface{}]interface{}); ok { relayMap["am_relay"] = false - relayMap["relays"] = []string{"192.168.201.0"} + relayMap["relays"] = []string{"192.168.201.1"} } else { return "", errors.New("relay not found in nebula.yml") } From 2e728b3ac337bb813b052618f9252d4e8267d8d7 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 30 Aug 2023 14:25:16 +0100 Subject: [PATCH 13/36] [release] v0.10.0-unstable11 --- package.json | 2 +- src/constellation/DNS.go | 96 +++++++++++++++++++++++++++------------- src/utils/types.go | 1 + src/utils/utils.go | 21 +++++++++ 4 files changed, 89 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 9d4fe70..1068b0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable10", + "version": "0.10.0-unstable11", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/DNS.go b/src/constellation/DNS.go index 2d85f7b..9764c74 100644 --- a/src/constellation/DNS.go +++ b/src/constellation/DNS.go @@ -11,7 +11,7 @@ import ( "github.com/azukaar/cosmos-server/src/utils" ) -var DNSBlacklist = []string{} +var DNSBlacklist = map[string]bool{} func externalLookup(client *dns.Client, r *dns.Msg, serverAddr string) (*dns.Msg, time.Duration, error) { rCopy := r.Copy() // Create a copy of the request to forward @@ -88,6 +88,22 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } } + if !customHandled { + customDNSEntries := config.ConstellationConfig.CustomDNSEntries + + // Overwrite local hostnames with custom entries + for _, q := range r.Question { + for hostname, ip := range customDNSEntries { + if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA { + utils.Debug("DNS Overwrite " + hostname + " with " + ip) + rr, _ := dns.NewRR(q.Name + " A " + ip) + m.Answer = append(m.Answer, rr) + customHandled = true + } + } + } + } + if !specialQuery { // Overwrite local hostnames with Constellation IP for _, q := range r.Question { @@ -101,37 +117,19 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } } } - - if !customHandled { - // map[string]string customEntries - customDNSEntries := config.ConstellationConfig.CustomDNSEntries - - // Overwrite local hostnames with custom entries - for _, q := range r.Question { - for hostname, ip := range customDNSEntries { - if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA { - utils.Debug("DNS Overwrite " + hostname + " with " + ip) - rr, _ := dns.NewRR(q.Name + " A " + ip) - m.Answer = append(m.Answer, rr) - customHandled = true - } - } - } - } if !customHandled { // Block blacklisted domains for _, q := range r.Question { - for _, hostname := range DNSBlacklist { - if strings.HasSuffix(q.Name, hostname + ".") { - if q.Qtype == dns.TypeA { - utils.Debug("DNS Block " + hostname) - rr, _ := dns.NewRR(q.Name + " A 0.0.0.0") - m.Answer = append(m.Answer, rr) - } - - customHandled = true + noDot := strings.TrimSuffix(q.Name, ".") + if DNSBlacklist[noDot] { + if q.Qtype == dns.TypeA { + utils.Debug("DNS Block " + noDot) + rr, _ := dns.NewRR(q.Name + " A 0.0.0.0") + m.Answer = append(m.Answer, rr) } + + customHandled = true } } } @@ -155,6 +153,32 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { w.WriteMsg(m) } +func isDomain(domain string) bool { + // contains . and at least a letter and no special characters invalid in a domain + if strings.Contains(domain, ".") && strings.ContainsAny(domain, "abcdefghijklmnopqrstuvwxyz") && !strings.ContainsAny(domain, " !@#$%^&*()+=[]{}\\|;:'\",/<>?") { + return true + } + return false +} + +func loadRawBlockList(DNSBlacklistRaw string) { + DNSBlacklistArray := strings.Split(string(DNSBlacklistRaw), "\n") + for _, domain := range DNSBlacklistArray { + if domain != "" && !strings.HasPrefix(domain, "#") { + splitDomain := strings.Split(domain, " ") + if len(splitDomain) == 1 && isDomain(splitDomain[0]) { + DNSBlacklist[splitDomain[0]] = true + } else if len(splitDomain) == 2 { + if isDomain(splitDomain[0]) { + DNSBlacklist[splitDomain[0]] = true + } else if isDomain(splitDomain[1]) { + DNSBlacklist[splitDomain[1]] = true + } + } + } + } +} + func InitDNS() { config := utils.GetMainConfig() DNSPort := config.ConstellationConfig.DNSPort @@ -165,7 +189,7 @@ func InitDNS() { } if DNSBlockBlacklist { - DNSBlacklist = []string{} + DNSBlacklist = map[string]bool{} blacklistPath := utils.CONFIGFOLDER + "dns-blacklist.txt" utils.Log("Loading DNS blacklist from " + blacklistPath) @@ -176,12 +200,24 @@ func InitDNS() { if err != nil { utils.Error("Failed to load DNS blacklist", err) } else { - DNSBlacklist = strings.Split(string(DNSBlacklistRaw), "\n") - utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains to block") + loadRawBlockList(string(DNSBlacklistRaw)) } } else { utils.Log("No DNS blacklist found") } + + // download additional blocklists from config.DNSAdditionalBlocklists []string + for _, url := range config.ConstellationConfig.DNSAdditionalBlocklists { + utils.Log("Downloading DNS blacklist from " + url) + DNSBlacklistRaw, err := utils.DownloadFile(url) + if err != nil { + utils.Error("Failed to download DNS blacklist", err) + } else { + loadRawBlockList(DNSBlacklistRaw) + } + } + + utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains") } if(config.ConstellationConfig.DNS) { diff --git a/src/utils/types.go b/src/utils/types.go index f8aab17..ea8646c 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -214,6 +214,7 @@ type ConstellationConfig struct { DNSPort string DNSFallback string DNSBlockBlacklist bool + DNSAdditionalBlocklists []string CustomDNSEntries map[string]string NebulaConfig NebulaConfig } diff --git a/src/utils/utils.go b/src/utils/utils.go index 6e7ed5a..f131fa2 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -66,6 +66,12 @@ var DefaultConfig = Config{ Enabled: false, DNS: true, DNSFallback: "8.8.8.8:53", + DNSAdditionalBlocklists: []string{ + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", + "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt", + "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", + "https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-only/hosts", + }, }, } @@ -550,6 +556,21 @@ func GetNetworkUsage() NetworkStatus { return NetworkStatus{} } +func DownloadFile(url string) (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(body), nil +} + func GetClientIP(req *http.Request) string { /*ip := req.Header.Get("X-Forwarded-For") if ip == "" { From 0af998d06c602cc60ae1b1c38d5f8f662c7e08e9 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 30 Aug 2023 14:30:28 +0100 Subject: [PATCH 14/36] [release] v0.10.0-unstable12 --- package.json | 2 +- src/utils/middleware.go | 23 +++++++++++++++++++++++ src/utils/types.go | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1068b0e..ca4cc36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable11", + "version": "0.10.0-unstable12", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/utils/middleware.go b/src/utils/middleware.go index 406174c..e06bf40 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -260,4 +260,27 @@ func IsValidHostname(hostname string) bool { } return false +} + +func Restrictions(RestrictToConstellation bool) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + // check if the request is coming from the constellation IP range 192.168.201.0/24 + if RestrictToConstellation { + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } + + if !strings.HasPrefix(ip, "192.168.201.") && !strings.HasPrefix(ip, "192.168.202.") { + http.Error(w, "Access denied", http.StatusForbidden) + return + } + } + + next.ServeHTTP(w, r) + }) + } } \ No newline at end of file diff --git a/src/utils/types.go b/src/utils/types.go index ea8646c..8310cba 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -181,6 +181,7 @@ type ProxyRouteConfig struct { DisableHeaderHardening bool VerboseForwardHeader bool AddionalFilters []AddionalFiltersConfig + RestrictToConstellation bool } type EmailConfig struct { From 07e7f634b78122578d9d4f3e0b74590baecc30e4 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 30 Aug 2023 14:31:51 +0100 Subject: [PATCH 15/36] [release] v0.10.0-unstable12 --- src/proxy/routerGen.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proxy/routerGen.go b/src/proxy/routerGen.go index 3e23364..19cab92 100644 --- a/src/proxy/routerGen.go +++ b/src/proxy/routerGen.go @@ -143,6 +143,8 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt destination = utils.SetSecurityHeaders(destination) } + destination = utils.Restrictions(route.RestrictToConstellation)(destination) + destination = tokenMiddleware(route.AuthEnabled, route.AdminOnly)(utils.CORSHeader(originCORS)((destination))) origin.Handler(destination) From a57dbc806b5d8cdbefbbb7ca8e448793ffe8425c Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Tue, 5 Sep 2023 20:06:20 +0100 Subject: [PATCH 16/36] [release] v0.10.0-unstable13 --- client/src/api/market.demo.ts | 2785 ++++++++++-------- client/src/pages/constellation/addDevice.jsx | 18 +- package.json | 2 +- readme.md | 2 +- src/constellation/DNS.go | 99 +- src/constellation/api_devices_create.go | 18 +- src/constellation/nebula.go | 12 +- src/proxy/routerGen.go | 4 +- 8 files changed, 1670 insertions(+), 1270 deletions(-) diff --git a/client/src/api/market.demo.ts b/client/src/api/market.demo.ts index 142c778..0ee08a4 100644 --- a/client/src/api/market.demo.ts +++ b/client/src/api/market.demo.ts @@ -1,649 +1,9 @@ function list() { return new Promise((resolve, reject) => { - resolve({ - "data": { - "showcase": [ - { - "name": "Jellyfin", - "description": "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Plex, to provide media from a dedicated server to end-user devices via multiple apps.", - "url": "", - "longDescription": "

Jellyfin is an open-source media system that gives you the power to control the management and streaming of your media content. It offers an alternative to proprietary systems like Plex, empowering users to deliver media content from a dedicated server to various end-user devices through a range of apps.

Key features of Jellyfin include the ability to stream movies, TV shows, music, photos, videos, audiobooks, and podcasts. It also supports various technologies and platforms such as DLNA, Chromecast, Android, iOS, Roku, FireTV, SmartTV, Web browser, Kodi, and Emby. Jellyfin essentially functions as a comprehensive media browser, server, streaming system, player, center, manager, organizer, and library. In addition to these, it also facilitates media sharing, transcoding, and casting, offering a robust solution for your media needs.

", - "tags": [ - "media", - "server", - "streaming", - "movies", - "tv", - "music", - "photos", - "videos", - "audiobooks", - "podcasts", - "dlna", - "chromecast", - "android", - "ios", - "roku", - "firetv", - "smarttv", - "web", - "browser", - "kodi", - "emby", - "plex", - "media browser", - "media server", - "media streaming", - "media player", - "media center", - "media management", - "media organizer", - "media collection", - "media library", - "media manager", - "media sharing", - "media transcoding", - "media casting", - "media casting", - "media casting" - ], - "repository": "https://github.com/jellyfin/jellyfin", - "image": "https://hub.docker.com/r/linuxserver/jellyfin", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/2.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/3.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/4.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Nextcloud", - "description": "Nextcloud is an open-source, self-hosted file share and collaboration platform, similar to Dropbox. It offers features such as file sharing, syncing, encryption, and collaboration tools. With its extensible app ecosystem, you can tailor your cloud storage service to your needs. Nextcloud is compatible with numerous platforms and can be integrated with many IT infrastructure systems.", - "url": "", - "longDescription": "

Nextcloud is a suite of client-server software for creating and using file hosting services. It is functionally similar to Dropbox, but is free and open-source, allowing users to install it on private servers.

Nextcloud provides an array of features including file sharing, syncing, end-to-end encryption, collaboration tools, and more. Its extensible app ecosystem allows you to set up your own cloud storage service to your exact needs. You can also integrate Nextcloud with many known IT infrastructure systems.

Available for Windows, Linux, MacOS, and other platforms, Nextcloud is designed to work in a variety of environments including both on premise and cloud setups. Start using Nextcloud today to control and secure your own private cloud storage!

", - "tags": [ - "storage", - "file sharing", - "syncing", - "encryption", - "collaboration", - "windows", - "linux", - "macos", - "self-hosted", - "nextcloud" - ], - "repository": "https://github.com/nextcloud/server", - "image": "https://hub.docker.com/r/linuxserver/nextcloud", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/screenshots/2.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/screenshots/3.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Plex", - "description": "Plex organizes all of your video, and music collections, and gives you instant access to them on all of your devices. With the free Plex Media Server software on your home computer and Plex for iOS, you can enjoy all of your personal media on your iPhone, iPad or iPod touch, and easily share it with friends and family. Plex also makes your media look beautiful with rich descriptions, artwork, and other related information. With an optional premium Plex Pass subscription, you can even sync videos, music, and photos to your smartphones and tablets to enjoy while offline. Parental controls, premium music features, music videos, trailers and extras, and powerful management tools are also part of our premium offering. Getting up and running is fast and simple, so get started now!", - "url": "", - "longDescription": "

Plex is a comprehensive media solution that organizes your video and music collections, giving you instant access across all your devices. With the free Plex Media Server software installed on your home computer and Plex's iOS app, you can enjoy your personal media on your iPhone, iPad, or iPod touch, and conveniently share it with your friends and family.

Plex is not just about easy access, it also enhances your media collection by adding rich descriptions, artwork, and other related information, making your media look visually appealing. If you choose to subscribe to the optional premium Plex Pass, you get the added ability to sync videos, music, and photos to your smartphones and tablets for offline enjoyment.

Premium features also include parental controls, enhanced music features, access to music videos, trailers, extras, and robust management tools. Starting with Plex is straightforward and quick. Get started today and transform your media experience!

", - "tags": [ - "media", - "movies", - "streaming", - "tv", - "music", - "photos", - "videos", - "audiobooks", - "podcasts", - "dlna", - "chromecast", - "android", - "ios", - "roku", - "firetv", - "smarttv", - "web", - "browser", - "kodi", - "emby", - "plex", - "media browser", - "media server", - "media streaming", - "media player", - "media center", - "media management", - "media organizer", - "media collection", - "media library", - "media manager", - "media sharing", - "media transcoding", - "media casting", - "media casting", - "media casting" - ], - "repository": "https://github.com/plex/plex", - "image": "https://hub.docker.com/r/linuxserver/plex", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - } - ], - "all": { - "cosmos-cloud": [ - { - "name": "Bazarr", - "description": "Bazarr is an automated subtitle download application designed to complement Sonarr and Radarr. It simplifies the process of finding and downloading subtitles in various languages. Bazarr supports Windows, Linux, MacOS and provides a mobile-friendly web interface. If you're using Sonarr and Radarr, Bazarr can enhance your media management experience.", - "url": "", - "longDescription": "

Bazarr is a companion application to Sonarr and Radarr that manages and downloads subtitles based on your requirements. It was designed with the aim to make it easier for users to find and download subtitles for their media content.

Bazarr provides a simple and efficient interface to manage your subtitle collection, supporting many languages and integrating with popular subtitle providers. The software works seamlessly with Sonarr and Radarr to provide a unified experience for managing your media.

Bazarr supports a variety of platforms including Windows, Linux, and MacOS, and also offers a mobile-friendly web interface. Setting up Bazarr is easy, making it a must-have tool for anyone using Sonarr and Radarr.

", - "tags": [ - "download", - "subtitle", - "automation", - "downloader", - "media", - "collection", - "sonarr", - "radarr", - "windows", - "linux", - "macos", - "web interface", - "movie management", - "media management", - "bazarr" - ], - "repository": "https://github.com/morpheus65535/bazarr", - "image": "https://hub.docker.com/r/linuxserver/bazarr", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/screenshots/2.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Calibre", - "description": "Calibre is an open-source e-book management tool that allows users to organize, save, and manage e-books of various formats. It supports a wide range of e-book formats and offers features like format conversion, e-book syncing, news feeds from web sources, a built-in e-book viewer, and a server for online access.", - "url": "", - "longDescription": "

Calibre is a powerful, open-source e-book management tool. It allows users to organize, save and manage e-books of various formats. Calibre supports a wide range of e-book formats including EPUB, Kindle, PDF, and more.

With its easy-to-use interface, Calibre allows users to convert e-books from different formats, sync e-books with e-book readers, and even provides news feeds from web sources. Calibre also features a built-in e-book viewer and a server for online access to your book collection.

Calibre is available for a variety of platforms, including Windows, Linux, and MacOS.

", - "tags": [ - "e-book", - "library", - "reader", - "open-source", - "cross-platform", - "management", - "calibre", - "windows", - "linux", - "macos" - ], - "repository": "https://github.com/kovidgoyal/calibre", - "image": "https://hub.docker.com/r/linuxserver/calibre", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Calibre-web", - "description": "Calibre-web is a web application providing a clean interface for browsing, reading, and downloading eBooks using an existing Calibre database. It offers features like categorization, a browser-based eBook reader, sending eBooks to Kindle devices, user management, and Goodreads integration.", - "url": "", - "longDescription": "

Calibre-web is a web application providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. This software is a fork of the original Calibre web with several additional features and enhancements.

Calibre-web allows users to sort and categorize eBooks, provides a browser-based eBook reader, and even allows the sending of eBooks to Kindle devices. It also supports user management to control access to the eBook collection and offers the ability to interface with Goodreads for book reviews and recommendations.

Calibre-web is compatible with Docker and can be used on any platform that supports Docker.

", - "tags": [ - "e-book", - "library", - "reader", - "web interface", - "management", - "calibre-web", - "docker", - "reading", - "goodreads", - "kindle" - ], - "repository": "https://github.com/janeczku/calibre-web", - "image": "https://hub.docker.com/r/linuxserver/calibre-web", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre-Web/screenshots/1.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre-Web/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre-Web/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Code-Server", - "description": "Code-Server is an open-source tool that enables running Visual Studio Code on a remote server. It provides a full VS Code experience, secure remote access, and compatibility with VS Code extensions. Code-Server is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Code-Server is an open-source project that allows developers to run Visual Studio Code, a popular development environment, on any machine remotely. This makes it possible to code on your cloud server, which can be particularly beneficial when dealing with large datasets, complex computations, or restricted local resources.

Key features of Code-Server include full Visual Studio Code experience, secure access over SSH or HTTPS, and compatibility with VS Code extensions. It is also designed to be easy to install and get up and running.

Code-Server is available on various platforms including Windows, Linux, and MacOS, and it supports Docker, making it a highly versatile tool for developers across different environments.

", - "tags": [ - "development", - "visual studio code", - "code-server", - "open-source", - "cross-platform", - "VS Code", - "development environment", - "remote coding", - "SSH", - "HTTPS", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/cdr/code-server", - "image": "https://hub.docker.com/r/codercom/code-server", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/screenshots/2.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Deluge", - "description": "Deluge is a lightweight, open-source BitTorrent client known for its extensibility and low resource demand. It supports multiple BitTorrent protocols, offers a web interface for remote control, and includes features like encryption, peer exchange, speed limits, and proxy support. Deluge is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Deluge is a lightweight, open-source BitTorrent client. It's highly extendable via a rich collection of plugins and is noted for its multi-platform compatibility and low resource demand.

Key features include support for a wide range of BitTorrent protocols, a web interface for remote control, encryption, peer exchange, speed limits, and proxy support. With its intuitive interface, users can easily download and share files via the BitTorrent protocol.

Deluge is compatible with various platforms, including Windows, Linux, and MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "download", - "torrent", - "bittorrent", - "cross-platform", - "BitTorrent client", - "file sharing", - "peer-to-peer", - "p2p", - "encryption", - "web interface", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/deluge-torrent/deluge", - "image": "https://hub.docker.com/r/linuxserver/deluge", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Deluge/screenshots/1.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Deluge/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Deluge/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Docker Mailserver", - "description": "Docker Mailserver is a full-featured, open-source mailserver solution that runs on Docker. It supports various protocols such as SMTP, IMAP, POP3, and LDAP, and includes features such as spam protection, antivirus, DKIM signing, and automated SSL/TLS certificates. Docker Mailserver can be run on platforms that support Docker, including Linux, MacOS, and Windows.", - "url": "", - "longDescription": "

Docker Mailserver is a full-featured, open-source mailserver solution that is designed to run on Docker. It provides a comprehensive set of tools and features needed to run your own mailserver in a production environment.

Key features of Docker Mailserver include support for various protocols such as SMTP, IMAP, POP3, and LDAP. It also includes SpamAssassin for spam protection, ClamAV for antivirus, Postfix for SMTP, and Dovecot for IMAP/POP3. It also supports DKIM signing to authenticate your email and prevent phishing, and provides automated SSL/TLS certificates via Let's Encrypt.

Docker Mailserver is designed to be run on any platform that supports Docker, including Linux, MacOS, and Windows.

", - "tags": [ - "email", - "mail server", - "SMTP", - "docker mailserver", - "open-source", - "mail server", - "docker", - "IMAP", - "POP3", - "LDAP", - "spam protection", - "antivirus", - "DKIM", - "SSL/TLS", - "linux", - "macos", - "windows" - ], - "repository": "https://github.com/docker-mailserver/docker-mailserver", - "image": "https://hub.docker.com/r/mailserver/docker-mailserver", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/DockerMailServer/screenshots/1.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/DockerMailServer/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/DockerMailServer/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Duplicati", - "description": "Duplicati is an open-source backup software that creates, encrypts, and stores backups of your files. It offers AES-256 encryption, incremental backups, scheduling, automated backups, and backup verification. Duplicati is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Duplicati is an open-source backup software designed to secure your data by creating, encrypting, and storing backups of your files. It can backup your files to local drives or various online storage services.

Key features of Duplicati include AES-256 encryption, incremental backups, scheduling, and automated backups. It also supports backup verification, ensuring that your files are not corrupted or lost. Its web-based interface makes it easy to manage and schedule your backups.

Duplicati is compatible with various platforms, including Windows, Linux, and MacOS, and it supports Docker, making it a versatile tool for various environments.

", - "tags": [ - "backup", - "automation", - "encryption", - "open-source", - "cross-platform", - "backup", - "encryption", - "scheduling", - "automated backup", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/duplicati/duplicati", - "image": "https://hub.docker.com/r/duplicati/duplicati", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/screenshots/2.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Emulator-JS", - "description": "Emulator-JS is an open-source, in-browser multi-system emulator. It allows users to play games from various console systems directly in the browser, facilitated by an intuitive ROM management system. Emulator-JS can be used on any platform that supports a modern web browser, such as Windows, Linux, MacOS, and mobile devices.", - "url": "", - "longDescription": "

Emulator-JS is an open-source, in-browser multi-system emulator. It leverages JavaScript to emulate a wide range of gaming consoles, creating a convenient, accessible, and platform-agnostic gaming experience.

Key features of Emulator-JS include compatibility with multiple console systems, an intuitive ROM management system, and the ability to save game states. Because it's browser-based, Emulator-JS eliminates the need for complex installations or system-specific emulators.

As Emulator-JS runs directly in the browser, it can be used on any platform that supports a modern web browser, including Windows, Linux, MacOS, and even mobile devices. This makes it highly versatile for users across different environments.

", - "tags": [ - "gaming", - "emulation", - "web", - "open-source", - "cross-platform", - "emulator", - "in-browser", - "gaming", - "rom management", - "javascript", - "windows", - "linux", - "macos", - "mobile" - ], - "repository": "https://github.com/EmulatorJS/EmulatorJS", - "image": "https://hub.docker.com/r/linuxserver/emulatorjs", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "FreshRSS", - "description": "FreshRSS is an open-source, self-hosted RSS feed aggregator that is lightweight and easy to use. Features include a responsive design, import/export OPML files, multiple themes, filters, categories, multi-user support, and extensibility with plugins. FreshRSS is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

FreshRSS is an open-source, self-hosted RSS feed aggregator. It is lightweight, easy to use, and packed with features. FreshRSS lets you collect, manage, and read your RSS feeds from your own server, offering a high degree of control and privacy.

Key features include a responsive design that works well on desktop and mobile devices, the ability to import/export OPML files, multiple themes, filters, categories, and multi-user support with the ability to manage user roles. Furthermore, it's extensible with plugins for additional functionalities.

FreshRSS is compatible with various platforms, including Windows, Linux, and MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "RSS", - "feed aggregator", - "news", - "freshrss", - "open-source", - "self-hosted", - "responsive", - "OPML", - "multi-user", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/FreshRSS/FreshRSS", - "image": "https://hub.docker.com/r/linuxserver/freshrss", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/FreshRSS/screenshots/1.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/FreshRSS/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/FreshRSS/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Gitea", - "description": "Gitea is a lightweight, open-source Git platform for self-hosted repository management, code collaboration, and issue tracking. It offers an intuitive interface, powerful access control, and integrations with popular development tools.", - "url": "", - "longDescription": "

Gitea is a lightweight, open-source Git platform that provides a self-hosted solution for managing repositories, collaborative code reviews, and issue tracking.

With Gitea, you can easily create and manage Git repositories, track changes, collaborate with team members, and streamline your software development workflow. It offers a simple and intuitive user interface, powerful access control, and a range of features such as code highlighting, pull requests, and issue management.

Gitea is written in Go and is designed to be lightweight, fast, and easy to deploy. It supports multiple authentication methods, including built-in user accounts, LDAP, and OAuth. Gitea also provides integrations with popular development tools and services.

Whether you're a small team or a large organization, Gitea provides a flexible and customizable platform for hosting your Git repositories and managing your software projects.

", - "tags": [ - "development", - "git", - "repository management", - "code collaboration", - "issue tracking", - "go", - "self-hosted", - "gitea" - ], - "repository": "https://github.com/go-gitea/gitea", - "image": "https://hub.docker.com/r/gitea/gitea", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/screenshots/2.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/screenshots/3.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Grav", - "description": "Grav is an open-source, self-hosted, flat-file content management system (CMS). It offers features like content filtering, multi-language support, flexible templating with Twig, and simple content creation with Markdown. Grav can be extended with plugins and themes. It can be run on Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Grav is an open-source, self-hosted, flat-file content management system (CMS). Unlike traditional CMS platforms, Grav does not use a database to store content, making it incredibly lightweight and easy to manage.

Key features of Grav include powerful content filtering, multi-language support, flexible templating with Twig, and simple content creation process with Markdown. Its extendibility with a rich ecosystem of plugins and themes, plus a strong focus on performance, makes Grav a versatile platform for creating all kinds of websites.

Grav is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "web", - "blog", - "code", - "open-source", - "self-hosted", - "CMS", - "flat-file", - "content management", - "twig", - "markdown", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/getgrav/grav", - "image": "https://hub.docker.com/r/getgrav/grav", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Grav/screenshots/1.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grav/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grav/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Grocy", - "description": "Grocy is a self-hosted, open-source grocery management solution offering features like stock management with a barcode scanner, recipe management, household chore management, and tracking of purchased items with expiration dates. Grocy is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Grocy is a self-hosted, open-source grocery management solution designed to help users manage their groceries. It provides a suite of tools for managing shopping lists, recipes, inventory, and chores.

Key features include stock management with a barcode scanner, recipe management, household chore management, and tracking of purchased items with expiration dates. Grocy helps users to avoid waste by providing reminders about due dates of perishable items.

Grocy is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "life", - "groceries", - "list", - "open-source", - "self-hosted", - "grocery", - "management", - "shopping list", - "inventory", - "recipes", - "chores", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/grocy/grocy", - "image": "https://hub.docker.com/r/linuxserver/grocy", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Grocy/screenshots/1.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grocy/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grocy/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Heimdall", - "description": "Heimdall is an open-source, self-hosted dashboard software that provides a central hub for web-based applications and services. Features include a built-in application library, custom themes, multi-user support, and compatibility with desktop and mobile browsers. Heimdall is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Heimdall is an open-source, self-hosted dashboard software that allows you to organize and access your web-based applications and services all in one place. It's designed to provide a central hub to simplify your web environment.

Key features of Heimdall include the ability to add applications via a built-in library or by creating custom application definitions, custom themes, and the capacity to run on a desktop or mobile browser. It supports multiple users and each user can have their own personalized set of applications.

Heimdall is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "dashboard", - "home", - "icons", - "open-source", - "self-hosted", - "web-based applications", - "services", - "central hub", - "multi-user", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/linuxserver/Heimdall", - "image": "https://hub.docker.com/r/linuxserver/heimdall", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/screenshots/2.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/screenshots/3.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Home Assistant", - "description": "Home Assistant is an open-source home automation platform that focuses on privacy and local control. It allows you to control all your devices from a single interface, integrating with a large number of devices and services. Home Assistant offers advanced automation capabilities, running perfectly on a Raspberry Pi or a local server. Start using Home Assistant today for a comprehensive home automation solution.", - "url": "", - "longDescription": "

Home Assistant is an open-source home automation platform focused on privacy and local control. It allows you to control all your devices from a single, unified interface. It is powered by a worldwide community of tinkerers and DIY enthusiasts, perfect to run on a Raspberry Pi or a local server.

Home Assistant integrates with a large number of different devices and services, providing the user with a powerful and flexible home automation control center. In addition to its powerful, flexible features, it provides advanced automation capabilities to help make your life easier.

With support for a vast array of devices and services, Home Assistant can be the one-stop solution for all your home automation needs. Get started with Home Assistant today and take control of your home automation!

", - "tags": [ - "smart home", - "home automation", - "IoT", - "Raspberry Pi", - "local server", - "privacy", - "control", - "automation", - "devices", - "services", - "home assistant" - ], - "repository": "https://github.com/home-assistant/core", - "image": "https://hub.docker.com/r/homeassistant/home-assistant", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/screenshots/2.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/screenshots/3.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Immich", - "description": "Immich - High performance self-hosted photo and video backup solution", - "url": "", - "longDescription": "

Immich is an open-source personal photo management tool that makes use of the latest technologies to provide an easy and intuitive way to keep your photos organized. It provides a web-based platform to browse, organize, and share your personal photo collection.

With features like smart search, automated tagging, and geolocation, Immich makes it easy to find and group your photos. It supports various image formats and RAW files from high-quality cameras. Immich can also automatically generate thumbnails and convert RAW images.

Available for Windows, Linux, MacOS, and other platforms, Immich offers a private, self-hosted solution for your photo management needs. Start using Immich today to take control of your photo collection!

", - "tags": [ - "photo management", - "image organizer", - "smart search", - "automated tagging", - "geolocation", - "thumbnails", - "RAW images", - "self-hosted", - "windows", - "linux", - "macos", - "Immich" - ], - "repository": "https://github.com/Immich/Immich", - "image": "https://hub.docker.com/r/Immich/Immich", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/2.webp", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/3.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, + resolve({ + "data": { + "showcase": [ { "name": "Jellyfin", "description": "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Plex, to provide media from a dedicated server to end-user devices via multiple apps.", @@ -703,207 +63,6 @@ function list() { "arm64" ] }, - { - "name": "Joplin", - "description": "Joplin is a free, open-source note-taking and to-do application that supports markdown and end-to-end encryption. It offers capabilities such as tagging, searching, and modifying notes, and can sync with various cloud platforms. It features an extensible plugin system, allowing for tailored functionality. Joplin is compatible across multiple platforms including Windows, Linux, MacOS, iOS, and Android.", - "url": "", - "longDescription": "

Joplin is a free and open-source note-taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified directly from the applications or from your own text editor. It also supports markdown, with inline previewing.

Joplin offers end-to-end encryption and can synchronize with various cloud platforms, making it a secure choice for your note-taking needs. With its API, Joplin can be extended with plugins for custom functionality, further tailoring your note management experience.

Available for Windows, Linux, MacOS, iOS, and Android, Joplin is flexible to fit a variety of environments and workflows. Start using Joplin today to organize your thoughts and tasks in a secure and efficient manner!

", - "tags": [ - "productivity", - "note-taking", - "to-do", - "markdown", - "encryption", - "syncing", - "windows", - "linux", - "macos", - "ios", - "android", - "self-hosted", - "joplin" - ], - "repository": "https://github.com/laurent22/joplin", - "image": "https://hub.docker.com/r/joplin/server", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "KitchenOwl", - "description": "KitchenOwl helps you organize your grocery life.", - "url": "", - "longDescription": "

KitchenOwl is a smart grocery list and recipe manager.

Easily add items to your shopping list before you go shopping. You can also create recipes and get suggestions on what you want to cook.

Track your expenses so you know how much you've spent.

", - "tags": [ - "recipes", - "meals", - "shopping", - "planning", - "expenses", - "docker" - ], - "repository": "https://github.com/tombursch/kitchenowl", - "image": "https://hub.docker.com/r/tombursch/kitchenowl", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Lemmy", - "description": "Lemmy is an open-source, self-hosted, federated link aggregator similar to Reddit. It offers community formation, moderation tools, content voting, and supports commenting and private messaging between users. Lemmy can be run on Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Lemmy is an open-source, self-hosted, federated link aggregator application similar to Reddit, which allows users to create their own communities and share links and posts with others.

Key features of Lemmy include its ability to form federated link-aggregator communities, moderation tools, and the ability to vote on content. It also supports commenting and private messaging between users. Lemmy is designed to consume minimal resources and can be easily self-hosted on small servers.

Lemmy is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "social", - "federation", - "reddit", - "open-source", - "self-hosted", - "federated", - "link aggregator", - "community", - "moderation", - "content voting", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/LemmyNet/lemmy", - "image": "https://hub.docker.com/r/dessalines/lemmy", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/screenshots/2.webp" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Lidarr", - "description": "Lidarr is an automated music manager for maintaining your music library. It does this by finding and automatically downloading your desired music tracks. It integrates with popular download clients and supports metadata fetching from multiple music databases. With Lidarr, your music downloading and organization process can be streamlined and automated. It supports various platforms and has a mobile-friendly web interface for easy access. Get started with Lidarr now and enhance your music management experience!", - "url": "", - "longDescription": "

Lidarr is an automated music collection manager that can find, add, and manage music in your collection. With Lidarr, you can automatically search for new albums and singles from your favorite artists, and automatically download them to your media server. It is integrated with popular download clients and supports multiple music databases for metadata fetching.

Lidarr is a perfect solution for those who want to automate their music downloading and organization process. It is available for various platforms including Windows, Linux, and MacOS, and has a mobile-friendly web interface that works across different devices.

Setting up Lidarr is straightforward. Whether you are an audiophile with a large collection or someone who just loves listening to music, Lidarr can transform your music management experience!

", - "tags": [ - "download", - "torrent", - "music", - "usenet", - "automation", - "manager", - "media", - "collection", - "download", - "metadata", - "windows", - "linux", - "macos", - "web interface", - "streaming", - "music library", - "music database", - "music management", - "music collection", - "lidarr" - ], - "repository": "https://github.com/lidarr/Lidarr", - "image": "https://hub.docker.com/r/linuxserver/lidarr", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/screenshots/2.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/screenshots/3.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Mastodon", - "description": "Mastodon is a free and open-source self-hosted social networking service. It features decentralization, ability to build your own community, per-post privacy settings, anti-abuse tools, and supports multimedia attachments. Mastodon can be run on any platform that supports Ruby, Node.js, and PostgreSQL, including various Linux distributions and Docker.", - "url": "", - "longDescription": "

Mastodon is a free and open-source self-hosted social networking service. It allows anyone to host their own server node in the network, while interacting with other users in a federated manner. It is similar to Twitter, but is distinguished by its decentralization and ability for users to host their own servers, and its character limit of 500 as opposed to Twitter's 280.

Key features of Mastodon include the ability to build your own community, granular, per-post privacy settings, and anti-abuse tools. It supports a rich variety of multimedia attachments in posts, and you can also choose to use content warnings on your posts for viewer discretion.

Mastodon can be run on any platform that supports Ruby, Node.js, and PostgreSQL, including various Linux distributions and Docker.

", - "tags": [ - "social", - "federation", - "reddit", - "open-source", - "self-hosted", - "federated", - "link aggregator", - "community", - "moderation", - "content voting", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/tootsuite/mastodon", - "image": "https://hub.docker.com/r/tootsuite/mastodon", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "NZBGet", - "description": "NZBGet is a high-performance Usenet downloader that uses NZB files to fetch content. It is designed to use minimal system resources while offering features such as a web-interface, API, automatic post-processing, and RSS feed support. NZBGet works well on various platforms including low powered devices. Start using NZBGet for your Usenet downloads today!", - "url": "", - "longDescription": "

NZBGet is a robust and lightweight Usenet downloader. It can download files from Usenet servers via NZB index files. The software is focused on performance and uses very little system resources, while achieving high download speeds.

NZBGet offers a variety of features including web-interface, API, automatic post-processing, RSS feed support, and more. It is also designed to work well on low powered devices like Raspberry Pi.

Available on multiple platforms including Windows, MacOS, Linux, and others, NZBGet is easy to set up and use. Start using NZBGet today for efficient Usenet downloading!

", - "tags": [ - "download", - "torrent", - "usenet", - "downloader", - "nzb", - "windows", - "linux", - "macos", - "raspberry pi", - "performance", - "rss feed", - "post-processing", - "nzbget" - ], - "repository": "https://github.com/nzbget/nzbget", - "image": "https://hub.docker.com/r/linuxserver/nzbget", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, { "name": "Nextcloud", "description": "Nextcloud is an open-source, self-hosted file share and collaboration platform, similar to Dropbox. It offers features such as file sharing, syncing, encryption, and collaboration tools. With its extensible app ecosystem, you can tailor your cloud storage service to your needs. Nextcloud is compatible with numerous platforms and can be integrated with many IT infrastructure systems.", @@ -935,98 +94,6 @@ function list() { "arm64" ] }, - { - "name": "OhMyForm", - "description": "OhMyForm is a free, open-source form creation software. It offers a range of features for creating, administering, analyzing, and distributing beautiful, embeddable forms for various purposes such as recruiting, market research, and surveys. OhMyForm allows for self-hosting with no installation fees or monthly charges.", - "url": "", - "longDescription": "

OhMyForm is a free, open-source software that allows you to craft beautiful forms in seconds for various purposes such as recruiting, market research, surveys, and more. It offers a user-friendly interface and features that make creating, administering, analyzing, and distributing forms straightforward.

Key features of OhMyForm include its wide variety of built-in tools, the ability to create embeddable forms, and a powerful community of contributors that constantly improve and vet the platform. As open-source software, OhMyForm is not only always improving but also completely free.

One of the advantages of OhMyForm is the ability to host it yourself, ensuring no installation fees or monthly charges. This commitment to being 100% open-source allows users to maintain full control over their forms and data.

", - "tags": [ - "productivity", - "open-source", - "self-hosted", - "form creation", - "surveys", - "recruiting", - "market research", - "embeddable forms" - ], - "repository": "https://github.com/ohmyform/ohmyform", - "image": "https://hub.docker.com/r/ohmyform/ohmyform", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/OhMyForm/screenshots/1.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/OhMyForm/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/OhMyForm/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Ombi", - "description": "Ombi is an open-source, self-hosted web application that enables Plex or Emby users to request content. It offers features like user management, a request system, notification system, and automatic updates. Ombi is compatible with popular platforms such as Plex, Emby, Sonarr, Radarr, and Tautulli. It can be run on Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Ombi is an open-source, self-hosted web application that enables you to give your shared Plex or Emby users the ability to request content. It's an intuitive interface between your media server and the users.

Key features of Ombi include user management, a request system, notification system, and automatic updates. Ombi can be integrated with popular platforms such as Plex, Emby, Sonarr, Radarr, and Tautulli, making it an essential tool for managing requests for media content.

Ombi is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "media", - "request", - "library", - "open-source", - "self-hosted", - "web application", - "plex", - "emby", - "request system", - "media content", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/Ombi-app/Ombi", - "image": "https://hub.docker.com/r/linuxserver/ombi", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Ombi/screenshots/1.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Ombi/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Ombi/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "PhotoPrism", - "description": "PhotoPrism is an open-source personal photo management tool that uses smart search, automated tagging, and geolocation to help organize your photos. It supports various image formats, generates thumbnails, and converts RAW images. PhotoPrism works across various platforms and offers a private, self-hosted solution for managing your photos. Start using PhotoPrism today for your photo management needs.", - "url": "", - "longDescription": "

PhotoPrism is an open-source personal photo management tool that makes use of the latest technologies to provide an easy and intuitive way to keep your photos organized. It provides a web-based platform to browse, organize, and share your personal photo collection.

With features like smart search, automated tagging, and geolocation, PhotoPrism makes it easy to find and group your photos. It supports various image formats and RAW files from high-quality cameras. PhotoPrism can also automatically generate thumbnails and convert RAW images.

Available for Windows, Linux, MacOS, and other platforms, PhotoPrism offers a private, self-hosted solution for your photo management needs. Start using PhotoPrism today to take control of your photo collection!

", - "tags": [ - "photo management", - "image organizer", - "smart search", - "automated tagging", - "geolocation", - "thumbnails", - "RAW images", - "self-hosted", - "windows", - "linux", - "macos", - "photoprism" - ], - "repository": "https://github.com/photoprism/photoprism", - "image": "https://hub.docker.com/r/photoprism/photoprism", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, { "name": "Plex", "description": "Plex organizes all of your video, and music collections, and gives you instant access to them on all of your devices. With the free Plex Media Server software on your home computer and Plex for iOS, you can enjoy all of your personal media on your iPhone, iPad or iPod touch, and easily share it with friends and family. Plex also makes your media look beautiful with rich descriptions, artwork, and other related information. With an optional premium Plex Pass subscription, you can even sync videos, music, and photos to your smartphones and tablets to enjoy while offline. Parental controls, premium music features, music videos, trailers and extras, and powerful management tools are also part of our premium offering. Getting up and running is fast and simple, so get started now!", @@ -1082,240 +149,1620 @@ function list() { "amd64", "arm64" ] - }, - { - "name": "Radarr", - "description": "Radarr is an automated movie download application, similar to CouchPotato. It helps you to manage your movie collection with features like metadata fetching, trailer linking, and integration with popular download clients. Radarr works across Windows, Linux, MacOS and has a mobile-friendly web interface. Start using Radarr now to automate and enhance your movie downloading and management process.", - "url": "", - "longDescription": "

Radarr is an independent fork of Sonarr reworked for automatically downloading movies via Usenet and BitTorrent. The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.

Radarr provides a sleek, customizable interface to manage your movie collection, offering features like trailer linking, metadata fetching, and more. The software integrates with popular download clients and can automate your movie download process.

Radarr supports a variety of platforms including Windows, Linux, and MacOS, and also offers a mobile-friendly web interface. Setting up Radarr is straightforward. Get started today to streamline your movie downloading and management experience!

", - "tags": [ - "download", - "movies", - "torrent", - "usenet", - "automated", - "downloader", - "media", - "collection", - "usenet", - "bittorrent", - "windows", - "linux", - "macos", - "web interface", - "metadata", - "movie management", - "movie collection", - "radarr" - ], - "repository": "https://github.com/Radarr/Radarr", - "image": "https://hub.docker.com/r/linuxserver/radarr", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Readarr", - "description": "Readarr is an open-source, self-hosted PVR for managing and downloading ebooks. It offers features like adding favorite authors, automatic book searching, calendar integration, and importing existing libraries. Readarr supports integration with popular download clients and can send notifications. It can be run on Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Readarr is an open-source, self-hosted PVR (Personal Video Recorder) for managing and downloading ebooks. It's built off of the Radarr, Sonarr, and Lidarr platforms and brings the same powerful features to the world of ebooks.

Key features of Readarr include the ability to add your favorite authors, automatic book searching, calendar integration, and the ability to import existing libraries. It supports integration with popular download clients and can send notifications via a number of platforms.

Readarr is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "download", - "ebooks", - "readarr", - "open-source", - "self-hosted", - "PVR", - "book management", - "download", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/Readarr/Readarr", - "image": "https://hub.docker.com/r/linuxserver/readarr", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Sonarr", - "description": "Sonarr is an automated TV show manager that can search, download, and manage your TV series. It offers features like automatic quality upgrades, episode tracking, and metadata fetching. Sonarr works across various platforms including Windows, Linux, MacOS, and has a mobile-friendly web interface. Start using Sonarr now to automate your TV show management process.", - "url": "", - "longDescription": "

Sonarr is a multi-platform app to search, download, and manage TV shows. It automates the process of searching for new episodes, and can automatically download these files with your preferred download software.

Sonarr offers features such as automatic quality upgrades, series and episode tracking, and metadata fetching to enrich your media library. The interface is user-friendly and mobile-responsive, ensuring you can manage your TV shows easily on any device.

Available for Windows, Linux, and MacOS, Sonarr makes the TV show management process easy and efficient. Get started with Sonarr today and transform your media management experience!

", - "tags": [ - "download", - "tv shows", - "torrent", - "usenet", - "automated", - "manager", - "media", - "collection", - "windows", - "linux", - "macos", - "web interface", - "metadata", - "episode tracking", - "quality upgrades", - "sonarr" - ], - "repository": "https://github.com/Sonarr/Sonarr", - "image": "https://hub.docker.com/r/linuxserver/sonarr", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/screenshots/2.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/screenshots/3.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Tandoor", - "description": "Tandoor is a recipe manager that allows you to manage your ever growing collection of digital recipes.", - "url": "", - "longDescription": "

Tandoor Recipes is the self hosted recipe manager with meal planning, shopping lists and cookbook collections.

The days of storing recipes on paper cards is in the past and what better way to preserve older recipes that were handed down then to log them into a digital, self hosted recipe manager where you can access them anywhere?

Tandoor Recipes is much more than a digital recipe reference archive, it's a complete meal planning power house.

", - "tags": [ - "recipes", - "meals", - "shopping", - "planning", - "cookbook", - "docker", - "pwa" - ], - "repository": "https://github.com/TandoorRecipes/recipes", - "image": "https://hub.docker.com/r/vabene1111/recipes", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/screenshots/1.png", - "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/screenshots/2.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Transmission", - "description": "Transmission is an open-source, cross-platform BitTorrent client. It is widely praised for its minimal resource usage, ease-of-use, and performance. Transmission provides features like built-in encryption, webseed support, peer exchange, automatic port mapping, and individual torrent speed limit settings. It supports Windows, Linux, MacOS, and also offers a web interface for remote control.", - "url": "", - "longDescription": "

Transmission is a cross-platform BitTorrent client that is open-source, volunteer-based, and free from advertisements. It is highly regarded for its low resource usage, simplicity, and performance.

With its intuitive interface, Transmission allows users to quickly and easily download and share files via the BitTorrent protocol. Its standout features include built-in encryption, webseed support, peer exchange, automatic port mapping, and the ability to set speed limits for individual torrents.

Transmission is available for a variety of platforms including Windows, Linux, and MacOS. It also provides a web interface for remote control, making it a popular choice among users looking for a lightweight, reliable BitTorrent client.

", - "tags": [ - "download", - "torrent", - "bittorrent", - "downloader", - "open-source", - "cross-platform", - "file sharing", - "peer-to-peer", - "p2p", - "transmission", - "windows", - "linux", - "macos", - "web interface" - ], - "repository": "https://github.com/transmission/transmission", - "image": "https://hub.docker.com/r/linuxserver/transmission", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Transmission/screenshots/1.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Transmission/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Transmission/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "Uptime Kuma", - "description": "Uptime Kuma is an open-source, self-hosted monitoring tool for tracking the uptime of online services. It offers status checks for various services, detailed statistics with charts, and multiple notification methods. Uptime Kuma is compatible with Windows, Linux, MacOS, and Docker.", - "url": "", - "longDescription": "

Uptime Kuma is an open-source, self-hosted monitoring tool that allows users to monitor their online services and get alerts when they go down or become unresponsive. This robust yet user-friendly tool is designed to help you keep track of your services' uptime.

Key features of Uptime Kuma include status checking for various services including HTTP(s), TCP, and ICMP, detailed statistics with charts, and multiple notification methods (like email, Slack, Telegram, etc.). Its clean, intuitive user interface makes setup and management of monitored services easy.

Uptime Kuma is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", - "tags": [ - "technical", - "administration", - "server", - "open-source", - "self-hosted", - "monitoring", - "uptime", - "service status", - "notifications", - "windows", - "linux", - "macos", - "docker" - ], - "repository": "https://github.com/louislam/uptime-kuma", - "image": "https://hub.docker.com/r/louislam/uptime-kuma", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/screenshots/1.jpg", - "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/screenshots/2.jpg" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] - }, - { - "name": "WordPress", - "description": "WordPress is a free, open-source content management system based on PHP and MySQL. Known for its flexibility and ease-of-use, it supports a range of website types with thousands of plugins and themes. WordPress features a robust administrative dashboard for website creation, modification, and management. It is widely supported across various hosting platforms.", - "url": "", - "longDescription": "

WordPress is a free and open-source content management system (CMS) based on PHP and MySQL. It is one of the most popular website management or blogging system in use on the Web, supporting more than 60 million websites.

WordPress allows users to create and edit websites through a central administrative dashboard, which includes a text editor for modifying content, menus and various design elements. It offers thousands of themes and plugins that can enhance functionality and customization options, making it suitable for a variety of website types, from personal blogs to online stores.

With its vast community, WordPress users have access to a plethora of resources, tutorials, and guides. Start your website journey with WordPress today!

", - "tags": [ - "blogging", - "cms", - "website management", - "php", - "mysql", - "themes", - "plugins", - "self-hosted", - "wordpress" - ], - "repository": "https://github.com/WordPress/WordPress", - "image": "https://hub.docker.com/_/wordpress", - "screenshots": [ - "https://azukaar.github.io/cosmos-servapps-official/servapps/Wordpress/screenshots/1.png" - ], - "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Wordpress/icon.png", - "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Wordpress/cosmos-compose.json", - "supported_architectures": [ - "amd64", - "arm64" - ] } - ] - } - }, - "status": "OK" - }) + ], + "all": { + "cosmos-cloud": [ + { + "name": "Audiobookshelf", + "description": "Audiobookshelf is a self-hosted audiobook and podcast server.", + "url": "", + "longDescription": "

Audiobookshelf is an open-source, self-hosted, audiobook and podcast server. It features an Android and iOS app for streaming your library, multi-user support, storing progress per user, and syncing across devices.

It also includes various audiobook management services like metadata fetching, chapter editing tools, and media merging.

", + "tags": [ + "e-book", + "library", + "audiobook", + "open-source", + "management", + "audiobookshelf", + "docker", + "linux", + "ios", + "android" + ], + "repository": "https://github.com/advplyr/audiobookshelf", + "image": "https://hub.docker.com/r/advplyr/audiobookshelf", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Audiobookshelf/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Audiobookshelf/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Audiobookshelf/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Audiobookshelf/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Bazarr", + "description": "Bazarr is an automated subtitle download application designed to complement Sonarr and Radarr. It simplifies the process of finding and downloading subtitles in various languages. Bazarr supports Windows, Linux, MacOS and provides a mobile-friendly web interface. If you're using Sonarr and Radarr, Bazarr can enhance your media management experience.", + "url": "", + "longDescription": "

Bazarr is a companion application to Sonarr and Radarr that manages and downloads subtitles based on your requirements. It was designed with the aim to make it easier for users to find and download subtitles for their media content.

Bazarr provides a simple and efficient interface to manage your subtitle collection, supporting many languages and integrating with popular subtitle providers. The software works seamlessly with Sonarr and Radarr to provide a unified experience for managing your media.

Bazarr supports a variety of platforms including Windows, Linux, and MacOS, and also offers a mobile-friendly web interface. Setting up Bazarr is easy, making it a must-have tool for anyone using Sonarr and Radarr.

", + "tags": [ + "download", + "subtitle", + "automation", + "downloader", + "media", + "collection", + "sonarr", + "radarr", + "windows", + "linux", + "macos", + "web interface", + "movie management", + "media management", + "bazarr" + ], + "repository": "https://github.com/morpheus65535/bazarr", + "image": "https://hub.docker.com/r/linuxserver/bazarr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/screenshots/2.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Bazarr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Calibre", + "description": "Calibre is an open-source e-book management tool that allows users to organize, save, and manage e-books of various formats. It supports a wide range of e-book formats and offers features like format conversion, e-book syncing, news feeds from web sources, a built-in e-book viewer, and a server for online access.", + "url": "", + "longDescription": "

Calibre is a powerful, open-source e-book management tool. It allows users to organize, save and manage e-books of various formats. Calibre supports a wide range of e-book formats including EPUB, Kindle, PDF, and more.

With its easy-to-use interface, Calibre allows users to convert e-books from different formats, sync e-books with e-book readers, and even provides news feeds from web sources. Calibre also features a built-in e-book viewer and a server for online access to your book collection.

Calibre is available for a variety of platforms, including Windows, Linux, and MacOS.

", + "tags": [ + "e-book", + "library", + "reader", + "open-source", + "cross-platform", + "management", + "calibre", + "windows", + "linux", + "macos" + ], + "repository": "https://github.com/kovidgoyal/calibre", + "image": "https://hub.docker.com/r/linuxserver/calibre", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Calibre-web", + "description": "Calibre-web is a web application providing a clean interface for browsing, reading, and downloading eBooks using an existing Calibre database. It offers features like categorization, a browser-based eBook reader, sending eBooks to Kindle devices, user management, and Goodreads integration.", + "url": "", + "longDescription": "

Calibre-web is a web application providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. This software is a fork of the original Calibre web with several additional features and enhancements.

Calibre-web allows users to sort and categorize eBooks, provides a browser-based eBook reader, and even allows the sending of eBooks to Kindle devices. It also supports user management to control access to the eBook collection and offers the ability to interface with Goodreads for book reviews and recommendations.

Calibre-web is compatible with Docker and can be used on any platform that supports Docker.

", + "tags": [ + "e-book", + "library", + "reader", + "web interface", + "management", + "calibre-web", + "docker", + "reading", + "goodreads", + "kindle" + ], + "repository": "https://github.com/janeczku/calibre-web", + "image": "https://hub.docker.com/r/linuxserver/calibre-web", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre-Web/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre-Web/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Calibre-Web/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Code-Server", + "description": "Code-Server is an open-source tool that enables running Visual Studio Code on a remote server. It provides a full VS Code experience, secure remote access, and compatibility with VS Code extensions. Code-Server is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Code-Server is an open-source project that allows developers to run Visual Studio Code, a popular development environment, on any machine remotely. This makes it possible to code on your cloud server, which can be particularly beneficial when dealing with large datasets, complex computations, or restricted local resources.

Key features of Code-Server include full Visual Studio Code experience, secure access over SSH or HTTPS, and compatibility with VS Code extensions. It is also designed to be easy to install and get up and running.

Code-Server is available on various platforms including Windows, Linux, and MacOS, and it supports Docker, making it a highly versatile tool for developers across different environments.

", + "tags": [ + "development", + "visual studio code", + "code-server", + "open-source", + "cross-platform", + "VS Code", + "development environment", + "remote coding", + "SSH", + "HTTPS", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/cdr/code-server", + "image": "https://hub.docker.com/r/codercom/code-server", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/screenshots/2.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/CodeServer/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Deluge", + "description": "Deluge is a lightweight, open-source BitTorrent client known for its extensibility and low resource demand. It supports multiple BitTorrent protocols, offers a web interface for remote control, and includes features like encryption, peer exchange, speed limits, and proxy support. Deluge is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Deluge is a lightweight, open-source BitTorrent client. It's highly extendable via a rich collection of plugins and is noted for its multi-platform compatibility and low resource demand.

Key features include support for a wide range of BitTorrent protocols, a web interface for remote control, encryption, peer exchange, speed limits, and proxy support. With its intuitive interface, users can easily download and share files via the BitTorrent protocol.

Deluge is compatible with various platforms, including Windows, Linux, and MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "download", + "torrent", + "bittorrent", + "cross-platform", + "BitTorrent client", + "file sharing", + "peer-to-peer", + "p2p", + "encryption", + "web interface", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/deluge-torrent/deluge", + "image": "https://hub.docker.com/r/linuxserver/deluge", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Deluge/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Deluge/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Deluge/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Docker Mailserver", + "description": "Docker Mailserver is a full-featured, open-source mailserver solution that runs on Docker. It supports various protocols such as SMTP, IMAP, POP3, and LDAP, and includes features such as spam protection, antivirus, DKIM signing, and automated SSL/TLS certificates. Docker Mailserver can be run on platforms that support Docker, including Linux, MacOS, and Windows.", + "url": "", + "longDescription": "

Docker Mailserver is a full-featured, open-source mailserver solution that is designed to run on Docker. It provides a comprehensive set of tools and features needed to run your own mailserver in a production environment.

Key features of Docker Mailserver include support for various protocols such as SMTP, IMAP, POP3, and LDAP. It also includes SpamAssassin for spam protection, ClamAV for antivirus, Postfix for SMTP, and Dovecot for IMAP/POP3. It also supports DKIM signing to authenticate your email and prevent phishing, and provides automated SSL/TLS certificates via Let's Encrypt.

Docker Mailserver is designed to be run on any platform that supports Docker, including Linux, MacOS, and Windows.

", + "tags": [ + "email", + "mail server", + "SMTP", + "docker mailserver", + "open-source", + "mail server", + "docker", + "IMAP", + "POP3", + "LDAP", + "spam protection", + "antivirus", + "DKIM", + "SSL/TLS", + "linux", + "macos", + "windows" + ], + "repository": "https://github.com/docker-mailserver/docker-mailserver", + "image": "https://hub.docker.com/r/mailserver/docker-mailserver", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/DockerMailServer/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/DockerMailServer/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/DockerMailServer/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Dozzle", + "description": "Dozzle is a real-time log viewer for docker containers.", + "url": "", + "longDescription": "

Dozzle is a small lightweight application with a web based interface to monitor Docker logs. It doesn’t store any log files. It is for live monitoring of your container logs only.

", + "tags": [ + "dozzle", + "open-source", + "self-hosted", + "log viewer" + ], + "repository": "https://github.com/amir20/dozzle", + "image": "https://hub.docker.com/r/amir20/dozzle", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Dozzle/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Dozzle/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Dozzle/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Duplicati", + "description": "Duplicati is an open-source backup software that creates, encrypts, and stores backups of your files. It offers AES-256 encryption, incremental backups, scheduling, automated backups, and backup verification. Duplicati is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Duplicati is an open-source backup software designed to secure your data by creating, encrypting, and storing backups of your files. It can backup your files to local drives or various online storage services.

Key features of Duplicati include AES-256 encryption, incremental backups, scheduling, and automated backups. It also supports backup verification, ensuring that your files are not corrupted or lost. Its web-based interface makes it easy to manage and schedule your backups.

Duplicati is compatible with various platforms, including Windows, Linux, and MacOS, and it supports Docker, making it a versatile tool for various environments.

", + "tags": [ + "backup", + "automation", + "encryption", + "open-source", + "cross-platform", + "backup", + "encryption", + "scheduling", + "automated backup", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/duplicati/duplicati", + "image": "https://hub.docker.com/r/duplicati/duplicati", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/screenshots/2.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Duplicati/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Emulator-JS", + "description": "Emulator-JS is an open-source, in-browser multi-system emulator. It allows users to play games from various console systems directly in the browser, facilitated by an intuitive ROM management system. Emulator-JS can be used on any platform that supports a modern web browser, such as Windows, Linux, MacOS, and mobile devices.", + "url": "", + "longDescription": "

Emulator-JS is an open-source, in-browser multi-system emulator. It leverages JavaScript to emulate a wide range of gaming consoles, creating a convenient, accessible, and platform-agnostic gaming experience.

Key features of Emulator-JS include compatibility with multiple console systems, an intuitive ROM management system, and the ability to save game states. Because it's browser-based, Emulator-JS eliminates the need for complex installations or system-specific emulators.

As Emulator-JS runs directly in the browser, it can be used on any platform that supports a modern web browser, including Windows, Linux, MacOS, and even mobile devices. This makes it highly versatile for users across different environments.

", + "tags": [ + "gaming", + "emulation", + "web", + "open-source", + "cross-platform", + "emulator", + "in-browser", + "gaming", + "rom management", + "javascript", + "windows", + "linux", + "macos", + "mobile" + ], + "repository": "https://github.com/EmulatorJS/EmulatorJS", + "image": "https://hub.docker.com/r/linuxserver/emulatorjs", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Emulator-JS/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Filebrowser", + "description": "Filebrowser is an open-source, self-hosted web application for managing files within your server. It offers features like custom commands, user and permissions management, shareable links, built-in text editor, and media file previews. Filebrowser can be run on any platform that supports Go, including various Linux distributions, Windows, MacOS, and Docker.", + "url": "", + "longDescription": "

Filebrowser is an open-source, self-hosted web application for managing files within your server's file system. The application provides a user-friendly interface, making it simple to navigate, upload, download, and manage your files.

Key features of Filebrowser include support for custom commands, user and permissions management, shareable links, and a built-in text editor. It also supports various media file previews, including video and image file formats.

Filebrowser is designed to run on any platform that supports Go, including various Linux distributions, Windows, and MacOS. Additionally, it can be easily deployed using Docker.

", + "tags": [ + "filebrowser", + "open-source", + "self-hosted", + "file management", + "custom commands", + "permissions management", + "shareable links", + "text editor", + "media previews", + "go", + "linux", + "windows", + "macos", + "docker" + ], + "repository": "https://github.com/filebrowser/filebrowser", + "image": "https://hub.docker.com/r/filebrowser/filebrowser", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Filebrowser/screenshots/1.gif" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Filebrowser/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Filebrowser/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "FreshRSS", + "description": "FreshRSS is an open-source, self-hosted RSS feed aggregator that is lightweight and easy to use. Features include a responsive design, import/export OPML files, multiple themes, filters, categories, multi-user support, and extensibility with plugins. FreshRSS is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

FreshRSS is an open-source, self-hosted RSS feed aggregator. It is lightweight, easy to use, and packed with features. FreshRSS lets you collect, manage, and read your RSS feeds from your own server, offering a high degree of control and privacy.

Key features include a responsive design that works well on desktop and mobile devices, the ability to import/export OPML files, multiple themes, filters, categories, and multi-user support with the ability to manage user roles. Furthermore, it's extensible with plugins for additional functionalities.

FreshRSS is compatible with various platforms, including Windows, Linux, and MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "RSS", + "feed aggregator", + "news", + "freshrss", + "open-source", + "self-hosted", + "responsive", + "OPML", + "multi-user", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/FreshRSS/FreshRSS", + "image": "https://hub.docker.com/r/linuxserver/freshrss", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/FreshRSS/screenshots/1.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/FreshRSS/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/FreshRSS/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Gitea", + "description": "Gitea is a lightweight, open-source Git platform for self-hosted repository management, code collaboration, and issue tracking. It offers an intuitive interface, powerful access control, and integrations with popular development tools.", + "url": "", + "longDescription": "

Gitea is a lightweight, open-source Git platform that provides a self-hosted solution for managing repositories, collaborative code reviews, and issue tracking.

With Gitea, you can easily create and manage Git repositories, track changes, collaborate with team members, and streamline your software development workflow. It offers a simple and intuitive user interface, powerful access control, and a range of features such as code highlighting, pull requests, and issue management.

Gitea is written in Go and is designed to be lightweight, fast, and easy to deploy. It supports multiple authentication methods, including built-in user accounts, LDAP, and OAuth. Gitea also provides integrations with popular development tools and services.

Whether you're a small team or a large organization, Gitea provides a flexible and customizable platform for hosting your Git repositories and managing your software projects.

", + "tags": [ + "development", + "git", + "repository management", + "code collaboration", + "issue tracking", + "go", + "self-hosted", + "gitea" + ], + "repository": "https://github.com/go-gitea/gitea", + "image": "https://hub.docker.com/r/gitea/gitea", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/screenshots/3.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Gitea/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Grav", + "description": "Grav is an open-source, self-hosted, flat-file content management system (CMS). It offers features like content filtering, multi-language support, flexible templating with Twig, and simple content creation with Markdown. Grav can be extended with plugins and themes. It can be run on Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Grav is an open-source, self-hosted, flat-file content management system (CMS). Unlike traditional CMS platforms, Grav does not use a database to store content, making it incredibly lightweight and easy to manage.

Key features of Grav include powerful content filtering, multi-language support, flexible templating with Twig, and simple content creation process with Markdown. Its extendibility with a rich ecosystem of plugins and themes, plus a strong focus on performance, makes Grav a versatile platform for creating all kinds of websites.

Grav is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "web", + "blog", + "code", + "open-source", + "self-hosted", + "CMS", + "flat-file", + "content management", + "twig", + "markdown", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/getgrav/grav", + "image": "https://hub.docker.com/r/getgrav/grav", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Grav/screenshots/1.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grav/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grav/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Grocy", + "description": "Grocy is a self-hosted, open-source grocery management solution offering features like stock management with a barcode scanner, recipe management, household chore management, and tracking of purchased items with expiration dates. Grocy is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Grocy is a self-hosted, open-source grocery management solution designed to help users manage their groceries. It provides a suite of tools for managing shopping lists, recipes, inventory, and chores.

Key features include stock management with a barcode scanner, recipe management, household chore management, and tracking of purchased items with expiration dates. Grocy helps users to avoid waste by providing reminders about due dates of perishable items.

Grocy is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "life", + "groceries", + "list", + "open-source", + "self-hosted", + "grocery", + "management", + "shopping list", + "inventory", + "recipes", + "chores", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/grocy/grocy", + "image": "https://hub.docker.com/r/linuxserver/grocy", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Grocy/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grocy/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Grocy/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Handbrake", + "description": "HandBrake is a tool for converting video from nearly any format to a selection of modern, widely supported codecs.", + "url": "", + "longDescription": "

This project implements a Docker container for HandBrake.

The GUI of the application is accessed through a modern web browser (no installation or configuration needed on the client side) or via any VNC client.

A fully automated mode is also available: drop files into a watch folder and let HandBrake process them without any user interaction.

Full documentation is available at https://github.com/jlesage/docker-handbrake.

", + "tags": [ + "media", + "video", + "conversion" + ], + "repository": "https://github.com/jlesage/docker-handbrake", + "image": "https://hub.docker.com/r/jlesage/handbrake", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Handbrake/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Handbrake/screenshots/2.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Handbrake/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Handbrake/cosmos-compose.json", + "supported_architectures": [ + "amd64" + ] + }, + { + "name": "Heimdall", + "description": "Heimdall is an open-source, self-hosted dashboard software that provides a central hub for web-based applications and services. Features include a built-in application library, custom themes, multi-user support, and compatibility with desktop and mobile browsers. Heimdall is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Heimdall is an open-source, self-hosted dashboard software that allows you to organize and access your web-based applications and services all in one place. It's designed to provide a central hub to simplify your web environment.

Key features of Heimdall include the ability to add applications via a built-in library or by creating custom application definitions, custom themes, and the capacity to run on a desktop or mobile browser. It supports multiple users and each user can have their own personalized set of applications.

Heimdall is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "dashboard", + "home", + "icons", + "open-source", + "self-hosted", + "web-based applications", + "services", + "central hub", + "multi-user", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/linuxserver/Heimdall", + "image": "https://hub.docker.com/r/linuxserver/heimdall", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/screenshots/3.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Heimdall/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Homarr", + "description": "Homarr is an open-source, self-hosted dashboard software that provides a central hub for web-based applications and services. Features include a built-in application library, custom themes, multi-user support, and compatibility with desktop and mobile browsers. Homarr is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Homarr is an open-source, self-hosted dashboard software that allows you to organize and access your web-based applications and services all in one place. It's designed to provide a central hub to simplify your web environment.

Key features of Homarr include the ability to add applications via a built-in library or by creating custom application definitions, custom themes, and the capacity to run on a desktop or mobile browser. It supports multiple users and each user can have their own personalized set of applications.

Homarr is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "dashboard", + "home", + "icons", + "open-source", + "self-hosted", + "web-based applications", + "services", + "central hub", + "multi-user", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/ajnart/homarr", + "image": "https://hub.docker.com/r/homarr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Homarr/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Homarr/screenshots/2.webp" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Homarr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Homarr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Home Assistant", + "description": "Home Assistant is an open-source home automation platform that focuses on privacy and local control. It allows you to control all your devices from a single interface, integrating with a large number of devices and services. Home Assistant offers advanced automation capabilities, running perfectly on a Raspberry Pi or a local server. Start using Home Assistant today for a comprehensive home automation solution.", + "url": "", + "longDescription": "

Home Assistant is an open-source home automation platform focused on privacy and local control. It allows you to control all your devices from a single, unified interface. It is powered by a worldwide community of tinkerers and DIY enthusiasts, perfect to run on a Raspberry Pi or a local server.

Home Assistant integrates with a large number of different devices and services, providing the user with a powerful and flexible home automation control center. In addition to its powerful, flexible features, it provides advanced automation capabilities to help make your life easier.

With support for a vast array of devices and services, Home Assistant can be the one-stop solution for all your home automation needs. Get started with Home Assistant today and take control of your home automation!

", + "tags": [ + "smart home", + "home automation", + "IoT", + "Raspberry Pi", + "local server", + "privacy", + "control", + "automation", + "devices", + "services", + "home assistant" + ], + "repository": "https://github.com/home-assistant/core", + "image": "https://hub.docker.com/r/homeassistant/home-assistant", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/screenshots/2.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/screenshots/3.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/HomeAssistant/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Homebox", + "description": "Homebox is the inventory and organization system built for the Home User.", + "url": "", + "longDescription": "

Homebox is the inventory and organization system built for the Home User! With a focus on simplicity and ease of use, Homebox is the perfect solution for your home inventory, organization, and management needs.

", + "tags": [ + "home", + "homebox", + "organization", + "inventory", + "management", + "docker", + "linux" + ], + "repository": "https://github.com/hay-kot/homebox", + "image": "https://ghcr.io/hay-kot/homebox:latest", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Homebox/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Homebox/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Homebox/screenshots/3.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Homebox/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Homebox/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Immich", + "description": "Immich - High performance self-hosted photo and video backup solution", + "url": "", + "longDescription": "

Immich is an open-source personal photo management tool that makes use of the latest technologies to provide an easy and intuitive way to keep your photos organized. It provides a web-based platform to browse, organize, and share your personal photo collection.

With features like smart search, automated tagging, and geolocation, Immich makes it easy to find and group your photos. It supports various image formats and RAW files from high-quality cameras. Immich can also automatically generate thumbnails and convert RAW images.

Available for Windows, Linux, MacOS, and other platforms, Immich offers a private, self-hosted solution for your photo management needs. Start using Immich today to take control of your photo collection!

", + "tags": [ + "photo management", + "image organizer", + "smart search", + "automated tagging", + "geolocation", + "thumbnails", + "RAW images", + "self-hosted", + "windows", + "linux", + "macos", + "Immich" + ], + "repository": "https://github.com/Immich/Immich", + "image": "https://hub.docker.com/r/Immich/Immich", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/2.webp", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/3.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Jellyfin", + "description": "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Plex, to provide media from a dedicated server to end-user devices via multiple apps.", + "url": "", + "longDescription": "

Jellyfin is an open-source media system that gives you the power to control the management and streaming of your media content. It offers an alternative to proprietary systems like Plex, empowering users to deliver media content from a dedicated server to various end-user devices through a range of apps.

Key features of Jellyfin include the ability to stream movies, TV shows, music, photos, videos, audiobooks, and podcasts. It also supports various technologies and platforms such as DLNA, Chromecast, Android, iOS, Roku, FireTV, SmartTV, Web browser, Kodi, and Emby. Jellyfin essentially functions as a comprehensive media browser, server, streaming system, player, center, manager, organizer, and library. In addition to these, it also facilitates media sharing, transcoding, and casting, offering a robust solution for your media needs.

", + "tags": [ + "media", + "server", + "streaming", + "movies", + "tv", + "music", + "photos", + "videos", + "audiobooks", + "podcasts", + "dlna", + "chromecast", + "android", + "ios", + "roku", + "firetv", + "smarttv", + "web", + "browser", + "kodi", + "emby", + "plex", + "media browser", + "media server", + "media streaming", + "media player", + "media center", + "media management", + "media organizer", + "media collection", + "media library", + "media manager", + "media sharing", + "media transcoding", + "media casting", + "media casting", + "media casting" + ], + "repository": "https://github.com/jellyfin/jellyfin", + "image": "https://hub.docker.com/r/linuxserver/jellyfin", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/3.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/screenshots/4.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Jellyfin/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Joplin", + "description": "Joplin is a free, open-source note-taking and to-do application that supports markdown and end-to-end encryption. It offers capabilities such as tagging, searching, and modifying notes, and can sync with various cloud platforms. It features an extensible plugin system, allowing for tailored functionality. Joplin is compatible across multiple platforms including Windows, Linux, MacOS, iOS, and Android.", + "url": "", + "longDescription": "

Joplin is a free and open-source note-taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified directly from the applications or from your own text editor. It also supports markdown, with inline previewing.

Joplin offers end-to-end encryption and can synchronize with various cloud platforms, making it a secure choice for your note-taking needs. With its API, Joplin can be extended with plugins for custom functionality, further tailoring your note management experience.

Available for Windows, Linux, MacOS, iOS, and Android, Joplin is flexible to fit a variety of environments and workflows. Start using Joplin today to organize your thoughts and tasks in a secure and efficient manner!

", + "tags": [ + "productivity", + "note-taking", + "to-do", + "markdown", + "encryption", + "syncing", + "windows", + "linux", + "macos", + "ios", + "android", + "self-hosted", + "joplin" + ], + "repository": "https://github.com/laurent22/joplin", + "image": "https://hub.docker.com/r/joplin/server", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Joplin/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "KitchenOwl", + "description": "KitchenOwl helps you organize your grocery life.", + "url": "", + "longDescription": "

KitchenOwl is a smart grocery list and recipe manager.

Easily add items to your shopping list before you go shopping. You can also create recipes and get suggestions on what you want to cook.

Track your expenses so you know how much you've spent.

", + "tags": [ + "recipes", + "meals", + "shopping", + "planning", + "expenses", + "docker" + ], + "repository": "https://github.com/tombursch/kitchenowl", + "image": "https://hub.docker.com/r/tombursch/kitchenowl", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Kitchenowl/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Lemmy", + "description": "Lemmy is an open-source, self-hosted, federated link aggregator similar to Reddit. It offers community formation, moderation tools, content voting, and supports commenting and private messaging between users. Lemmy can be run on Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Lemmy is an open-source, self-hosted, federated link aggregator application similar to Reddit, which allows users to create their own communities and share links and posts with others.

Key features of Lemmy include its ability to form federated link-aggregator communities, moderation tools, and the ability to vote on content. It also supports commenting and private messaging between users. Lemmy is designed to consume minimal resources and can be easily self-hosted on small servers.

Lemmy is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "social", + "federation", + "reddit", + "open-source", + "self-hosted", + "federated", + "link aggregator", + "community", + "moderation", + "content voting", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/LemmyNet/lemmy", + "image": "https://hub.docker.com/r/dessalines/lemmy", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/screenshots/2.webp" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lemmy/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Lidarr", + "description": "Lidarr is an automated music manager for maintaining your music library. It does this by finding and automatically downloading your desired music tracks. It integrates with popular download clients and supports metadata fetching from multiple music databases. With Lidarr, your music downloading and organization process can be streamlined and automated. It supports various platforms and has a mobile-friendly web interface for easy access. Get started with Lidarr now and enhance your music management experience!", + "url": "", + "longDescription": "

Lidarr is an automated music collection manager that can find, add, and manage music in your collection. With Lidarr, you can automatically search for new albums and singles from your favorite artists, and automatically download them to your media server. It is integrated with popular download clients and supports multiple music databases for metadata fetching.

Lidarr is a perfect solution for those who want to automate their music downloading and organization process. It is available for various platforms including Windows, Linux, and MacOS, and has a mobile-friendly web interface that works across different devices.

Setting up Lidarr is straightforward. Whether you are an audiophile with a large collection or someone who just loves listening to music, Lidarr can transform your music management experience!

", + "tags": [ + "download", + "torrent", + "music", + "usenet", + "automation", + "manager", + "media", + "collection", + "download", + "metadata", + "windows", + "linux", + "macos", + "web interface", + "streaming", + "music library", + "music database", + "music management", + "music collection", + "lidarr" + ], + "repository": "https://github.com/lidarr/Lidarr", + "image": "https://hub.docker.com/r/linuxserver/lidarr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/screenshots/3.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Lidarr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "MKVToolNix", + "description": "MKVToolNix is a set of tools to create, alter and inspect Matroska files.", + "url": "", + "longDescription": "

This project implements a Docker container for MKVToolNix.

The GUI of the application is accessed through a modern web browser (no installation or configuration needed on the client side) or via any VNC client.

", + "tags": [ + "media", + "video", + "conversion" + ], + "repository": "https://github.com/jlesage/docker-mkvtoolnix", + "image": "https://hub.docker.com/r/jlesage/mkvtoolnix", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/MKVToolNix/screenshots/1.jpeg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/MKVToolNix/screenshots/2.jpeg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/MKVToolNix/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/MKVToolNix/cosmos-compose.json", + "supported_architectures": [ + "amd64" + ] + }, + { + "name": "Mastodon", + "description": "Mastodon is a free and open-source self-hosted social networking service. It features decentralization, ability to build your own community, per-post privacy settings, anti-abuse tools, and supports multimedia attachments. Mastodon can be run on any platform that supports Ruby, Node.js, and PostgreSQL, including various Linux distributions and Docker.", + "url": "", + "longDescription": "

Mastodon is a free and open-source self-hosted social networking service. It allows anyone to host their own server node in the network, while interacting with other users in a federated manner. It is similar to Twitter, but is distinguished by its decentralization and ability for users to host their own servers, and its character limit of 500 as opposed to Twitter's 280.

Key features of Mastodon include the ability to build your own community, granular, per-post privacy settings, and anti-abuse tools. It supports a rich variety of multimedia attachments in posts, and you can also choose to use content warnings on your posts for viewer discretion.

Mastodon can be run on any platform that supports Ruby, Node.js, and PostgreSQL, including various Linux distributions and Docker.

", + "tags": [ + "social", + "federation", + "reddit", + "open-source", + "self-hosted", + "federated", + "link aggregator", + "community", + "moderation", + "content voting", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/tootsuite/mastodon", + "image": "https://hub.docker.com/r/tootsuite/mastodon", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Mastodon/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "MeTube", + "description": "youtube-dl web UI", + "url": "", + "longDescription": "

Web GUI for youtube-dl (using the yt-dlp fork) with playlist support. Allows you to download videos from YouTube and dozens of other sites.

", + "tags": [ + "youtube", + "youtube-dl", + "download", + "videos", + "music", + "media" + ], + "repository": "https://github.com/alexta69/metube", + "image": "https://github.com/alexta69/metube/pkgs/container/metube", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/MeTube/screenshots/1.gif" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/MeTube/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/MeTube/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Minecraft", + "description": "Minecraft is a sandbox video game developed by Mojang Studios that allows players to explore, interact with, and modify a dynamically-generated map made of blocks. Players can build structures, craft items, and interact with the game world in a variety of ways. The game supports multiple gameplay styles and modes, and is available on a wide range of platforms.", + "url": "", + "longDescription": "

Minecraft is a highly popular and critically acclaimed sandbox video game developed by Mojang Studios. It allows players to explore a blocky, procedurally generated 3D world with virtually infinite terrain, and may discover and extract raw materials, craft tools and items, build structures, and interact with the game's various entities.

Gameplay involves players interacting with the game world by placing and breaking various types of blocks in a three-dimensional environment. In this environment, players can build creative structures, creations, and artwork on multiplayer servers and singleplayer worlds across multiple game modes. Minecraft supports a wide range of gameplay styles, including survival mode, creative mode, adventure mode, and spectator mode.

Minecraft is available on multiple platforms, including Windows, MacOS, Linux, and various consoles and mobile devices.

", + "tags": [ + "game servers", + "mojang", + "sandbox", + "game", + "procedurally generated", + "3D", + "multiplayer", + "singleplayer", + "survival", + "creative", + "adventure", + "spectator", + "windows", + "macos", + "linux", + "console", + "mobile" + ], + "repository": "https://www.minecraft.net/", + "image": "https://www.minecraft.net/en-us/about-minecraft", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Minecraft-Server/screenshots/1.webp" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Minecraft-Server/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Minecraft-Server/cosmos-compose.json", + "supported_architectures": [ + "x86", + "amd64", + "arm", + "arm64" + ] + }, + { + "name": "NZBGet", + "description": "NZBGet is a high-performance Usenet downloader that uses NZB files to fetch content. It is designed to use minimal system resources while offering features such as a web-interface, API, automatic post-processing, and RSS feed support. NZBGet works well on various platforms including low powered devices. Start using NZBGet for your Usenet downloads today!", + "url": "", + "longDescription": "

NZBGet is a robust and lightweight Usenet downloader. It can download files from Usenet servers via NZB index files. The software is focused on performance and uses very little system resources, while achieving high download speeds.

NZBGet offers a variety of features including web-interface, API, automatic post-processing, RSS feed support, and more. It is also designed to work well on low powered devices like Raspberry Pi.

Available on multiple platforms including Windows, MacOS, Linux, and others, NZBGet is easy to set up and use. Start using NZBGet today for efficient Usenet downloading!

", + "tags": [ + "download", + "torrent", + "usenet", + "downloader", + "nzb", + "windows", + "linux", + "macos", + "raspberry pi", + "performance", + "rss feed", + "post-processing", + "nzbget" + ], + "repository": "https://github.com/nzbget/nzbget", + "image": "https://hub.docker.com/r/linuxserver/nzbget", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/NZBGet/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Nextcloud", + "description": "Nextcloud is an open-source, self-hosted file share and collaboration platform, similar to Dropbox. It offers features such as file sharing, syncing, encryption, and collaboration tools. With its extensible app ecosystem, you can tailor your cloud storage service to your needs. Nextcloud is compatible with numerous platforms and can be integrated with many IT infrastructure systems.", + "url": "", + "longDescription": "

Nextcloud is a suite of client-server software for creating and using file hosting services. It is functionally similar to Dropbox, but is free and open-source, allowing users to install it on private servers.

Nextcloud provides an array of features including file sharing, syncing, end-to-end encryption, collaboration tools, and more. Its extensible app ecosystem allows you to set up your own cloud storage service to your exact needs. You can also integrate Nextcloud with many known IT infrastructure systems.

Available for Windows, Linux, MacOS, and other platforms, Nextcloud is designed to work in a variety of environments including both on premise and cloud setups. Start using Nextcloud today to control and secure your own private cloud storage!

", + "tags": [ + "storage", + "file sharing", + "syncing", + "encryption", + "collaboration", + "windows", + "linux", + "macos", + "self-hosted", + "nextcloud" + ], + "repository": "https://github.com/nextcloud/server", + "image": "https://hub.docker.com/r/linuxserver/nextcloud", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/screenshots/3.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Nextcloud/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "OhMyForm", + "description": "OhMyForm is a free, open-source form creation software. It offers a range of features for creating, administering, analyzing, and distributing beautiful, embeddable forms for various purposes such as recruiting, market research, and surveys. OhMyForm allows for self-hosting with no installation fees or monthly charges.", + "url": "", + "longDescription": "

OhMyForm is a free, open-source software that allows you to craft beautiful forms in seconds for various purposes such as recruiting, market research, surveys, and more. It offers a user-friendly interface and features that make creating, administering, analyzing, and distributing forms straightforward.

Key features of OhMyForm include its wide variety of built-in tools, the ability to create embeddable forms, and a powerful community of contributors that constantly improve and vet the platform. As open-source software, OhMyForm is not only always improving but also completely free.

One of the advantages of OhMyForm is the ability to host it yourself, ensuring no installation fees or monthly charges. This commitment to being 100% open-source allows users to maintain full control over their forms and data.

", + "tags": [ + "productivity", + "open-source", + "self-hosted", + "form creation", + "surveys", + "recruiting", + "market research", + "embeddable forms" + ], + "repository": "https://github.com/ohmyform/ohmyform", + "image": "https://hub.docker.com/r/ohmyform/ohmyform", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/OhMyForm/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/OhMyForm/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/OhMyForm/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Ombi", + "description": "Ombi is an open-source, self-hosted web application that enables Plex or Emby users to request content. It offers features like user management, a request system, notification system, and automatic updates. Ombi is compatible with popular platforms such as Plex, Emby, Sonarr, Radarr, and Tautulli. It can be run on Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Ombi is an open-source, self-hosted web application that enables you to give your shared Plex or Emby users the ability to request content. It's an intuitive interface between your media server and the users.

Key features of Ombi include user management, a request system, notification system, and automatic updates. Ombi can be integrated with popular platforms such as Plex, Emby, Sonarr, Radarr, and Tautulli, making it an essential tool for managing requests for media content.

Ombi is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "media", + "request", + "library", + "open-source", + "self-hosted", + "web application", + "plex", + "emby", + "request system", + "media content", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/Ombi-app/Ombi", + "image": "https://hub.docker.com/r/linuxserver/ombi", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Ombi/screenshots/1.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Ombi/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Ombi/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Overseerr", + "description": "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem.", + "url": "", + "longDescription": "

Overseerr is a free and open source software application for managing requests for your media library. It integrates with your existing services, such as Sonarr, Radarr, and Plex.

Overseerr helps you find media you want to watch. With inline recommendations and suggestions, you will find yourself deeper and deeper in a rabbit hole of content you never knew you just had to have.

Overseerr presents you and your users with a request interface that is incredibly easy to understand and use. Users can select the exact seasons they want to watch. Advanced users can use the “Advanced Requests” options to change destination folders and quality profiles.

Overseerr aims to make you and your user's lives more effortless than ever before.

", + "tags": [ + "media", + "request", + "library", + "open-source", + "self-hosted", + "web application", + "plex", + "request system", + "media content", + "windows", + "linux", + "macos", + "docker", + "radarr", + "sonarr" + ], + "repository": "https://github.com/sct/overseerr", + "image": "https://hub.docker.com/r/linuxserver/overseerr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Overseerr/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Overseerr/screenshots/2.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Overseerr/screenshots/3.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Overseerr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Overseerr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Paperless-ngx", + "description": "Paperless-ngx is a document management system that transforms your physical documents into a searchable online archive so you can keep, well, less paper.", + "url": "", + "longDescription": "

Paperless-ngx is a community-supported open-source document management system that transforms your physical documents into a searchable online archive so you can keep, well, less paper.

This version of Paperless-ngx is configured using PostgreSQL as the database server, as well as Apache, Tika, and Gotenberg servers to provide support for consuming Office documents (Word, Excel, Power Point and their LibreOffice counterparts.

", + "tags": [ + "document", + "management", + "organization", + "archive", + "collection", + "web interface", + "paper", + "docker" + ], + "repository": "https://github.com/paperless-ngx/paperless-ngx", + "image": "https://ghcr.io/paperless-ngx/paperless-ngx", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Paperless-ngx/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Paperless-ngx/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Paperless-ngx/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Paperless-ngx/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "PhotoPrism", + "description": "PhotoPrism is an open-source personal photo management tool that uses smart search, automated tagging, and geolocation to help organize your photos. It supports various image formats, generates thumbnails, and converts RAW images. PhotoPrism works across various platforms and offers a private, self-hosted solution for managing your photos. Start using PhotoPrism today for your photo management needs.", + "url": "", + "longDescription": "

PhotoPrism is an open-source personal photo management tool that makes use of the latest technologies to provide an easy and intuitive way to keep your photos organized. It provides a web-based platform to browse, organize, and share your personal photo collection.

With features like smart search, automated tagging, and geolocation, PhotoPrism makes it easy to find and group your photos. It supports various image formats and RAW files from high-quality cameras. PhotoPrism can also automatically generate thumbnails and convert RAW images.

Available for Windows, Linux, MacOS, and other platforms, PhotoPrism offers a private, self-hosted solution for your photo management needs. Start using PhotoPrism today to take control of your photo collection!

", + "tags": [ + "photo management", + "image organizer", + "smart search", + "automated tagging", + "geolocation", + "thumbnails", + "RAW images", + "self-hosted", + "windows", + "linux", + "macos", + "photoprism" + ], + "repository": "https://github.com/photoprism/photoprism", + "image": "https://hub.docker.com/r/photoprism/photoprism", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Photoprism/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Plex", + "description": "Plex organizes all of your video, and music collections, and gives you instant access to them on all of your devices. With the free Plex Media Server software on your home computer and Plex for iOS, you can enjoy all of your personal media on your iPhone, iPad or iPod touch, and easily share it with friends and family. Plex also makes your media look beautiful with rich descriptions, artwork, and other related information. With an optional premium Plex Pass subscription, you can even sync videos, music, and photos to your smartphones and tablets to enjoy while offline. Parental controls, premium music features, music videos, trailers and extras, and powerful management tools are also part of our premium offering. Getting up and running is fast and simple, so get started now!", + "url": "", + "longDescription": "

Plex is a comprehensive media solution that organizes your video and music collections, giving you instant access across all your devices. With the free Plex Media Server software installed on your home computer and Plex's iOS app, you can enjoy your personal media on your iPhone, iPad, or iPod touch, and conveniently share it with your friends and family.

Plex is not just about easy access, it also enhances your media collection by adding rich descriptions, artwork, and other related information, making your media look visually appealing. If you choose to subscribe to the optional premium Plex Pass, you get the added ability to sync videos, music, and photos to your smartphones and tablets for offline enjoyment.

Premium features also include parental controls, enhanced music features, access to music videos, trailers, extras, and robust management tools. Starting with Plex is straightforward and quick. Get started today and transform your media experience!

", + "tags": [ + "media", + "movies", + "streaming", + "tv", + "music", + "photos", + "videos", + "audiobooks", + "podcasts", + "dlna", + "chromecast", + "android", + "ios", + "roku", + "firetv", + "smarttv", + "web", + "browser", + "kodi", + "emby", + "plex", + "media browser", + "media server", + "media streaming", + "media player", + "media center", + "media management", + "media organizer", + "media collection", + "media library", + "media manager", + "media sharing", + "media transcoding", + "media casting", + "media casting", + "media casting" + ], + "repository": "https://github.com/plex/plex", + "image": "https://hub.docker.com/r/linuxserver/plex", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Plex/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Radarr", + "description": "Radarr is an automated movie download application, similar to CouchPotato. It helps you to manage your movie collection with features like metadata fetching, trailer linking, and integration with popular download clients. Radarr works across Windows, Linux, MacOS and has a mobile-friendly web interface. Start using Radarr now to automate and enhance your movie downloading and management process.", + "url": "", + "longDescription": "

Radarr is an independent fork of Sonarr reworked for automatically downloading movies via Usenet and BitTorrent. The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.

Radarr provides a sleek, customizable interface to manage your movie collection, offering features like trailer linking, metadata fetching, and more. The software integrates with popular download clients and can automate your movie download process.

Radarr supports a variety of platforms including Windows, Linux, and MacOS, and also offers a mobile-friendly web interface. Setting up Radarr is straightforward. Get started today to streamline your movie downloading and management experience!

", + "tags": [ + "download", + "movies", + "torrent", + "usenet", + "automated", + "downloader", + "media", + "collection", + "usenet", + "bittorrent", + "windows", + "linux", + "macos", + "web interface", + "metadata", + "movie management", + "movie collection", + "radarr" + ], + "repository": "https://github.com/Radarr/Radarr", + "image": "https://hub.docker.com/r/linuxserver/radarr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Radarr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Readarr", + "description": "Readarr is an open-source, self-hosted PVR for managing and downloading ebooks. It offers features like adding favorite authors, automatic book searching, calendar integration, and importing existing libraries. Readarr supports integration with popular download clients and can send notifications. It can be run on Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Readarr is an open-source, self-hosted PVR (Personal Video Recorder) for managing and downloading ebooks. It's built off of the Radarr, Sonarr, and Lidarr platforms and brings the same powerful features to the world of ebooks.

Key features of Readarr include the ability to add your favorite authors, automatic book searching, calendar integration, and the ability to import existing libraries. It supports integration with popular download clients and can send notifications via a number of platforms.

Readarr is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "download", + "ebooks", + "readarr", + "open-source", + "self-hosted", + "PVR", + "book management", + "download", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/Readarr/Readarr", + "image": "https://hub.docker.com/r/linuxserver/readarr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Readarr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Ryot", + "description": "A self hosted platform for tracking various facets of your life - media, fitness etc.", + "url": "", + "longDescription": "

Ryot is a self hosted platform for tracking various facets of your life - media, fitness etc. Imagine you have a special notebook where you can write down all the media you have consumed, like books you've read, shows you have watched, video games you have played or workouts you have done. Now, imagine that instead of a physical notebook, you have a special tool on your computer or phone that lets you keep track of all these digitally.

", + "tags": [ + "tracker", + "media", + "fitness", + "open-source", + "collection", + "docker", + "windows", + "linux", + "macos", + "web interface", + "metadata", + "episode tracking", + "movie tracking", + "audiobook tracking", + "reading tracking", + "fitness tracking", + "integrations", + "ryot" + ], + "repository": "https://github.com/IgnisDa/ryot", + "image": "https://ghcr.io/ignisda/ryot", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Ryot/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Ryot/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Ryot/screenshots/3.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Ryot/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Ryot/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Sonarr", + "description": "Sonarr is an automated TV show manager that can search, download, and manage your TV series. It offers features like automatic quality upgrades, episode tracking, and metadata fetching. Sonarr works across various platforms including Windows, Linux, MacOS, and has a mobile-friendly web interface. Start using Sonarr now to automate your TV show management process.", + "url": "", + "longDescription": "

Sonarr is a multi-platform app to search, download, and manage TV shows. It automates the process of searching for new episodes, and can automatically download these files with your preferred download software.

Sonarr offers features such as automatic quality upgrades, series and episode tracking, and metadata fetching to enrich your media library. The interface is user-friendly and mobile-responsive, ensuring you can manage your TV shows easily on any device.

Available for Windows, Linux, and MacOS, Sonarr makes the TV show management process easy and efficient. Get started with Sonarr today and transform your media management experience!

", + "tags": [ + "download", + "tv shows", + "torrent", + "usenet", + "automated", + "manager", + "media", + "collection", + "windows", + "linux", + "macos", + "web interface", + "metadata", + "episode tracking", + "quality upgrades", + "sonarr" + ], + "repository": "https://github.com/Sonarr/Sonarr", + "image": "https://hub.docker.com/r/linuxserver/sonarr", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/screenshots/3.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Sonarr/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Tandoor", + "description": "Tandoor is a recipe manager that allows you to manage your ever growing collection of digital recipes.", + "url": "", + "longDescription": "

Tandoor Recipes is the self hosted recipe manager with meal planning, shopping lists and cookbook collections.

The days of storing recipes on paper cards is in the past and what better way to preserve older recipes that were handed down then to log them into a digital, self hosted recipe manager where you can access them anywhere?

Tandoor Recipes is much more than a digital recipe reference archive, it's a complete meal planning power house.

", + "tags": [ + "recipes", + "meals", + "shopping", + "planning", + "cookbook", + "docker", + "pwa" + ], + "repository": "https://github.com/TandoorRecipes/recipes", + "image": "https://hub.docker.com/r/vabene1111/recipes", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/screenshots/2.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Tandoor/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Transmission", + "description": "Transmission is an open-source, cross-platform BitTorrent client. It is widely praised for its minimal resource usage, ease-of-use, and performance. Transmission provides features like built-in encryption, webseed support, peer exchange, automatic port mapping, and individual torrent speed limit settings. It supports Windows, Linux, MacOS, and also offers a web interface for remote control.", + "url": "", + "longDescription": "

Transmission is a cross-platform BitTorrent client that is open-source, volunteer-based, and free from advertisements. It is highly regarded for its low resource usage, simplicity, and performance.

With its intuitive interface, Transmission allows users to quickly and easily download and share files via the BitTorrent protocol. Its standout features include built-in encryption, webseed support, peer exchange, automatic port mapping, and the ability to set speed limits for individual torrents.

Transmission is available for a variety of platforms including Windows, Linux, and MacOS. It also provides a web interface for remote control, making it a popular choice among users looking for a lightweight, reliable BitTorrent client.

", + "tags": [ + "download", + "torrent", + "bittorrent", + "downloader", + "open-source", + "cross-platform", + "file sharing", + "peer-to-peer", + "p2p", + "transmission", + "windows", + "linux", + "macos", + "web interface" + ], + "repository": "https://github.com/transmission/transmission", + "image": "https://hub.docker.com/r/linuxserver/transmission", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Transmission/screenshots/1.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Transmission/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Transmission/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Unmanic", + "description": "Unmanic is a simple tool for optimising your file library. You can use it to convert your files into a single, uniform format, manage file movements based on timestamps, or execute custom commands against a file based on its file size.", + "url": "", + "longDescription": "

Unmanic is a simple tool for optimising your file library. You can use it to convert your files into a single, uniform format, manage file movements based on timestamps, or execute custom commands against a file based on its file size.

Simply configure Unmanic pointing it at your library and let it automatically manage that library for you.

Unmanic provides you with the following main functions:

  • A scheduler built in to scan your whole library for files that do not conform to your configured file presets. Files found requiring processing are then queued.
  • A file/directory monitor. When a file is modified, or a new file is added in your library, Unmanic is able to again test that against your configured file presets. Like the first function, if this file requires processing, it is added to a queue for processing.
  • A handler to manage running multiple file manipulation tasks at a time.
  • A Web UI to easily configure, manage and monitor the progress of your library optimisation.
", + "tags": [ + "media", + "server", + "tv", + "videos", + "web", + "browser", + "media browser", + "media server", + "media streaming", + "media player", + "media center", + "media management", + "media organizer", + "media collection", + "media library", + "media manager" + ], + "repository": "https://github.com/Unmanic/unmanic", + "image": "https://hub.docker.com/r/josh5/unmanic", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Unmanic/screenshots/1.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Unmanic/screenshots/2.png", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Unmanic/screenshots/3.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Unmanic/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Unmanic/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm7", + "arm64" + ] + }, + { + "name": "Uptime Kuma", + "description": "Uptime Kuma is an open-source, self-hosted monitoring tool for tracking the uptime of online services. It offers status checks for various services, detailed statistics with charts, and multiple notification methods. Uptime Kuma is compatible with Windows, Linux, MacOS, and Docker.", + "url": "", + "longDescription": "

Uptime Kuma is an open-source, self-hosted monitoring tool that allows users to monitor their online services and get alerts when they go down or become unresponsive. This robust yet user-friendly tool is designed to help you keep track of your services' uptime.

Key features of Uptime Kuma include status checking for various services including HTTP(s), TCP, and ICMP, detailed statistics with charts, and multiple notification methods (like email, Slack, Telegram, etc.). Its clean, intuitive user interface makes setup and management of monitored services easy.

Uptime Kuma is compatible with various platforms, including Windows, Linux, MacOS, and it supports Docker, making it a highly versatile tool for various environments.

", + "tags": [ + "technical", + "administration", + "server", + "open-source", + "self-hosted", + "monitoring", + "uptime", + "service status", + "notifications", + "windows", + "linux", + "macos", + "docker" + ], + "repository": "https://github.com/louislam/uptime-kuma", + "image": "https://hub.docker.com/r/louislam/uptime-kuma", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/screenshots/2.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/UptimeKuma/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Van_DAM", + "description": "A self-hosted digital asset manager for 3d print files", + "url": "", + "longDescription": "

VanDAM is a Digital Asset Manager (DAM), specifically designed for 3D print files. Create a library pointing at your files on disk, and it will scan for models and parts. It assumes that any folders containing STL or OBJ files are models, and the files within them are parts. You can then view the files easily through your browser!

", + "tags": [ + "3d models", + "3d printing" + ], + "repository": "https://github.com/Floppy/van_dam", + "image": "ghcr.io/floppy/van_dam", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Van_DAM/screenshots/1.jpg", + "https://azukaar.github.io/cosmos-servapps-official/servapps/Van_DAM/screenshots/2.jpg" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Van_DAM/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Van_DAM/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "Vaultwarden", + "description": "Vaultwarden is an open-source, self-hosted password management server compatible with Bitwarden apps. Written in Rust, it provides a secure way to store and synchronize sensitive data across devices with lightweight and low resource usage. Vaultwarden supports various platforms, including Linux, Windows, MacOS, and Docker.", + "url": "", + "longDescription": "

Vaultwarden is an open-source password management server that is compatible with Bitwarden apps. It provides a secure and free self-hosted solution to store and synchronize sensitive data across multiple devices, with the ability to share data across organizations, users, and teams.

Written in Rust, Vaultwarden is designed for lightweight and low resource usage. It allows you to store all kinds of sensitive information, including passwords, credit card details, and secure notes, which can be accessed through Bitwarden's web, mobile, or browser extension apps.

Vaultwarden supports various platforms, including Linux, Windows, MacOS, and can be deployed easily using Docker.

", + "tags": [ + "productivity", + "password-manager", + "password manager", + "bitwarden", + "rust", + "sensitive data", + "synchronization", + "linux", + "windows", + "macos", + "docker" + ], + "repository": "https://github.com/dani-garcia/vaultwarden", + "image": "https://hub.docker.com/r/vaultwarden/server", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Vaultwarden/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Vaultwarden/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Vaultwarden/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + }, + { + "name": "WordPress", + "description": "WordPress is a free, open-source content management system based on PHP and MySQL. Known for its flexibility and ease-of-use, it supports a range of website types with thousands of plugins and themes. WordPress features a robust administrative dashboard for website creation, modification, and management. It is widely supported across various hosting platforms.", + "url": "", + "longDescription": "

WordPress is a free and open-source content management system (CMS) based on PHP and MySQL. It is one of the most popular website management or blogging system in use on the Web, supporting more than 60 million websites.

WordPress allows users to create and edit websites through a central administrative dashboard, which includes a text editor for modifying content, menus and various design elements. It offers thousands of themes and plugins that can enhance functionality and customization options, making it suitable for a variety of website types, from personal blogs to online stores.

With its vast community, WordPress users have access to a plethora of resources, tutorials, and guides. Start your website journey with WordPress today!

", + "tags": [ + "blogging", + "cms", + "website management", + "php", + "mysql", + "themes", + "plugins", + "self-hosted", + "wordpress" + ], + "repository": "https://github.com/WordPress/WordPress", + "image": "https://hub.docker.com/_/wordpress", + "screenshots": [ + "https://azukaar.github.io/cosmos-servapps-official/servapps/Wordpress/screenshots/1.png" + ], + "icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Wordpress/icon.png", + "compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Wordpress/cosmos-compose.json", + "supported_architectures": [ + "amd64", + "arm64" + ] + } + ] + } + }, + "status": "OK" + }); }); } diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 91cb749..7d401ec 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -72,7 +72,7 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { return API.constellation.addDevice(values).then(({data}) => { setIsDone(data); refreshConfig(); - renderCanvas(data); + renderCanvas(data.Config); }).catch((err) => { setErrors(err.response.data); }); @@ -92,24 +92,24 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {

- +
- + {/* - + /> */} + - { filename={`ca.crt`} content={isDone.CA} label={"Download ca.crt"} - /> + /> */}
: diff --git a/package.json b/package.json index ca4cc36..64a84c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable12", + "version": "0.10.0-unstable13", "description": "", "main": "test-server.js", "bugs": { diff --git a/readme.md b/readme.md index d7219bb..7f1d879 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,7 @@

null null Clayton Stone -Seraph91P +null

--- diff --git a/src/constellation/DNS.go b/src/constellation/DNS.go index 9764c74..1f303ae 100644 --- a/src/constellation/DNS.go +++ b/src/constellation/DNS.go @@ -5,7 +5,6 @@ import ( "strconv" "strings" "io/ioutil" - "fmt" "github.com/miekg/dns" "github.com/azukaar/cosmos-server/src/utils" @@ -41,52 +40,6 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { // []string hostnames hostnames := utils.GetAllHostnames(false, true) - originalHostname := hostnames[0] - - specialQuery := false - - // if lighthouse-cosmos.constellation is the query, return originalHostname's external lookup - for i, q := range r.Question { - if strings.HasSuffix(q.Name, "lighthouse-cosmos.constellation.") { - utils.Debug("DNS Overwrite lighthouse-cosmos.constellation with " + originalHostname) - - // Create a deep copy of the original request. - modifiedRequest := r.Copy() - - client := new(dns.Client) - - // Modify only the copied request. - modifiedRequest.Question[i].Name = originalHostname + "." - - externalResponse, time, err := externalLookup(client, modifiedRequest, DNSFallback) - if err != nil { - utils.Error("Failed to forward query:", err) - return - } - utils.Debug("DNS Forwarded DNS query to "+DNSFallback+" in " + time.String()) - - for _, rr := range externalResponse.Answer { - if aRecord, ok := rr.(*dns.A); ok { - // 2. Replace the hostname with "lighthouse-cosmos.constellation". - modifiedString := fmt.Sprintf("lighthouse-cosmos.constellation. A %s", aRecord.A.String()) - - // 3. Convert the string back into a dns.RR. - newRR, err := dns.NewRR(modifiedString) - if err != nil { - utils.Error("Failed to convert string into dns.RR:", err) - return - } - - // Replace the response RR with the new RR. - r.Answer = append(r.Answer, newRR) - } - } - - m = r - - specialQuery = true - } - } if !customHandled { customDNSEntries := config.ConstellationConfig.CustomDNSEntries @@ -104,7 +57,7 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } } - if !specialQuery { + if !customHandled { // Overwrite local hostnames with Constellation IP for _, q := range r.Question { utils.Debug("DNS Question " + q.Name) @@ -117,37 +70,37 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } } } + } - if !customHandled { - // Block blacklisted domains - for _, q := range r.Question { - noDot := strings.TrimSuffix(q.Name, ".") - if DNSBlacklist[noDot] { - if q.Qtype == dns.TypeA { - utils.Debug("DNS Block " + noDot) - rr, _ := dns.NewRR(q.Name + " A 0.0.0.0") - m.Answer = append(m.Answer, rr) - } - - customHandled = true + if !customHandled { + // Block blacklisted domains + for _, q := range r.Question { + noDot := strings.TrimSuffix(q.Name, ".") + if DNSBlacklist[noDot] { + if q.Qtype == dns.TypeA { + utils.Debug("DNS Block " + noDot) + rr, _ := dns.NewRR(q.Name + " A 0.0.0.0") + m.Answer = append(m.Answer, rr) } + + customHandled = true } } + } - // If not custom handled, use external DNS - if !customHandled { - client := new(dns.Client) - externalResponse, time, err := externalLookup(client, r, DNSFallback) - if err != nil { - utils.Error("Failed to forward query:", err) - return - } - utils.Debug("DNS Forwarded DNS query to "+DNSFallback+" in " + time.String()) - - externalResponse.Id = r.Id - - m = externalResponse + // If not custom handled, use external DNS + if !customHandled { + client := new(dns.Client) + externalResponse, time, err := externalLookup(client, r, DNSFallback) + if err != nil { + utils.Error("Failed to forward query:", err) + return } + utils.Debug("DNS Forwarded DNS query to "+DNSFallback+" in " + time.String()) + + externalResponse.Id = r.Id + + m = externalResponse } w.WriteMsg(m) diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index adab752..f37e54a 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -81,15 +81,6 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { return } - // read configYml from config/nebula.yml - configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml") - if err != nil { - utils.Error("DeviceCreation: Error while reading config", err) - utils.HTTPError(w, "Device Creation Error: " + err.Error(), - http.StatusInternalServerError, "DC005") - return - } - capki, err := getCApki() if err != nil { utils.Error("DeviceCreation: Error while reading ca.crt", err) @@ -98,6 +89,15 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { return } + // read configYml from config/nebula.yml + configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key) + if err != nil { + utils.Error("DeviceCreation: Error while reading config", err) + utils.HTTPError(w, "Device Creation Error: " + err.Error(), + http.StatusInternalServerError, "DC005") + return + } + json.NewEncoder(w).Encode(map[string]interface{}{ "status": "OK", "data": map[string]interface{}{ diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 750babc..da8d614 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -86,7 +86,6 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st finalConfig.StaticHostMap = map[string][]string{ "192.168.201.1": []string{ - "lighthouse-cosmos.constellation:4242", utils.GetMainConfig().HTTPConfig.Hostname + ":4242", }, } @@ -119,7 +118,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st return nil } -func getYAMLClientConfig(name, configPath string) (string, error) { +func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, error) { utils.Log("Exporting YAML config for " + name + " with file " + configPath) // Read the YAML config file @@ -137,7 +136,6 @@ func getYAMLClientConfig(name, configPath string) (string, error) { if staticHostMap, ok := configMap["static_host_map"].(map[interface{}]interface{}); ok { staticHostMap["192.168.201.1"] = []string{ - "lighthouse-cosmos.constellation:4242", utils.GetMainConfig().HTTPConfig.Hostname + ":4242", } } else { @@ -156,9 +154,9 @@ func getYAMLClientConfig(name, configPath string) (string, error) { } if pkiMap, ok := configMap["pki"].(map[interface{}]interface{}); ok { - pkiMap["ca"] = "ca.crt" - pkiMap["cert"] = name + ".crt" - pkiMap["key"] = name + ".key" + pkiMap["ca"] = capki + pkiMap["cert"] = cert + pkiMap["key"] = key } else { return "", errors.New("pki not found in nebula.yml") } @@ -170,6 +168,8 @@ func getYAMLClientConfig(name, configPath string) (string, error) { return "", errors.New("relay not found in nebula.yml") } + configMap["deviceName"] = name + // export configMap as YML yamlData, err = yaml.Marshal(configMap) if err != nil { diff --git a/src/proxy/routerGen.go b/src/proxy/routerGen.go index 19cab92..f4aef7c 100644 --- a/src/proxy/routerGen.go +++ b/src/proxy/routerGen.go @@ -85,6 +85,8 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt } } + destination = utils.Restrictions(route.RestrictToConstellation)(destination) + destination = SmartShieldMiddleware(route.Name, route.SmartShield)(destination) originCORS := route.CORSOrigin @@ -143,8 +145,6 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt destination = utils.SetSecurityHeaders(destination) } - destination = utils.Restrictions(route.RestrictToConstellation)(destination) - destination = tokenMiddleware(route.AuthEnabled, route.AdminOnly)(utils.CORSHeader(originCORS)((destination))) origin.Handler(destination) From c912f1664085b6e8cd9338b7ab49b5cf4e2c1601 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 13 Sep 2023 22:11:50 +0100 Subject: [PATCH 17/36] [skip ci] stuff --- client/src/pages/home/index.jsx | 2 +- readme.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index e3cee33..253f497 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -228,7 +228,7 @@ const HomePage = () => { {coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && ( - There are errors with your Let's Encrypt configuration or one of your routes, please fix them as soon as possible.: + There are errors with your Let's Encrypt configuration or one of your routes, please fix them as soon as possible: {coStatus.LetsEncryptErrors.map((err) => { return
- {err}
})} diff --git a/readme.md b/readme.md index 7f1d879..8d00e7d 100644 --- a/readme.md +++ b/readme.md @@ -8,6 +8,7 @@ null Clayton Stone null +Adrian Germeck

--- From ef37940742e1d9704d1db52faa2dffac7af85347 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Fri, 15 Sep 2023 18:00:59 +0100 Subject: [PATCH 18/36] [release] v0.10.0-unstable14 --- client/src/pages/config/routes/routeman.jsx | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/pages/config/routes/routeman.jsx b/client/src/pages/config/routes/routeman.jsx index e3f1162..83349da 100644 --- a/client/src/pages/config/routes/routeman.jsx +++ b/client/src/pages/config/routes/routeman.jsx @@ -72,6 +72,7 @@ const RouteManagement = ({ routeConfig, routeNames, config, TargetContainer, noC StripPathPrefix: routeConfig.StripPathPrefix, AuthEnabled: routeConfig.AuthEnabled, _SmartShield_Enabled: (routeConfig.SmartShield ? routeConfig.SmartShield.Enabled : false), + RestrictToConstellation: routeConfig.RestrictToConstellation, }} validationSchema={ValidateRouteSchema} onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => { @@ -256,6 +257,12 @@ const RouteManagement = ({ routeConfig, routeNames, config, TargetContainer, noC label="Smart Shield Protection" formik={formik} /> + + {submitButton && + { + callback(); + setOpenModal(false); + }}>Confirm + +
+ + + +}; + +export default ConfirmModal; diff --git a/client/src/components/fileUpload.jsx b/client/src/components/fileUpload.jsx index a7ba9fc..a541984 100644 --- a/client/src/components/fileUpload.jsx +++ b/client/src/components/fileUpload.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { Button } from '@mui/material'; import { UploadOutlined } from '@ant-design/icons'; -export default function UploadButtons({OnChange, accept, label}) { +export default function UploadButtons({OnChange, accept, label, variant, fullWidth, size}) { return (
diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 7d401ec..ea2aea8 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -12,10 +12,67 @@ import { PlusCircleFilled } from '@ant-design/icons'; import { Formik } from 'formik'; import * as yup from 'yup'; import * as API from '../../api'; -import { CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; +import { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; import { DownloadFile } from '../../api/downloadButton'; import QRCode from 'qrcode'; +const getDocker = (data, isCompose) => { + let lighthouses = ''; + + for (let i = 0; i < data.LighthousesList.length; i++) { + const l = data.LighthousesList[i]; + lighthouses += l.publicHostname + ";" + l.ip + ":" + l.port + ";" + l.isRelay + ","; + } + + let containerName = "cosmos-constellation-lighthouse"; + let imageName = "cosmos-constellation-lighthouse:latest"; + + let volPath = "/var/lib/cosmos-constellation"; + + if (isCompose) { + return ` +version: "3.8" +services: + ${containerName}: + image: ${imageName} + container_name: ${containerName} + restart: unless-stopped + network_mode: bridge + ports: + - "${data.Port}:4242" + volumes: + - ${volPath}:/config + environment: + - CA=${JSON.stringify(data.CA)} + - CERT=${JSON.stringify(data.PrivateKey)} + - KEY=${JSON.stringify(data.PublicKey)} + - LIGHTHOUSES=${lighthouses} + - PUBLIC_HOSTNAME=${data.PublicHostname} + - IS_RELAY=${data.IsRelay} + - IP=${data.IP} +`; + } else { + return ` +docker run -d \\ + --name ${containerName} \\ + --restart unless-stopped \\ + --network bridge \\ + -v ${volPath}:/config \\ + -e CA=${JSON.stringify(data.CA)} \\ + -e CERT=${JSON.stringify(data.PrivateKey)} \\ + -e KEY=${JSON.stringify(data.PublicKey)} \\ + -e LIGHTHOUSES=${lighthouses} \\ + -e PUBLIC_HOSTNAME=${data.PublicHostname} \\ + -e IS_RELAY=${data.IsRelay} \\ + -e IP=${data.IP} \\ + -p ${data.Port}:4242 \\ + ${imageName} +`; + } + +} + + const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { const [openModal, setOpenModal] = useState(false); const [isDone, setIsDone] = useState(null); @@ -63,12 +120,18 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { deviceName: '', ip: firstIP, publicKey: '', + Port: "4242", + PublicHostname: '', + IsRelay: true, + isLighthouse: false, }} validationSchema={yup.object({ })} onSubmit={(values, { setSubmitting, setStatus, setErrors }) => { + if(values.isLighthouse) values.nickname = null; + return API.constellation.addDevice(values).then(({data}) => { setIsDone(data); refreshConfig(); @@ -85,52 +148,55 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { {isDone ?

- Device added successfully! + Device added successfully! Download scan the QR Code from the Cosmos app or download the relevant files to your device along side the config and network certificate to connect:

- -
- -
- {/* - */} + {/* {isDone.isLighthouse ? <> + + + + + : <> */} + +
+ +
+ {/* } */} + - - {/* - - */}
: -

Add a device to the constellation using either the Cosmos or Nebula client

+

Add a Device to the constellation using either the Cosmos or Nebula client

+ + {!formik.values.isLighthouse && { return [u.nickname, u.nickname] }) } - /> + />} { formik={formik} /> + {/* */} + + {formik.values.isLighthouse && <> + + + + + + }
{formik.errors && formik.errors.length > 0 && {formik.errors.map((err) => { @@ -189,7 +276,9 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { setIsDone(null); setOpenModal(true); }} - variant="contained" + variant={ + "contained" + } startIcon={} > Add Device diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index f11d2d3..d4d0518 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -4,14 +4,26 @@ import * as API from "../../api"; import AddDeviceModal from "./addDevice"; import PrettyTableView from "../../components/tableView/prettyTableView"; import { DeleteButton } from "../../components/delete"; -import { CloudOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; +import { CloudOutlined, CloudServerOutlined, CompassOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; import IsLoggedIn from "../../isLoggedIn"; -import { Button, CircularProgress, Stack } from "@mui/material"; -import { CosmosCheckbox, CosmosFormDivider } from "../config/users/formShortcuts"; +import { Alert, Button, CircularProgress, Stack } from "@mui/material"; +import { CosmosCheckbox, CosmosFormDivider, CosmosInputText } from "../config/users/formShortcuts"; import MainCard from "../../components/MainCard"; import { Formik } from "formik"; import { LoadingButton } from "@mui/lab"; import ApiModal from "../../components/apiModal"; +import { isDomain } from "../../utils/indexs"; +import ConfirmModal from "../../components/confirmModal"; +import UploadButtons from "../../components/fileUpload"; + +const getDefaultConstellationHostname = (config) => { + // if domain is set, use it + if(isDomain(config.HTTPConfig.Hostname)) { + return "vpn." + config.HTTPConfig.Hostname; + } else { + return config.HTTPConfig.Hostname; + } +} export const ConstellationIndex = () => { const [isAdmin, setIsAdmin] = useState(false); @@ -41,6 +53,8 @@ export const ConstellationIndex = () => { return } else if (r.deviceName.toLowerCase().includes("tablet")) { return + } else if (r.deviceName.toLowerCase().includes("lighthouse") || r.deviceName.toLowerCase().includes("server")) { + return } else { return } @@ -53,22 +67,30 @@ export const ConstellationIndex = () => {
+ {config.ConstellationConfig.Enabled && config.ConstellationConfig.SlaveMode && <> + + You are currently connected to an external constellation network. Use your main Cosmos server to manage your constellation network and devices. + + } { let newConfig = { ...config }; newConfig.ConstellationConfig.Enabled = values.Enabled; newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay; + newConfig.ConstellationConfig.ConstellationHostname = values.ConstellationHostname; return API.config.set(newConfig); }} > {(formik) => ( - + {formik.values.Enabled && - - - + + + { + await API.constellation.reset(); + refreshConfig(); + }} + /> + } - - + {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <> + {formik.values.Enabled && <> + + This is your Constellation hostname, that you will use to connect. If you are using a domain name, this needs to be different from your server's hostname. Whatever the domain you choose, it is very important that you make sure there is a A entry in your domain DNS pointing to this server. If you change this value, you will need to reset your network and reconnect all the clients! + + } + } + { + let file = e.target.files[0]; + await API.constellation.connect(file); + refreshConfig(); + }} + /> {
+ {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <> r.deviceName} buttons={[ - + , ]} columns={[ { @@ -121,6 +170,10 @@ export const ConstellationIndex = () => { title: 'Owner', field: (r) => {r.nickname}, }, + { + title: 'Type', + field: (r) => {r.isLighthouse ? "Lighthouse" : "Client"}, + }, { title: 'Constellation IP', screenMin: 'md', @@ -137,6 +190,7 @@ export const ConstellationIndex = () => { } ]} /> + }
:
diff --git a/package.json b/package.json index fd46e32..f5bc0e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable14", + "version": "0.10.0-unstable15", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index f37e54a..f7ea61d 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -9,10 +9,18 @@ import ( ) type DeviceCreateRequestJSON struct { - Nickname string `json:"nickname",validate:"required,min=3,max=32,alphanum"` DeviceName string `json:"deviceName",validate:"required,min=3,max=32,alphanum"` IP string `json:"ip",validate:"required,ipv4"` PublicKey string `json:"publicKey",omitempty` + + // for devices only + Nickname string `json:"nickname",validate:"max=32,alphanum",omitempty` + + // for lighthouse only + IsLighthouse bool `json:"isLighthouse",omitempty` + IsRelay bool `json:"isRelay",omitempty` + PublicHostname string `json:"PublicHostname",omitempty` + Port string `json:"port",omitempty` } func DeviceCreate(w http.ResponseWriter, req *http.Request) { @@ -67,11 +75,22 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { return } + if request.IsLighthouse && request.Nickname != "" { + utils.Error("DeviceCreation: Lighthouse cannot belong to a user", nil) + utils.HTTPError(w, "Device Creation Error: Lighthouse cannot have a nickname", + http.StatusInternalServerError, "DC003") + return + } + _, err3 := c.InsertOne(nil, map[string]interface{}{ "Nickname": nickname, "DeviceName": deviceName, "PublicKey": key, "IP": request.IP, + "IsLighthouse": request.IsLighthouse, + "IsRelay": request.IsRelay, + "PublicHostname": request.PublicHostname, + "Port": request.Port, }) if err3 != nil { @@ -88,9 +107,24 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { http.StatusInternalServerError, "DC006") return } + + lightHousesList := []utils.ConstellationDevice{} + if request.IsLighthouse { + lightHousesList, err = GetAllLightHouses() + } // read configYml from config/nebula.yml - configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key) + configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, utils.ConstellationDevice{ + Nickname: nickname, + DeviceName: deviceName, + PublicKey: key, + IP: request.IP, + IsLighthouse: request.IsLighthouse, + IsRelay: request.IsRelay, + PublicHostname: request.PublicHostname, + Port: request.Port, + }) + if err != nil { utils.Error("DeviceCreation: Error while reading config", err) utils.HTTPError(w, "Device Creation Error: " + err.Error(), @@ -108,6 +142,11 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { "IP": request.IP, "Config": configYml, "CA": capki, + "IsLighthouse": request.IsLighthouse, + "IsRelay": request.IsRelay, + "PublicHostname": request.PublicHostname, + "Port": request.Port, + "LighthousesList": lightHousesList, }, }) } else if err2 == nil { diff --git a/src/constellation/api_devices_list.go b/src/constellation/api_devices_list.go index c2efc0f..df8b151 100644 --- a/src/constellation/api_devices_list.go +++ b/src/constellation/api_devices_list.go @@ -29,7 +29,7 @@ func DeviceList(w http.ResponseWriter, req *http.Request) { return } - var devices []utils.Device + var devices []utils.ConstellationDevice // Check if user is an admin if isAdmin { @@ -47,11 +47,6 @@ func DeviceList(w http.ResponseWriter, req *http.Request) { utils.HTTPError(w, "Error decoding devices", http.StatusInternalServerError, "DL002") return } - - // Remove the private key from the response - for i := range devices { - devices[i].PrivateKey = "" - } } else { // If not admin, get user's devices based on their nickname nickname := req.Header.Get("x-cosmos-user") @@ -68,11 +63,6 @@ func DeviceList(w http.ResponseWriter, req *http.Request) { utils.HTTPError(w, "Error decoding devices", http.StatusInternalServerError, "DL004") return } - - // Remove the private key from the response - for i := range devices { - devices[i].PrivateKey = "" - } } // Respond with the list of devices diff --git a/src/constellation/api_nebula.go b/src/constellation/api_nebula.go index 38be4a5..1d67c2f 100644 --- a/src/constellation/api_nebula.go +++ b/src/constellation/api_nebula.go @@ -55,6 +55,26 @@ func API_Restart(w http.ResponseWriter, req *http.Request) { } } +func API_Reset(w http.ResponseWriter, req *http.Request) { + if utils.AdminOnly(w, req) != nil { + return + } + + if(req.Method == "GET") { + ResetNebula() + + utils.Log("Constellation: nebula reset") + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + }) + } else { + utils.Error("SettingGet: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} + func API_GetLogs(w http.ResponseWriter, req *http.Request) { if utils.AdminOnly(w, req) != nil { return diff --git a/src/constellation/api_nebula_connect.go b/src/constellation/api_nebula_connect.go new file mode 100644 index 0000000..8972c74 --- /dev/null +++ b/src/constellation/api_nebula_connect.go @@ -0,0 +1,47 @@ +package constellation + +import ( + "net/http" + "encoding/json" + "io/ioutil" + + + "github.com/azukaar/cosmos-server/src/utils" +) + +func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) { + if utils.AdminOnly(w, req) != nil { + return + } + + if(req.Method == "POST") { + body, err := ioutil.ReadAll(req.Body) + if err != nil { + utils.Error("API_Restart: Invalid User Request", err) + utils.HTTPError(w, "API_Restart Error", + http.StatusInternalServerError, "AR001") + return + } + + config := utils.ReadConfigFromFile() + config.ConstellationConfig.Enabled = true + config.ConstellationConfig.SlaveMode = true + config.ConstellationConfig.DNS = false + // ConstellationHostname = + + // output utils.CONFIGFOLDER + "nebula.yml" + err = ioutil.WriteFile(utils.CONFIGFOLDER + "nebula.yml", body, 0644) + + utils.SetBaseMainConfig(config) + + RestartNebula() + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + }) + } else { + utils.Error("SettingGet: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} diff --git a/src/constellation/index.go b/src/constellation/index.go index 0697166..6f6bbba 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -6,32 +6,36 @@ import ( ) func Init() { + var err error + // if Constellation is enabled if utils.GetMainConfig().ConstellationConfig.Enabled { - InitConfig() - - utils.Log("Initializing Constellation module...") + if !utils.GetMainConfig().ConstellationConfig.SlaveMode { + InitConfig() + + utils.Log("Initializing Constellation module...") - // check if ca.crt exists - if _, err := os.Stat(utils.CONFIGFOLDER + "ca.crt"); os.IsNotExist(err) { - utils.Log("Constellation: ca.crt not found, generating...") - // generate ca.crt - generateNebulaCACert("Cosmos - " + utils.GetMainConfig().HTTPConfig.Hostname) - } + // check if ca.crt exists + if _, err = os.Stat(utils.CONFIGFOLDER + "ca.crt"); os.IsNotExist(err) { + utils.Log("Constellation: ca.crt not found, generating...") + // generate ca.crt + generateNebulaCACert("Cosmos - " + utils.GetMainConfig().ConstellationConfig.ConstellationHostname) + } - // check if cosmos.crt exists - if _, err := os.Stat(utils.CONFIGFOLDER + "cosmos.crt"); os.IsNotExist(err) { - utils.Log("Constellation: cosmos.crt not found, generating...") - // generate cosmos.crt - generateNebulaCert("cosmos", "192.168.201.1/24", "", true) - } + // check if cosmos.crt exists + if _, err := os.Stat(utils.CONFIGFOLDER + "cosmos.crt"); os.IsNotExist(err) { + utils.Log("Constellation: cosmos.crt not found, generating...") + // generate cosmos.crt + generateNebulaCert("cosmos", "192.168.201.1/24", "", true) + } - // export nebula.yml - utils.Log("Constellation: exporting nebula.yml...") - err := ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml") + // export nebula.yml + utils.Log("Constellation: exporting nebula.yml...") + err := ExportConfigToYAML(utils.GetMainConfig().ConstellationConfig, utils.CONFIGFOLDER + "nebula.yml") - if err != nil { - utils.Error("Constellation: error while exporting nebula.yml", err) + if err != nil { + utils.Error("Constellation: error while exporting nebula.yml", err) + } } // start nebula diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index da8d614..7645e57 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -80,18 +80,92 @@ func RestartNebula() { Init() } +func ResetNebula() error { + stop() + utils.Log("Resetting nebula...") + os.RemoveAll(utils.CONFIGFOLDER + "nebula.yml") + os.RemoveAll(utils.CONFIGFOLDER + "ca.crt") + os.RemoveAll(utils.CONFIGFOLDER + "ca.key") + os.RemoveAll(utils.CONFIGFOLDER + "cosmos.crt") + os.RemoveAll(utils.CONFIGFOLDER + "cosmos.key") + // remove everything in db + + c, err := utils.GetCollection(utils.GetRootAppId(), "devices") + if err != nil { + return err + } + + _, err = c.DeleteMany(nil, map[string]interface{}{}) + if err != nil { + return err + } + + Init() + + return nil +} + +func GetAllLightHouses() ([]utils.ConstellationDevice, error) { + c, err := utils.GetCollection(utils.GetRootAppId(), "devices") + if err != nil { + return []utils.ConstellationDevice{}, err + } + + var devices []utils.ConstellationDevice + + cursor, err := c.Find(nil, map[string]interface{}{ + "IsLighthouse": true, + }) + cursor.All(nil, &devices) + + if err != nil { + return []utils.ConstellationDevice{}, err + } + + return devices, nil +} + +func cleanIp(ip string) string { + return strings.Split(ip, "/")[0] +} + func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath string) error { // Combine defaultConfig and overwriteConfig finalConfig := NebulaDefaultConfig finalConfig.StaticHostMap = map[string][]string{ "192.168.201.1": []string{ - utils.GetMainConfig().HTTPConfig.Hostname + ":4242", + utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242", }, } + // for each lighthouse + lh, err := GetAllLightHouses() + if err != nil { + return err + } + + for _, l := range lh { + finalConfig.StaticHostMap[cleanIp(l.IP)] = []string{ + l.PublicHostname + ":" + l.Port, + } + } + + // add other lighthouses + finalConfig.Lighthouse.Hosts = []string{} + for _, l := range lh { + finalConfig.Lighthouse.Hosts = append(finalConfig.Lighthouse.Hosts, cleanIp(l.IP)) + } + finalConfig.Relay.AMRelay = overwriteConfig.NebulaConfig.Relay.AMRelay + finalConfig.Relay.Relays = []string{} + for _, l := range lh { + if l.IsRelay && l.IsLighthouse { + finalConfig.Relay.Relays = append(finalConfig.Relay.Relays, cleanIp(l.IP)) + } + } + // Marshal the combined config to YAML yamlData, err := yaml.Marshal(finalConfig) if err != nil { @@ -118,7 +192,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st return nil } -func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, error) { +func getYAMLClientConfig(name, configPath, capki, cert, key string, device utils.ConstellationDevice) (string, error) { utils.Log("Exporting YAML config for " + name + " with file " + configPath) // Read the YAML config file @@ -134,21 +208,38 @@ func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, err return "", err } + lh, err := GetAllLightHouses() + if err != nil { + return "", err + } + if staticHostMap, ok := configMap["static_host_map"].(map[interface{}]interface{}); ok { staticHostMap["192.168.201.1"] = []string{ - utils.GetMainConfig().HTTPConfig.Hostname + ":4242", + utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242", + } + + for _, l := range lh { + staticHostMap[cleanIp(l.IP)] = []string{ + l.PublicHostname + ":" + l.Port, + } } } else { return "", errors.New("static_host_map not found in nebula.yml") } - // set lightHouse to false + // set lightHouse if lighthouseMap, ok := configMap["lighthouse"].(map[interface{}]interface{}); ok { - lighthouseMap["am_lighthouse"] = false - + lighthouseMap["am_lighthouse"] = device.IsLighthouse + lighthouseMap["hosts"] = []string{ "192.168.201.1", } + + for _, l := range lh { + if cleanIp(l.IP) != cleanIp(device.IP) { + lighthouseMap["hosts"] = append(lighthouseMap["hosts"].([]string), cleanIp(l.IP)) + } + } } else { return "", errors.New("lighthouse not found in nebula.yml") } @@ -162,13 +253,34 @@ func getYAMLClientConfig(name, configPath, capki, cert, key string) (string, err } if relayMap, ok := configMap["relay"].(map[interface{}]interface{}); ok { - relayMap["am_relay"] = false - relayMap["relays"] = []string{"192.168.201.1"} + relayMap["am_relay"] = device.IsRelay && device.IsLighthouse + relayMap["relays"] = []string{} + if utils.GetMainConfig().ConstellationConfig.NebulaConfig.Relay.AMRelay { + relayMap["relays"] = append(relayMap["relays"].([]string), "192.168.201.1") + } + + for _, l := range lh { + if l.IsRelay && l.IsLighthouse && cleanIp(l.IP) != cleanIp(device.IP) { + relayMap["relays"] = append(relayMap["relays"].([]string), cleanIp(l.IP)) + } + } } else { return "", errors.New("relay not found in nebula.yml") } + + if listen, ok := configMap["listen"].(map[interface{}]interface{}); ok { + if device.Port != "" { + listen["port"] = device.Port + } else { + listen["port"] = "4242" + } + } else { + return "", errors.New("listen not found in nebula.yml") + } configMap["deviceName"] = name + configMap["local_dns_overwrite"] = "192.168.201.1" + configMap["public_hostname"] = device.PublicHostname // export configMap as YML yamlData, err = yaml.Marshal(configMap) diff --git a/src/httpServer.go b/src/httpServer.go index 76c2fc9..845fd66 100644 --- a/src/httpServer.go +++ b/src/httpServer.go @@ -334,6 +334,8 @@ func InitServer() *mux.Router { srapi.HandleFunc("/api/constellation/devices", constellation.ConstellationAPIDevices) srapi.HandleFunc("/api/constellation/restart", constellation.API_Restart) + srapi.HandleFunc("/api/constellation/reset", constellation.API_Reset) + srapi.HandleFunc("/api/constellation/connect", constellation.API_ConnectToExisting) srapi.HandleFunc("/api/constellation/config", constellation.API_GetConfig) srapi.HandleFunc("/api/constellation/logs", constellation.API_GetLogs) diff --git a/src/proxy/routeTo.go b/src/proxy/routeTo.go index e139507..cf98a5f 100644 --- a/src/proxy/routeTo.go +++ b/src/proxy/routeTo.go @@ -46,7 +46,7 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) { // NewProxy takes target host and creates a reverse proxy -func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardHeader bool, DisableHeaderHardening bool, CORSOrigin string) (*httputil.ReverseProxy, error) { +func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardHeader bool, DisableHeaderHardening bool, CORSOrigin string, route utils.ProxyRouteConfig) (*httputil.ReverseProxy, error) { url, err := url.Parse(targetHost) if err != nil { return nil, err @@ -76,15 +76,28 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH req.Header.Set("X-Forwarded-Ssl", "on") } - if CORSOrigin != "" { - req.Header.Set("X-Forwarded-Host", url.Host) + req.Header.Del("X-Origin-Host") + req.Header.Del("X-Forwarded-Host") + req.Header.Del("X-Forwarded-For") + req.Header.Del("X-Real-Ip") + req.Header.Del("Host") + + hostname := utils.GetMainConfig().HTTPConfig.Hostname + if route.Host != "" && route.UseHost { + hostname = route.Host + } + if route.UsePathPrefix { + hostname = hostname + route.PathPrefix } if VerboseForwardHeader { - req.Header.Set("X-Origin-Host", url.Host) - req.Header.Set("Host", url.Host) + req.Header.Set("X-Origin-Host", hostname) + req.Header.Set("Host", hostname) + req.Header.Set("X-Forwarded-Host", hostname) req.Header.Set("X-Forwarded-For", utils.GetClientIP(req)) req.Header.Set("X-Real-IP", utils.GetClientIP(req)) + } else { + req.Host = url.Host } } @@ -100,8 +113,6 @@ func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool, VerboseForwardH if CORSOrigin != "" { resp.Header.Del("Access-Control-Allow-Origin") - resp.Header.Del("Access-Control-Allow-Methods") - resp.Header.Del("Access-Control-Allow-Headers") resp.Header.Del("Access-Control-Allow-Credentials") } @@ -126,7 +137,7 @@ func RouteTo(route utils.ProxyRouteConfig) http.Handler { routeType := route.Mode if(routeType == "SERVAPP" || routeType == "PROXY") { - proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin) + proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget, route.VerboseForwardHeader, route.DisableHeaderHardening, route.CORSOrigin, route) if err != nil { utils.Error("Create Route", err) } diff --git a/src/proxy/routerGen.go b/src/proxy/routerGen.go index f4aef7c..8dd32ae 100644 --- a/src/proxy/routerGen.go +++ b/src/proxy/routerGen.go @@ -30,12 +30,12 @@ func tokenMiddleware(enabled bool, adminOnly bool) func(next http.Handler) http. r.Header.Set("x-cosmos-mfa", strconv.Itoa((int)(u.MFAState))) ogcookies := r.Header.Get("Cookie") - cookieRemoveRegex := regexp.MustCompile(`jwttoken=[^;]*;`) + cookieRemoveRegex := regexp.MustCompile(`\s?jwttoken=[^;]*;?\s?`) cookies := cookieRemoveRegex.ReplaceAllString(ogcookies, "") r.Header.Set("Cookie", cookies) // Replace the token with a application speicfic one - r.Header.Set("x-cosmos-token", "1234567890") + //r.Header.Set("x-cosmos-token", "1234567890") if enabled && adminOnly { if errT := utils.AdminOnlyWithRedirect(w, r); errT != nil { diff --git a/src/utils/middleware.go b/src/utils/middleware.go index e06bf40..b2ff755 100644 --- a/src/utils/middleware.go +++ b/src/utils/middleware.go @@ -82,8 +82,6 @@ func CORSHeader(origin string) func(next http.Handler) http.Handler { if origin != "" { w.Header().Set("Access-Control-Allow-Origin", origin) - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") w.Header().Set("Access-Control-Allow-Credentials", "true") } diff --git a/src/utils/types.go b/src/utils/types.go index 8310cba..8d4460a 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -211,6 +211,7 @@ type MarketSource struct { type ConstellationConfig struct { Enabled bool + SlaveMode bool DNS bool DNSPort string DNSFallback string @@ -218,6 +219,18 @@ type ConstellationConfig struct { DNSAdditionalBlocklists []string CustomDNSEntries map[string]string NebulaConfig NebulaConfig + ConstellationHostname string +} + +type ConstellationDevice struct { + Nickname string `json:"nickname"` + DeviceName string `json:"deviceName"` + PublicKey string `json:"publicKey"` + IP string `json:"ip"` + IsLighthouse bool `json:"isLighthouse"` + IsRelay bool `json:"isRelay"` + PublicHostname string `json:"publicHostname"` + Port string `json:"port"` } type NebulaFirewallRule struct { diff --git a/src/utils/utils.go b/src/utils/utils.go index f131fa2..1a32bed 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -214,6 +214,15 @@ func LoadBaseMainConfig(config Config) { if MainConfig.DockerConfig.DefaultDataPath == "" { MainConfig.DockerConfig.DefaultDataPath = "/usr" } + + if MainConfig.ConstellationConfig.ConstellationHostname == "" { + // if hostname is a domain add vpn. suffix otherwise use hostname + if IsDomain(MainConfig.HTTPConfig.Hostname) { + MainConfig.ConstellationConfig.ConstellationHostname = "vpn." + MainConfig.HTTPConfig.Hostname + } else { + MainConfig.ConstellationConfig.ConstellationHostname = MainConfig.HTTPConfig.Hostname + } + } } func GetMainConfig() Config { @@ -577,4 +586,12 @@ func GetClientIP(req *http.Request) string { ip = req.RemoteAddr }*/ return req.RemoteAddr +} + +func IsDomain(domain string) bool { + // contains . and at least a letter and no special characters invalid in a domain + if strings.Contains(domain, ".") && strings.ContainsAny(domain, "abcdefghijklmnopqrstuvwxyz") && !strings.ContainsAny(domain, " !@#$%^&*()+=[]{}\\|;:'\",/<>?") { + return true + } + return false } \ No newline at end of file From 9b033696e35932ce60d17f4b26092fcd70b892f6 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Mon, 2 Oct 2023 16:22:18 +0100 Subject: [PATCH 20/36] [release] v0.10.0-unstable16 --- client/src/api/constellation.tsx | 13 + .../src/pages/config/users/formShortcuts.jsx | 21 +- client/src/pages/constellation/dns.jsx | 174 +++++++++++++ client/src/pages/constellation/index.jsx | 228 +++--------------- client/src/pages/constellation/vpn.jsx | 205 ++++++++++++++++ client/src/routes/MainRoutes.jsx | 2 +- go.mod | 27 ++- go.sum | 48 +++- package.json | 2 +- readme.md | 2 +- src/constellation/DNS.go | 5 +- src/constellation/api_devices_block.go | 99 ++++++++ src/constellation/api_devices_create.go | 13 +- src/constellation/api_nebula.go | 10 +- src/constellation/nebula.go | 123 ++++++++-- src/constellation/nebula_default.go | 2 + src/httpServer.go | 1 + src/utils/types.go | 11 +- 18 files changed, 738 insertions(+), 248 deletions(-) create mode 100644 client/src/pages/constellation/dns.jsx create mode 100644 client/src/pages/constellation/vpn.jsx create mode 100644 src/constellation/api_devices_block.go diff --git a/client/src/api/constellation.tsx b/client/src/api/constellation.tsx index 375aabe..261362d 100644 --- a/client/src/api/constellation.tsx +++ b/client/src/api/constellation.tsx @@ -66,6 +66,18 @@ function connect(file) { })) } +function block(nickname, devicename, block) { + return wrap(fetch(`/cosmos/api/constellation/block`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + nickname, devicename, block + }), + })) +} + export { list, addDevice, @@ -74,4 +86,5 @@ export { getLogs, reset, connect, + block, }; \ No newline at end of file diff --git a/client/src/pages/config/users/formShortcuts.jsx b/client/src/pages/config/users/formShortcuts.jsx index 850bf8e..4419ebb 100644 --- a/client/src/pages/config/users/formShortcuts.jsx +++ b/client/src/pages/config/users/formShortcuts.jsx @@ -27,26 +27,33 @@ import { strengthColor, strengthIndicator } from '../../../utils/password-streng import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'; -export const CosmosInputText = ({ name, style, multiline, type, placeholder, onChange, label, formik }) => { +export const CosmosInputText = ({ name, style, value, errors, multiline, type, placeholder, onChange, label, formik }) => { return - {label} + {label && {label}} { + return formik && formik.handleBlur(...ar); + }} onChange={(...ar) => { onChange && onChange(...ar); - return formik.handleChange(...ar); + return formik && formik.handleChange(...ar); }} placeholder={placeholder} fullWidth - error={Boolean(formik.touched[name] && formik.errors[name])} + error={Boolean(formik && formik.touched[name] && formik.errors[name])} /> - {formik.touched[name] && formik.errors[name] && ( + {formik && formik.touched[name] && formik.errors[name] && ( + + {formik.errors[name]} + + )} + {errors && ( {formik.errors[name]} diff --git a/client/src/pages/constellation/dns.jsx b/client/src/pages/constellation/dns.jsx new file mode 100644 index 0000000..6e39761 --- /dev/null +++ b/client/src/pages/constellation/dns.jsx @@ -0,0 +1,174 @@ +import React from "react"; +import { useEffect, useState } from "react"; +import * as API from "../../api"; +import AddDeviceModal from "./addDevice"; +import PrettyTableView from "../../components/tableView/prettyTableView"; +import { DeleteButton } from "../../components/delete"; +import { CloudOutlined, CloudServerOutlined, CompassOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; +import IsLoggedIn from "../../isLoggedIn"; +import { Alert, Button, CircularProgress, InputLabel, Stack } from "@mui/material"; +import { CosmosCheckbox, CosmosFormDivider, CosmosInputText } from "../config/users/formShortcuts"; +import MainCard from "../../components/MainCard"; +import { Formik } from "formik"; +import { LoadingButton } from "@mui/lab"; +import ApiModal from "../../components/apiModal"; +import { isDomain } from "../../utils/indexs"; +import ConfirmModal from "../../components/confirmModal"; +import UploadButtons from "../../components/fileUpload"; + +export const ConstellationDNS = () => { + const [isAdmin, setIsAdmin] = useState(false); + const [config, setConfig] = useState(null); + + const refreshConfig = async () => { + let configAsync = await API.config.get(); + setConfig(configAsync.data); + setIsAdmin(configAsync.isAdmin); + }; + + useEffect(() => { + refreshConfig(); + }, []); + + return <> + + {(config) ? <> + +
+ + + + { + let newConfig = { ...config }; + newConfig.ConstellationConfig.DNS = values.Enabled; + newConfig.ConstellationConfig.DNSPort = values.Port; + newConfig.ConstellationConfig.DNSFallback = values.Fallback; + newConfig.ConstellationConfig.DNSBlockBlacklist = values.DNSBlockBlacklist; + newConfig.ConstellationConfig.DNSAdditionalBlocklists = values.DNSAdditionalBlocklists; + newConfig.ConstellationConfig.CustomDNSEntries = values.CustomDNSEntries; + + return API.config.set(newConfig); + }} + > + {(formik) => ( + + + + + + + + + + + DNS Blocklist URLs + {formik.values.DNSAdditionalBlocklists.map((item, index) => ( + + { + formik.setFieldValue("DNSAdditionalBlocklists", [...formik.values.DNSAdditionalBlocklists.slice(0, index), ...formik.values.DNSAdditionalBlocklists.slice(index + 1)]); + }} /> +
+ { + formik.setFieldValue("DNSAdditionalBlocklists", [...formik.values.DNSAdditionalBlocklists.slice(0, index), e.target.value, ...formik.values.DNSAdditionalBlocklists.slice(index + 1)]); + }} + /> +
+
+ ))} + + + + + + + + DNS Custom Entries + {formik.values.CustomDNSEntries.map((item, index) => ( + + { + formik.setFieldValue("CustomDNSEntries", [...formik.values.CustomDNSEntries.slice(0, index), ...formik.values.CustomDNSEntries.slice(index + 1)]); + }} /> +
+ { + const updatedCustomDNSEntries = [...formik.values.CustomDNSEntries]; + updatedCustomDNSEntries[index].Key = e.target.value; + formik.setFieldValue("CustomDNSEntries", updatedCustomDNSEntries); + }} + /> +
+
+ { + const updatedCustomDNSEntries = [...formik.values.CustomDNSEntries]; + updatedCustomDNSEntries[index].Value = e.target.value; + formik.setFieldValue("CustomDNSEntries", updatedCustomDNSEntries); + }} + /> +
+
+ ))} + + + + + + + Save + +
+ + )} +
+
+
+
+
+ :
+ +
} + +}; \ No newline at end of file diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index d4d0518..dea63ec 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -1,199 +1,35 @@ -import React from "react"; -import { useEffect, useState } from "react"; -import * as API from "../../api"; -import AddDeviceModal from "./addDevice"; -import PrettyTableView from "../../components/tableView/prettyTableView"; -import { DeleteButton } from "../../components/delete"; -import { CloudOutlined, CloudServerOutlined, CompassOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; -import IsLoggedIn from "../../isLoggedIn"; -import { Alert, Button, CircularProgress, Stack } from "@mui/material"; -import { CosmosCheckbox, CosmosFormDivider, CosmosInputText } from "../config/users/formShortcuts"; -import MainCard from "../../components/MainCard"; -import { Formik } from "formik"; -import { LoadingButton } from "@mui/lab"; -import ApiModal from "../../components/apiModal"; -import { isDomain } from "../../utils/indexs"; -import ConfirmModal from "../../components/confirmModal"; -import UploadButtons from "../../components/fileUpload"; +import * as React from 'react'; +import MainCard from '../../components/MainCard'; +import { Chip, Divider, Stack, useMediaQuery } from '@mui/material'; +import HostChip from '../../components/hostChip'; +import { RouteMode, RouteSecurity } from '../../components/routeComponents'; +import { getFaviconURL } from '../../utils/routes'; +import * as API from '../../api'; +import { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons"; +import IsLoggedIn from '../../isLoggedIn'; +import PrettyTabbedView from '../../components/tabbedView/tabbedView'; -const getDefaultConstellationHostname = (config) => { - // if domain is set, use it - if(isDomain(config.HTTPConfig.Hostname)) { - return "vpn." + config.HTTPConfig.Hostname; - } else { - return config.HTTPConfig.Hostname; - } +import { ConstellationVPN } from './vpn'; +import { ConstellationDNS } from './dns'; + +const ConstellationIndex = () => { + return
+ + + , + path: 'vpn' + }, + { + title: 'DNS', + children: , + path: 'dns' + }, + ]}/> + +
; } -export const ConstellationIndex = () => { - const [isAdmin, setIsAdmin] = useState(false); - const [config, setConfig] = useState(null); - const [users, setUsers] = useState(null); - const [devices, setDevices] = useState(null); - - const refreshConfig = async () => { - let configAsync = await API.config.get(); - setConfig(configAsync.data); - setIsAdmin(configAsync.isAdmin); - setDevices((await API.constellation.list()).data || []); - setUsers((await API.users.list()).data || []); - }; - - useEffect(() => { - refreshConfig(); - }, []); - - const getIcon = (r) => { - if (r.deviceName.toLowerCase().includes("mobile") || r.deviceName.toLowerCase().includes("phone")) { - return - } - else if (r.deviceName.toLowerCase().includes("laptop") || r.deviceName.toLowerCase().includes("computer")) { - return - } else if (r.deviceName.toLowerCase().includes("desktop")) { - return - } else if (r.deviceName.toLowerCase().includes("tablet")) { - return - } else if (r.deviceName.toLowerCase().includes("lighthouse") || r.deviceName.toLowerCase().includes("server")) { - return - } else { - return - } - } - - return <> - - {(devices && config && users) ? <> - -
- - - {config.ConstellationConfig.Enabled && config.ConstellationConfig.SlaveMode && <> - - You are currently connected to an external constellation network. Use your main Cosmos server to manage your constellation network and devices. - - } - { - let newConfig = { ...config }; - newConfig.ConstellationConfig.Enabled = values.Enabled; - newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay; - newConfig.ConstellationConfig.ConstellationHostname = values.ConstellationHostname; - return API.config.set(newConfig); - }} - > - {(formik) => ( -
- - {formik.values.Enabled && - - - - { - await API.constellation.reset(); - refreshConfig(); - }} - /> - } - - {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <> - {formik.values.Enabled && <> - - This is your Constellation hostname, that you will use to connect. If you are using a domain name, this needs to be different from your server's hostname. Whatever the domain you choose, it is very important that you make sure there is a A entry in your domain DNS pointing to this server. If you change this value, you will need to reset your network and reconnect all the clients! - - } - } - { - let file = e.target.files[0]; - await API.constellation.connect(file); - refreshConfig(); - }} - /> - - Save - - -
- )} -
-
-
-
- {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <> - - r.deviceName} - buttons={[ - , - ]} - columns={[ - { - title: '', - field: getIcon, - }, - { - title: 'Device Name', - field: (r) => {r.deviceName}, - }, - { - title: 'Owner', - field: (r) => {r.nickname}, - }, - { - title: 'Type', - field: (r) => {r.isLighthouse ? "Lighthouse" : "Client"}, - }, - { - title: 'Constellation IP', - screenMin: 'md', - field: (r) => r.ip, - }, - { - title: '', - clickable: true, - field: (r) => { - return { - alert("caca") - }}> - } - } - ]} - /> - } -
- :
- -
} - -}; \ No newline at end of file +export default ConstellationIndex; \ No newline at end of file diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx new file mode 100644 index 0000000..65e7b9f --- /dev/null +++ b/client/src/pages/constellation/vpn.jsx @@ -0,0 +1,205 @@ +import React from "react"; +import { useEffect, useState } from "react"; +import * as API from "../../api"; +import AddDeviceModal from "./addDevice"; +import PrettyTableView from "../../components/tableView/prettyTableView"; +import { DeleteButton } from "../../components/delete"; +import { CloudOutlined, CloudServerOutlined, CompassOutlined, DesktopOutlined, LaptopOutlined, MobileOutlined, TabletOutlined } from "@ant-design/icons"; +import IsLoggedIn from "../../isLoggedIn"; +import { Alert, Button, CircularProgress, Stack } from "@mui/material"; +import { CosmosCheckbox, CosmosFormDivider, CosmosInputText } from "../config/users/formShortcuts"; +import MainCard from "../../components/MainCard"; +import { Formik } from "formik"; +import { LoadingButton } from "@mui/lab"; +import ApiModal from "../../components/apiModal"; +import { isDomain } from "../../utils/indexs"; +import ConfirmModal from "../../components/confirmModal"; +import UploadButtons from "../../components/fileUpload"; + +const getDefaultConstellationHostname = (config) => { + // if domain is set, use it + if(isDomain(config.HTTPConfig.Hostname)) { + return "vpn." + config.HTTPConfig.Hostname; + } else { + return config.HTTPConfig.Hostname; + } +} + +export const ConstellationVPN = () => { + const [isAdmin, setIsAdmin] = useState(false); + const [config, setConfig] = useState(null); + const [users, setUsers] = useState(null); + const [devices, setDevices] = useState(null); + + const refreshConfig = async () => { + let configAsync = await API.config.get(); + setConfig(configAsync.data); + setIsAdmin(configAsync.isAdmin); + setDevices((await API.constellation.list()).data || []); + setUsers((await API.users.list()).data || []); + }; + + useEffect(() => { + refreshConfig(); + }, []); + + const getIcon = (r) => { + if (r.deviceName.toLowerCase().includes("mobile") || r.deviceName.toLowerCase().includes("phone")) { + return + } + else if (r.deviceName.toLowerCase().includes("laptop") || r.deviceName.toLowerCase().includes("computer")) { + return + } else if (r.deviceName.toLowerCase().includes("desktop")) { + return + } else if (r.deviceName.toLowerCase().includes("tablet")) { + return + } else if (r.deviceName.toLowerCase().includes("lighthouse") || r.deviceName.toLowerCase().includes("server")) { + return + } else { + return + } + } + + return <> + + {(devices && config && users) ? <> + +
+ + + {config.ConstellationConfig.Enabled && config.ConstellationConfig.SlaveMode && <> + + You are currently connected to an external constellation network. Use your main Cosmos server to manage your constellation network and devices. + + } + { + let newConfig = { ...config }; + newConfig.ConstellationConfig.Enabled = values.Enabled; + newConfig.ConstellationConfig.PrivateNode = values.PrivateNode; + newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay; + newConfig.ConstellationConfig.ConstellationHostname = values.ConstellationHostname; + return API.config.set(newConfig); + }} + > + {(formik) => ( +
+ + {formik.values.Enabled && + + + + { + await API.constellation.reset(); + refreshConfig(); + }} + /> + } + + {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <> + {formik.values.Enabled && <> + + + {!formik.values.PrivateNode && <> + This is your Constellation hostname, that you will use to connect. If you are using a domain name, this needs to be different from your server's hostname. Whatever the domain you choose, it is very important that you make sure there is a A entry in your domain DNS pointing to this server. If you change this value, you will need to reset your network and reconnect all the clients! + + } + } + } + + Save + + { + let file = e.target.files[0]; + await API.constellation.connect(file); + refreshConfig(); + }} + /> + +
+ )} +
+
+
+
+ {config.ConstellationConfig.Enabled && !config.ConstellationConfig.SlaveMode && <> + + !d.blocked)} + getKey={(r) => r.deviceName} + buttons={[ + , + ]} + columns={[ + { + title: '', + field: getIcon, + }, + { + title: 'Device Name', + field: (r) => {r.deviceName}, + }, + { + title: 'Owner', + field: (r) => {r.nickname}, + }, + { + title: 'Type', + field: (r) => {r.isLighthouse ? "Lighthouse" : "Client"}, + }, + { + title: 'Constellation IP', + screenMin: 'md', + field: (r) => r.ip, + }, + { + title: '', + clickable: true, + field: (r) => { + return { + await API.constellation.block(r.nickname, r.deviceName, true); + refreshConfig(); + }}> + } + } + ]} + /> + } +
+ :
+ +
} + +}; \ No newline at end of file diff --git a/client/src/routes/MainRoutes.jsx b/client/src/routes/MainRoutes.jsx index 5c67c26..84b7f49 100644 --- a/client/src/routes/MainRoutes.jsx +++ b/client/src/routes/MainRoutes.jsx @@ -15,7 +15,7 @@ import ContainerIndex from '../pages/servapps/containers'; import NewDockerServiceForm from '../pages/servapps/containers/newServiceForm'; import OpenIdList from '../pages/openid/openid-list'; import MarketPage from '../pages/market/listing'; -import { ConstellationIndex } from '../pages/constellation'; +import ConstellationIndex from '../pages/constellation'; // render - dashboard diff --git a/go.mod b/go.mod index 4e40dd9..7c1de41 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/docker/docker v23.0.3+incompatible github.com/docker/go-connections v0.4.0 github.com/foomo/tlsconfig v0.0.0-20180418120404-b67861b076c9 - github.com/go-acme/lego/v4 v4.13.3 + github.com/go-acme/lego/v4 v4.14.2 github.com/go-chi/chi v4.0.2+incompatible github.com/go-chi/httprate v0.7.1 github.com/go-playground/validator/v10 v10.14.0 @@ -16,7 +16,9 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/jasonlvhit/gocron v0.0.1 + github.com/miekg/dns v1.1.55 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f + github.com/natefinch/lumberjack v2.0.0+incompatible github.com/ory/fosite v0.44.0 github.com/oschwald/geoip2-golang v1.8.0 github.com/pquerna/otp v1.4.0 @@ -27,6 +29,7 @@ require ( golang.org/x/crypto v0.10.0 golang.org/x/net v0.11.0 golang.org/x/sys v0.9.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -57,7 +60,20 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/andybalholm/cascadia v1.1.0 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect - github.com/aws/aws-sdk-go v1.39.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.19.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.18.28 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29 // indirect + github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 // indirect + github.com/aws/smithy-go v1.13.5 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -126,7 +142,6 @@ require ( github.com/magiconair/properties v1.8.4 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/goveralls v0.0.6 // indirect - github.com/miekg/dns v1.1.55 // indirect github.com/mimuret/golang-iij-dpf v0.9.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -137,6 +152,7 @@ require ( github.com/montanaflynn/stats v0.7.0 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect github.com/nrdcg/auroradns v1.1.0 // indirect + github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9 // indirect github.com/nrdcg/desec v0.7.0 // indirect github.com/nrdcg/dnspod-go v0.4.0 // indirect github.com/nrdcg/freemyip v0.2.0 // indirect @@ -153,7 +169,7 @@ require ( github.com/ory/viper v1.7.5 // indirect github.com/ory/x v0.0.214 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect - github.com/ovh/go-ovh v1.4.1 // indirect + github.com/ovh/go-ovh v1.4.2 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pborman/uuid v1.2.0 // indirect github.com/pelletier/go-toml v1.8.1 // indirect @@ -167,7 +183,6 @@ require ( github.com/sacloud/packages-go v0.0.9 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/softlayer/softlayer-go v1.1.2 // indirect @@ -210,9 +225,9 @@ require ( google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/ns1/ns1-go.v2 v2.7.6 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect ) diff --git a/go.sum b/go.sum index ae94432..39b642d 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,7 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v4.0.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -107,9 +108,35 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= -github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k= +github.com/aws/aws-sdk-go-v2 v1.19.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.28 h1:TINEaKyh1Td64tqFvn09iYpKiWjmHYrG1fa91q2gnqw= +github.com/aws/aws-sdk-go-v2/config v1.18.28/go.mod h1:nIL+4/8JdAuNHEjn/gPEXqtnS02Q3NXB/9Z7o5xE4+A= +github.com/aws/aws-sdk-go-v2/credentials v1.13.27 h1:dz0yr/yR1jweAnsCx+BmjerUILVPQ6FS5AwF/OyG1kA= +github.com/aws/aws-sdk-go-v2/credentials v1.13.27/go.mod h1:syOqAek45ZXZp29HlnRS/BNgMIW6uiRmeuQsz4Qh2UE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 h1:kP3Me6Fy3vdi+9uHd7YLr6ewPxRL+PU6y15urfTaamU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5/go.mod h1:Gj7tm95r+QsDoN2Fhuz/3npQvcZbkEf5mL70n3Xfluc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 h1:hMUCiE3Zi5AHrRNGf5j985u0WyqI6r2NULhUfo0N/No= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 h1:yOpYx+FTBdpk/g+sBU6Cb1H0U/TLEcYYp66mYqsPpcc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29/go.mod h1:M/eUABlDbw2uVrdAn+UsI6M727qp2fxkp8K0ejcBDUY= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 h1:8r5m1BoAWkn0TDC34lUculryf7nUF25EgIMdjvGCkgo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36/go.mod h1:Rmw2M1hMVTwiUhjwMoIBFWFJMhvJbct06sSidxInkhY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29 h1:IiDolu/eLmuB18DRZibj77n1hHQT7z12jnGO7Ze3pLc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29/go.mod h1:fDbkK4o7fpPXWn8YAPmTieAMuB9mk/VgvW64uaUqxd4= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2 h1:PwNeYoonBzmTdCztKiiutws3U24KrnDBuabzRfIlZY4= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2/go.mod h1:gQhLZrTEath4zik5ixIe6axvgY5jJrgSBDJ360Fxnco= +github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4 h1:p4mTxJfCAyiTT4Wp6p/mOPa6j5MqCSRGot8qZwFs+Z0= +github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4/go.mod h1:VBLWpaHvhQNeu7N9rMEf00SWeOONb/HvaDUxe/7b44k= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.13 h1:sWDv7cMITPcZ21QdreULwxOOAmE05JjEsT6fCDtDA9k= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.13/go.mod h1:DfX0sWuT46KpcqbMhJ9QWtxAIP1VozkDWf8VAkByjYY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13 h1:BFubHS/xN5bjl818QaroN6mQdjneYQ+AOx44KNXlyH4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13/go.mod h1:BzqsVVFduubEmzrVtUFQQIQdFqvUItF8XUq2EnS8Wog= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 h1:e5mnydVdCVWxP+5rPAGi2PYxC7u2OZgH1ypC114H04U= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.3/go.mod h1:yVGZA1CPkmUhBdA039jXNJJG7/6t+G+EBWmFq23xqnY= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -259,8 +286,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-acme/lego/v4 v4.13.3 h1:aZ1S9FXIkCWG3Uw/rZKSD+MOuO8ZB1t6p9VCg6jJiNY= -github.com/go-acme/lego/v4 v4.13.3/go.mod h1:c/iodVGMeBXG/+KiQczoNkySo3YLWTVa0kiyeVd/FHc= +github.com/go-acme/lego/v4 v4.14.2 h1:/D/jqRgLi8Cbk33sLGtu2pX2jEg3bGJWHyV8kFuUHGM= +github.com/go-acme/lego/v4 v4.14.2/go.mod h1:kBXxbeTg0x9AgaOYjPSwIeJy3Y33zTz+tMD16O4MO6c= github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= @@ -672,6 +699,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= @@ -1024,11 +1052,15 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo= github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk= +github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9 h1:qpB3wZR4+MPK92cTC9zZPnndkJgDgPvQqPUAgVc1NXU= +github.com/nrdcg/bunny-go v0.0.0-20230728143221-c9dda82568d9/go.mod h1:HUoHXDrFvidN1NK9Wb/mZKNOfDNutKkzF2Pg71M9hHA= github.com/nrdcg/desec v0.7.0 h1:iuGhi4pstF3+vJWwt292Oqe2+AsSPKDynQna/eu1fDs= github.com/nrdcg/desec v0.7.0/go.mod h1:e1uRqqKv1mJdd5+SQROAhmy75lKMphLzWIuASLkpeFY= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= @@ -1124,8 +1156,8 @@ github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6 github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= -github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= -github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= +github.com/ovh/go-ovh v1.4.2 h1:ub4jVK6ERbiBTo4y5wbLCjeKCjGY+K36e7BviW+MaAU= +github.com/ovh/go-ovh v1.4.2/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -1241,8 +1273,6 @@ github.com/shurcooL/highlight_go v0.0.0-20170515013102-78fb10f4a5f8/go.mod h1:UD github.com/shurcooL/octicon v0.0.0-20180602230221-c42b0e3b24d9/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 h1:ZTzdx88+AcnjqUfJwnz89UBrMSBQ1NEysg9u5d+dU9c= -github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A= @@ -1877,6 +1907,8 @@ gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/ns1/ns1-go.v2 v2.7.6 h1:mCPl7q0jbIGACXvGBljAuuApmKZo3rRi4tlRIEbMvjA= gopkg.in/ns1/ns1-go.v2 v2.7.6/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= diff --git a/package.json b/package.json index f5bc0e2..6a39eec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable15", + "version": "0.10.0-unstable16", "description": "", "main": "test-server.js", "bugs": { diff --git a/readme.md b/readme.md index 8d00e7d..ad32c52 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ null Clayton Stone null -Adrian Germeck +Adrian

--- diff --git a/src/constellation/DNS.go b/src/constellation/DNS.go index 1f303ae..c7f23ac 100644 --- a/src/constellation/DNS.go +++ b/src/constellation/DNS.go @@ -46,7 +46,10 @@ func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { // Overwrite local hostnames with custom entries for _, q := range r.Question { - for hostname, ip := range customDNSEntries { + for _, entry := range customDNSEntries { + hostname := entry.Key + ip := entry.Value + if strings.HasSuffix(q.Name, hostname + ".") && q.Qtype == dns.TypeA { utils.Debug("DNS Overwrite " + hostname + " with " + ip) rr, _ := dns.NewRR(q.Name + " A " + ip) diff --git a/src/constellation/api_devices_block.go b/src/constellation/api_devices_block.go new file mode 100644 index 0000000..9465c76 --- /dev/null +++ b/src/constellation/api_devices_block.go @@ -0,0 +1,99 @@ +package constellation + +import ( + "net/http" + "encoding/json" + + "github.com/azukaar/cosmos-server/src/utils" +) + +type DeviceBlockRequestJSON struct { + Nickname string `json:"nickname",validate:"required,min=3,max=32,alphanum"` + DeviceName string `json:"deviceName",validate:"required,min=3,max=32,alphanum"` + Block bool `json:"block",omitempty` +} + +func DeviceBlock(w http.ResponseWriter, req *http.Request) { + if(req.Method == "POST") { + var request DeviceBlockRequestJSON + err1 := json.NewDecoder(req.Body).Decode(&request) + if err1 != nil { + utils.Error("ConstellationDeviceBlocking: Invalid User Request", err1) + utils.HTTPError(w, "Device Creation Error", + http.StatusInternalServerError, "DB001") + return + } + + errV := utils.Validate.Struct(request) + if errV != nil { + utils.Error("DeviceBlocking: Invalid User Request", errV) + utils.HTTPError(w, "Device Creation Error: " + errV.Error(), + http.StatusInternalServerError, "DB002") + return + } + + nickname := utils.Sanitize(request.Nickname) + deviceName := utils.Sanitize(request.DeviceName) + + if utils.AdminOrItselfOnly(w, req, nickname) != nil { + return + } + + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") + if errCo != nil { + utils.Error("Database Connect", errCo) + utils.HTTPError(w, "Database", http.StatusInternalServerError, "DB001") + return + } + + device := utils.Device{} + + utils.Debug("ConstellationDeviceBlocking: Blocking Device " + deviceName) + + err2 := c.FindOne(nil, map[string]interface{}{ + "DeviceName": deviceName, + "Nickname": nickname, + "Blocked": false, + }).Decode(&device) + + if err2 == nil { + utils.Debug("ConstellationDeviceBlocking: Found Device " + deviceName) + + _, err3 := c.UpdateOne(nil, map[string]interface{}{ + "DeviceName": deviceName, + "Nickname": nickname, + }, map[string]interface{}{ + "$set": map[string]interface{}{ + "Blocked": request.Block, + }, + }) + + if err3 != nil { + utils.Error("DeviceBlocking: Error while updating device", err3) + utils.HTTPError(w, "Device Creation Error: " + err3.Error(), + http.StatusInternalServerError, "DB001") + return + } + + if request.Block { + utils.Log("ConstellationDeviceBlocking: Device " + deviceName + " blocked") + } else { + utils.Log("ConstellationDeviceBlocking: Device " + deviceName + " unblocked") + } + } else { + utils.Error("DeviceBlocking: Error while finding device", err2) + utils.HTTPError(w, "Device Creation Error: " + err2.Error(), + http.StatusInternalServerError, "DB001") + return + } + + + json.NewEncoder(w).Encode(map[string]interface{}{ + "status": "OK", + }) + } else { + utils.Error("DeviceBlocking: Method not allowed" + req.Method, nil) + utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") + return + } +} \ No newline at end of file diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index f7ea61d..bc81754 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -4,7 +4,7 @@ import ( "net/http" "encoding/json" "go.mongodb.org/mongo-driver/mongo" - + "github.com/azukaar/cosmos-server/src/utils" ) @@ -62,11 +62,12 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { err2 := c.FindOne(nil, map[string]interface{}{ "DeviceName": deviceName, + "Blocked": false, }).Decode(&device) if err2 == mongo.ErrNoDocuments { - cert, key, err := generateNebulaCert(deviceName, request.IP, request.PublicKey, false) + cert, key, fingerprint, err := generateNebulaCert(deviceName, request.IP, request.PublicKey, false) if err != nil { utils.Error("DeviceCreation: Error while creating Device", err) @@ -82,6 +83,13 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { return } + if err != nil { + utils.Error("DeviceCreation: Error while getting fingerprint", err) + utils.HTTPError(w, "Device Creation Error: " + err.Error(), + http.StatusInternalServerError, "DC007") + return + } + _, err3 := c.InsertOne(nil, map[string]interface{}{ "Nickname": nickname, "DeviceName": deviceName, @@ -91,6 +99,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { "IsRelay": request.IsRelay, "PublicHostname": request.PublicHostname, "Port": request.Port, + "Fingerprint": fingerprint, }) if err3 != nil { diff --git a/src/constellation/api_nebula.go b/src/constellation/api_nebula.go index 1d67c2f..0374760 100644 --- a/src/constellation/api_nebula.go +++ b/src/constellation/api_nebula.go @@ -4,7 +4,7 @@ import ( "net/http" "encoding/json" "io/ioutil" - + "os" "github.com/azukaar/cosmos-server/src/utils" ) @@ -81,11 +81,15 @@ func API_GetLogs(w http.ResponseWriter, req *http.Request) { } if(req.Method == "GET") { - + logs, err := os.ReadFile(utils.CONFIGFOLDER+"nebula.log") + if err != nil { + utils.Error("Error reading file:", err) + return + } json.NewEncoder(w).Encode(map[string]interface{}{ "status": "OK", - "data": logBuffer.String(), + "data": string(logs), }) } else { utils.Error("SettingGet: Method not allowed" + req.Method, nil) diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index 7645e57..e4cc6e9 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -12,11 +12,12 @@ import ( "strings" "io/ioutil" "strconv" + "encoding/json" "io" - "bytes" + "github.com/natefinch/lumberjack" ) -var logBuffer bytes.Buffer +var logBuffer *lumberjack.Logger var ( process *exec.Cmd @@ -38,16 +39,24 @@ func startNebulaInBackground() error { return errors.New("nebula is already running") } + logBuffer = &lumberjack.Logger{ + Filename: utils.CONFIGFOLDER+"nebula.log", + MaxSize: 1, // megabytes + MaxBackups: 1, + MaxAge: 15, //days + Compress: false, + } + process = exec.Command(binaryToRun(), "-config", utils.CONFIGFOLDER+"nebula.yml") // Set up multi-writer for stderr - process.Stderr = io.MultiWriter(&logBuffer, os.Stderr) + process.Stderr = io.MultiWriter(logBuffer, os.Stderr) if utils.LoggingLevelLabels[utils.GetMainConfig().LoggingLevel] == utils.DEBUG { // Set up multi-writer for stdout if in debug mode - process.Stdout = io.MultiWriter(&logBuffer, os.Stdout) + process.Stdout = io.MultiWriter(logBuffer, os.Stdout) } else { - process.Stdout = io.MultiWriter(&logBuffer) + process.Stdout = io.MultiWriter(logBuffer) } // Start the process in the background @@ -125,6 +134,26 @@ func GetAllLightHouses() ([]utils.ConstellationDevice, error) { return devices, nil } +func GetBlockedDevices() ([]utils.ConstellationDevice, error) { + c, err := utils.GetCollection(utils.GetRootAppId(), "devices") + if err != nil { + return []utils.ConstellationDevice{}, err + } + + var devices []utils.ConstellationDevice + + cursor, err := c.Find(nil, map[string]interface{}{ + "Blocked": true, + }) + cursor.All(nil, &devices) + + if err != nil { + return []utils.ConstellationDevice{}, err + } + + return devices, nil +} + func cleanIp(ip string) string { return strings.Split(ip, "/")[0] } @@ -133,10 +162,14 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st // Combine defaultConfig and overwriteConfig finalConfig := NebulaDefaultConfig - finalConfig.StaticHostMap = map[string][]string{ - "192.168.201.1": []string{ - utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242", - }, + if !overwriteConfig.PrivateNode { + finalConfig.StaticHostMap = map[string][]string{ + "192.168.201.1": []string{ + utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242", + }, + } + } else { + finalConfig.StaticHostMap = map[string][]string{} } // for each lighthouse @@ -150,6 +183,16 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st l.PublicHostname + ":" + l.Port, } } + + // add blocked devices + blockedDevices, err := GetBlockedDevices() + if err != nil { + return err + } + + for _, d := range blockedDevices { + finalConfig.PKI.Blocklist = append(finalConfig.PKI.Blocklist, d.Fingerprint) + } // add other lighthouses finalConfig.Lighthouse.Hosts = []string{} @@ -334,7 +377,40 @@ func killAllNebulaInstances() error { return nil } -func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, error) { +func GetCertFingerprint(certPath string) (string, error) { + // nebula-cert print -json + var cmd *exec.Cmd + + cmd = exec.Command(binaryToRun() + "-cert", + "print", + "-json", + "-path", certPath, + ) + + // capture and parse output + output, err := cmd.CombinedOutput() + if err != nil { + utils.Error("Error while printing cert", err) + } + + var certInfo map[string]interface{} + err = json.Unmarshal(output, &certInfo) + if err != nil { + utils.Error("Error while unmarshalling cert information", err) + return "", err + } + + // Extract fingerprint, replace "fingerprint" with the actual key where the fingerprint is stored in the JSON output + fingerprint, ok := certInfo["fingerprint"].(string) + if !ok { + utils.Error("Fingerprint not found or not a string", nil) + return "", errors.New("fingerprint not found or not a string") + } + + return fingerprint, nil +} + +func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, string, error) { // Run the nebula-cert command var cmd *exec.Cmd @@ -348,9 +424,9 @@ func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, e ) } else { // write PK to temp.cert - err := ioutil.WriteFile("./temp.cert", []byte(PK), 0644) + err := ioutil.WriteFile("./temp.key", []byte(PK), 0644) if err != nil { - return "", "", fmt.Errorf("failed to write temp.cert: %s", err) + return "", "", "", fmt.Errorf("failed to write temp.key: %s", err) } cmd = exec.Command(binaryToRun() + "-cert", "sign", @@ -358,10 +434,10 @@ func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, e "-ca-key", utils.CONFIGFOLDER + "ca.key", "-name", name, "-ip", ip, - "-in-pub", "./temp.cert", + "-in-pub", "./temp.key", ) - // delete temp.cert - defer os.Remove("./temp.cert") + // delete temp.key + defer os.Remove("./temp.key") } utils.Debug(cmd.String()) @@ -377,7 +453,7 @@ func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, e cmd.Run() if cmd.ProcessState.ExitCode() != 0 { - return "", "", fmt.Errorf("nebula-cert exited with an error, check the Cosmos logs") + return "", "", "", fmt.Errorf("nebula-cert exited with an error, check the Cosmos logs") } // Read the generated certificate and key files @@ -387,14 +463,19 @@ func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, e utils.Debug("Reading certificate from " + certPath) utils.Debug("Reading key from " + keyPath) + fingerprint, err := GetCertFingerprint(certPath) + if err != nil { + return "", "", "", fmt.Errorf("failed to get certificate fingerprint: %s", err) + } + certContent, errCert := ioutil.ReadFile(certPath) if errCert != nil { - return "", "", fmt.Errorf("failed to read certificate file: %s", errCert) + return "", "", "", fmt.Errorf("failed to read certificate file: %s", errCert) } keyContent, errKey := ioutil.ReadFile(keyPath) if errKey != nil { - return "", "", fmt.Errorf("failed to read key file: %s", errKey) + return "", "", "", fmt.Errorf("failed to read key file: %s", errKey) } if saveToFile { @@ -407,15 +488,15 @@ func generateNebulaCert(name, ip, PK string, saveToFile bool) (string, string, e } else { // Delete the generated certificate and key files if err := os.Remove(certPath); err != nil { - return "", "", fmt.Errorf("failed to delete certificate file: %s", err) + return "", "", "", fmt.Errorf("failed to delete certificate file: %s", err) } if err := os.Remove(keyPath); err != nil { - return "", "", fmt.Errorf("failed to delete key file: %s", err) + return "", "", "", fmt.Errorf("failed to delete key file: %s", err) } } - return string(certContent), string(keyContent), nil + return string(certContent), string(keyContent), fingerprint, nil } func generateNebulaCACert(name string) (error) { diff --git a/src/constellation/nebula_default.go b/src/constellation/nebula_default.go index 8b3abe8..d36b850 100644 --- a/src/constellation/nebula_default.go +++ b/src/constellation/nebula_default.go @@ -12,10 +12,12 @@ func InitConfig() { CA string `yaml:"ca"` Cert string `yaml:"cert"` Key string `yaml:"key"` + Blocklist []string `yaml:"blocklist"` }{ CA: utils.CONFIGFOLDER + "ca.crt", Cert: utils.CONFIGFOLDER + "cosmos.crt", Key: utils.CONFIGFOLDER + "cosmos.key", + Blocklist: []string{}, }, StaticHostMap: map[string][]string{ diff --git a/src/httpServer.go b/src/httpServer.go index 845fd66..066a212 100644 --- a/src/httpServer.go +++ b/src/httpServer.go @@ -338,6 +338,7 @@ func InitServer() *mux.Router { srapi.HandleFunc("/api/constellation/connect", constellation.API_ConnectToExisting) srapi.HandleFunc("/api/constellation/config", constellation.API_GetConfig) srapi.HandleFunc("/api/constellation/logs", constellation.API_GetLogs) + srapi.HandleFunc("/api/constellation/block", constellation.DeviceBlock) if(!config.HTTPConfig.AcceptAllInsecureHostname) { srapi.Use(utils.EnsureHostname) diff --git a/src/utils/types.go b/src/utils/types.go index 8d4460a..0a6bcc7 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -212,16 +212,22 @@ type MarketSource struct { type ConstellationConfig struct { Enabled bool SlaveMode bool + PrivateNode bool DNS bool DNSPort string DNSFallback string DNSBlockBlacklist bool DNSAdditionalBlocklists []string - CustomDNSEntries map[string]string + CustomDNSEntries []ConstellationDNSEntry NebulaConfig NebulaConfig ConstellationHostname string } +type ConstellationDNSEntry struct { + Type string + Key string + Value string +} type ConstellationDevice struct { Nickname string `json:"nickname"` DeviceName string `json:"deviceName"` @@ -231,6 +237,8 @@ type ConstellationDevice struct { IsRelay bool `json:"isRelay"` PublicHostname string `json:"publicHostname"` Port string `json:"port"` + Blocked bool `json:"blocked"` + Fingerprint string `json:"fingerprint"` } type NebulaFirewallRule struct { @@ -251,6 +259,7 @@ type NebulaConfig struct { CA string `yaml:"ca"` Cert string `yaml:"cert"` Key string `yaml:"key"` + Blocklist []string `yaml:"blocklist"` } `yaml:"pki"` StaticHostMap map[string][]string `yaml:"static_host_map"` From c7dda3db6c3c4cd1c1023daa62a730b9d1e2a309 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 4 Oct 2023 18:26:47 +0100 Subject: [PATCH 21/36] [release] v0.10.0-unstable17 --- LICENCE | 66 ++++++++++++------ changelog.md | 2 + cla.md | 2 +- .../DrawerContent/Navigation/NavGroup.jsx | 2 +- .../DrawerContent/Navigation/NavItem.jsx | 7 ++ client/src/menu-items/pages.jsx | 5 +- client/src/pages/authentication/Logoff.jsx | 2 +- client/src/pages/config/users/configman.jsx | 67 +++++++++++++++++-- client/src/pages/constellation/addDevice.jsx | 23 +++++-- client/src/pages/constellation/dns.jsx | 14 ++-- client/src/pages/constellation/index.jsx | 28 +++++++- client/src/pages/constellation/vpn.jsx | 13 ++-- client/src/pages/home/index.jsx | 27 +++++--- client/src/pages/market/listing.jsx | 21 +++--- client/src/utils/hooks.js | 26 +++++++ package-lock.json | 32 ++++++++- package.json | 3 +- src/constellation/DNS.go | 2 +- src/constellation/api_devices_block.go | 2 + src/constellation/api_devices_config.go | 41 ++++++++++++ src/constellation/api_devices_create.go | 8 ++- src/constellation/api_nebula_connect.go | 2 +- src/constellation/nebula.go | 10 +-- src/market/index.go | 3 +- src/user/token.go | 25 +++++++ src/utils/types.go | 3 +- src/utils/utils.go | 2 +- 27 files changed, 355 insertions(+), 83 deletions(-) create mode 100644 client/src/utils/hooks.js create mode 100644 src/constellation/api_devices_config.go diff --git a/LICENCE b/LICENCE index 954fd0a..1cadf6a 100644 --- a/LICENCE +++ b/LICENCE @@ -1,31 +1,57 @@ -“Commons Clause” License Condition v1.0 - -The Software is provided to you by the Licensor under the -License, as defined below, subject to the following condition. - -Without limiting other conditions in the License, the grant -of rights under the License will not include, and the License -does not grant to you, the right to Sell the Software. - -For purposes of the foregoing, “Sell” means practicing any or -all of the rights granted to you under the License to provide -to third parties, for a fee or other consideration (including -without limitation fees for hosting or consulting/ support -services related to the Software), a product or service whose -value derives, entirely or substantially, from the functionality -of the Software. Any license notice or attribution required by -the License must also include this Commons Clause License -Condition notice. - Software: Cosmos-Server -License: Apache 2.0 with Commons Clause +License: Apache 2.0 with Commons Clause and Anti Tampering Clause Licensor: Yann Stepienik +--------------------------------------------------------------------- + + “Commons Clause” License Condition v1.0 + + The Software is provided to you by the Licensor under the + License, as defined below, subject to the following condition. + + Without limiting other conditions in the License, the grant + of rights under the License will not include, and the License + does not grant to you, the right to Sell the Software. + + For purposes of the foregoing, “Sell” means practicing any or + all of the rights granted to you under the License to provide + to third parties, for a fee or other consideration (including + without limitation fees for hosting or consulting/ support + services related to the Software), a product or service whose + value derives, entirely or substantially, from the functionality + of the Software. Any license notice or attribution required by + the License must also include this Commons Clause License + Condition notice. --------------------------------------------------------------------- + "Anti Tampering Clause” License Condition v1.0 + + Notwithstanding any provision of the Apache License 2.0, if the User + (or any party receiving or distributing derivative works, services, + or anything of value from the User related to the Software), directly + or indirectly, seeks to tamper with, alter, circumvent, or avoid + compliance with any subscription, paywall, feature restriction, or any + other licensing mechanism built into the Software or its usage, the + License granted under the Apache License 2.0 shall automatically and + immediately terminate, and access to the Software shall be withdrawn + with immediate effect. Upon such termination, any and all rights + established under the Apache License 2.0 shall be null and void. + + Tampering includes but is not limited to: (a) removing, disabling, + or circumventing any license key or other copy protection mechanism, + (b) redistributing parts or all of a feature that was intended + to be a paid feature, without keeping the restrictions, limitations, + or other licensing mechanisms with it(c) disabling, circumventing, or + avoiding any feature of the Software that is intended to enforce usage or + copy restrictions, or (d) providing or distributing any information + or code that enables disabling, circumvention, or avoidance of any + feature of the Software that is intended to enforce usage or copy + restrictions. + +--------------------------------------------------------------------- Apache License Version 2.0, January 2004 diff --git a/changelog.md b/changelog.md index 1f51c44..ad8ed2b 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,8 @@ - Added Constellation - DNS Challenge is now used for all certificates when enabled - Rework headers for better compatibility + - Improve experience for non-admin users + - Fix bug with redirect on logout ## Version 0.9.20 - 0.9.21 - Add option to disable CORS hardening (with empty value) diff --git a/cla.md b/cla.md index 147f9d2..22a970d 100644 --- a/cla.md +++ b/cla.md @@ -2,7 +2,7 @@ Cosmos Software Grant and Contributor License Agreement (“Agreement”) This agreement is based on the Apache Software Foundation Contributor License Agreement. (v r190612) -Thank you for your interest in software projects stewarded by Raintank, Inc. dba Cosmos (“Cosmos”). In order to clarify the intellectual property license granted with Contributions from any person or entity, Cosmos must have a Contributor License Agreement (CLA) on file that has been agreed to by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Cosmos and its users; it does not change your rights to use your own Contributions for any other purpose. This Agreement allows an individual to contribute to Cosmos on that individual’s own behalf, or an entity (the “Corporation”) to submit Contributions to Cosmos, to authorize Contributions submitted by its designated employees to Cosmos, and to grant copyright and patent licenses thereto. +Thank you for your interest in dba Cosmos (“Cosmos”). In order to clarify the intellectual property license granted with Contributions from any person or entity, Cosmos must have a Contributor License Agreement (CLA) on file that has been agreed to by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Cosmos and its users; it does not change your rights to use your own Contributions for any other purpose. This Agreement allows an individual to contribute to Cosmos on that individual’s own behalf, or an entity (the “Corporation”) to submit Contributions to Cosmos, to authorize Contributions submitted by its designated employees to Cosmos, and to grant copyright and patent licenses thereto. You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Cosmos. Except for the license granted herein to Cosmos and recipients of software distributed by Cosmos, You reserve all right, title, and interest in and to Your Contributions. diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx index 9c001af..77a1365 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx @@ -47,7 +47,7 @@ const NavGroup = ({ item }) => { } sx={{ mb: drawerOpen ? 1.5 : 0, py: 0, zIndex: 0 }} > - {navCollapse} + {navCollapse} ); }; diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx index 77a3df0..deb2ddf 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx @@ -9,6 +9,7 @@ import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography } // project import import { activeItem } from '../../../../../store/reducers/menu'; +import { useClientInfos } from '../../../../../utils/hooks'; // ==============================|| NAVIGATION - LIST ITEM ||============================== // @@ -17,6 +18,12 @@ const NavItem = ({ item, level }) => { const dispatch = useDispatch(); const menu = useSelector((state) => state.menu); const { drawerOpen, openItem } = menu; + const {role} = useClientInfos(); + const isAdmin = role === 1; + + if (item.adminOnly && !isAdmin) { + return null; + } let itemTarget = '_self'; if (item.target) { diff --git a/client/src/menu-items/pages.jsx b/client/src/menu-items/pages.jsx index a2e4068..171c841 100644 --- a/client/src/menu-items/pages.jsx +++ b/client/src/menu-items/pages.jsx @@ -20,7 +20,8 @@ const pages = { title: 'ServApps', type: 'item', url: '/cosmos-ui/servapps', - icon: AppstoreOutlined + icon: AppstoreOutlined, + adminOnly: true }, { id: 'url', @@ -43,6 +44,7 @@ const pages = { type: 'item', url: '/cosmos-ui/config-users', icon: icons.ProfileOutlined, + adminOnly: true }, { id: 'openid', @@ -50,6 +52,7 @@ const pages = { type: 'item', url: '/cosmos-ui/openid-manage', icon: PicLeftOutlined, + adminOnly: true }, { id: 'config', diff --git a/client/src/pages/authentication/Logoff.jsx b/client/src/pages/authentication/Logoff.jsx index 635efc0..3e7924f 100644 --- a/client/src/pages/authentication/Logoff.jsx +++ b/client/src/pages/authentication/Logoff.jsx @@ -9,7 +9,7 @@ import AuthWrapper from './AuthWrapper'; import { useEffect } from 'react'; import * as API from '../../api'; -import { redirectTo } from '../../utils/indexs'; +import { redirectTo, redirectToLocal } from '../../utils/indexs'; // ================================|| REGISTER ||================================ // diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index 20e90bf..e3a57af 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -31,6 +31,7 @@ import { TwitterPicker // TODO: Remove circular deps import {SetPrimaryColor, SetSecondaryColor} from '../../../App'; +import { useClientInfos } from '../../../utils/hooks'; const ConfigManagement = () => { const [config, setConfig] = React.useState(null); @@ -38,6 +39,8 @@ const ConfigManagement = () => { const [openResartModal, setOpenRestartModal] = React.useState(false); const [uploadingBackground, setUploadingBackground] = React.useState(false); const [saveLabel, setSaveLabel] = React.useState("Save"); + const {role} = useClientInfos(); + const isAdmin = role === 1; function refresh() { API.config.get().then((res) => { @@ -62,9 +65,9 @@ const ConfigManagement = () => { refresh(); }}>Refresh - + }}>Restart Server}
{config && <> @@ -186,7 +189,7 @@ const ConfigManagement = () => { {(formik) => (
- + {isAdmin && {formik.errors.submit && ( {formik.errors.submit} @@ -205,7 +208,13 @@ const ConfigManagement = () => { {saveLabel} - + } + + {!isAdmin &&
+ As you are not an admin, you can't edit the configuration. + This page is only here for visibility. + +
} @@ -331,6 +340,29 @@ const ConfigManagement = () => { formik.setFieldValue('PrimaryColor', colorRGB); SetPrimaryColor(colorRGB); }} + colors={[ + '#ab47bc', + '#4527a0', + '#FF6900', + '#FCB900', + '#7BDCB5', + '#00D084', + '#8ED1FC', + '#0693E3', + '#ABB8C3', + '#EB144C', + '#F78DA7', + '#9900EF', + '#FF0000', + '#FFC0CB', + '#20B2AA', + '#FFFF00', + '#8A2BE2', + '#A52A2A', + '#5F9EA0', + '#7FFF00', + '#D2691E' + ]} />
@@ -346,6 +378,29 @@ const ConfigManagement = () => { formik.setFieldValue('SecondaryColor', colorRGB); SetSecondaryColor(colorRGB); }} + colors={[ + '#ab47bc', + '#4527a0', + '#FF6900', + '#FCB900', + '#7BDCB5', + '#00D084', + '#8ED1FC', + '#0693E3', + '#ABB8C3', + '#EB144C', + '#F78DA7', + '#9900EF', + '#FF0000', + '#FFC0CB', + '#20B2AA', + '#FFFF00', + '#8A2BE2', + '#A52A2A', + '#5F9EA0', + '#7FFF00', + '#D2691E' + ]} /> @@ -627,7 +682,7 @@ const ConfigManagement = () => { - + {isAdmin && {formik.errors.submit && ( {formik.errors.submit} @@ -646,7 +701,7 @@ const ConfigManagement = () => { {saveLabel} - + }
)} diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index ea2aea8..1000dbc 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -1,5 +1,5 @@ // material-ui -import { Alert, Button, Stack, TextField } from '@mui/material'; +import { Alert, Button, InputLabel, OutlinedInput, Stack, TextField } from '@mui/material'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; @@ -15,6 +15,7 @@ import * as API from '../../api'; import { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; import { DownloadFile } from '../../api/downloadButton'; import QRCode from 'qrcode'; +import { useClientInfos } from '../../utils/hooks'; const getDocker = (data, isCompose) => { let lighthouses = ''; @@ -73,10 +74,12 @@ docker run -d \\ } -const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { +const AddDeviceModal = ({ users, config, refreshConfig, devices }) => { const [openModal, setOpenModal] = useState(false); const [isDone, setIsDone] = useState(null); const canvasRef = React.useRef(null); + const {role, nickname} = useClientInfos(); + const isAdmin = role === 1; let firstIP = "192.168.201.2/24"; if (devices && devices.length > 0) { @@ -116,7 +119,7 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { setOpenModal(false)}> { formik={formik} /> {!formik.values.isLighthouse && - { return [u.nickname, u.nickname] }) } - />} + /> : <> + Owner + + )} { }, []); return <> - {(config) ? <>
@@ -40,8 +39,6 @@ export const ConstellationDNS = () => { { }} onSubmit={(values) => { let newConfig = { ...config }; - newConfig.ConstellationConfig.DNS = values.Enabled; - newConfig.ConstellationConfig.DNSPort = values.Port; newConfig.ConstellationConfig.DNSFallback = values.Fallback; newConfig.ConstellationConfig.DNSBlockBlacklist = values.DNSBlockBlacklist; newConfig.ConstellationConfig.DNSAdditionalBlocklists = values.DNSAdditionalBlocklists; @@ -62,14 +57,19 @@ export const ConstellationDNS = () => { {(formik) => (
- - + This is a DNS that runs inside your Constellation network. It automatically + rewrites your domains DNS entries to be local to your network, and also allows you to do things like block ads + and trackers on all devices connected to your network. You can also add custom DNS entries to resolve to specific + IP addresses. This DNS server is only accessible from inside your network. + + When changing your DNS records, always use private mode on your browser and allow some times for various caches to expire. + DNS Blocklist URLs {formik.values.DNSAdditionalBlocklists.map((item, index) => ( diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index dea63ec..6b76c75 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -1,6 +1,6 @@ import * as React from 'react'; import MainCard from '../../components/MainCard'; -import { Chip, Divider, Stack, useMediaQuery } from '@mui/material'; +import { Alert, Chip, Divider, Stack, useMediaQuery } from '@mui/material'; import HostChip from '../../components/hostChip'; import { RouteMode, RouteSecurity } from '../../components/routeComponents'; import { getFaviconURL } from '../../utils/routes'; @@ -8,12 +8,16 @@ import * as API from '../../api'; import { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons"; import IsLoggedIn from '../../isLoggedIn'; import PrettyTabbedView from '../../components/tabbedView/tabbedView'; +import { useClientInfos } from '../../utils/hooks'; import { ConstellationVPN } from './vpn'; import { ConstellationDNS } from './dns'; const ConstellationIndex = () => { - return
+ const {role} = useClientInfos(); + const isAdmin = role === 1; + + return isAdmin ?
{ children: , path: 'dns' }, + { + title: 'Firewall', + children:
+ + Coming soon. This feature will allow you to open and close ports individually + on each device and decide who can access them. + +
, + }, + { + title: 'Unsafe Routes', + children:
+ + Coming soon. This feature will allow you to tunnel your traffic through + your devices to things outside of your constellation. + +
, + } ]}/> -
; +
: ; } export default ConstellationIndex; \ No newline at end of file diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx index 65e7b9f..c60c0af 100644 --- a/client/src/pages/constellation/vpn.jsx +++ b/client/src/pages/constellation/vpn.jsx @@ -15,6 +15,7 @@ import ApiModal from "../../components/apiModal"; import { isDomain } from "../../utils/indexs"; import ConfirmModal from "../../components/confirmModal"; import UploadButtons from "../../components/fileUpload"; +import { useClientInfos } from "../../utils/hooks"; const getDefaultConstellationHostname = (config) => { // if domain is set, use it @@ -26,17 +27,20 @@ const getDefaultConstellationHostname = (config) => { } export const ConstellationVPN = () => { - const [isAdmin, setIsAdmin] = useState(false); const [config, setConfig] = useState(null); const [users, setUsers] = useState(null); const [devices, setDevices] = useState(null); + const {role} = useClientInfos(); + const isAdmin = role === 1; const refreshConfig = async () => { let configAsync = await API.config.get(); setConfig(configAsync.data); - setIsAdmin(configAsync.isAdmin); setDevices((await API.constellation.list()).data || []); - setUsers((await API.users.list()).data || []); + if(isAdmin) + setUsers((await API.users.list()).data || []); + else + setUsers([]); }; useEffect(() => { @@ -61,7 +65,6 @@ export const ConstellationVPN = () => { } return <> - {(devices && config && users) ? <>
@@ -160,7 +163,7 @@ export const ConstellationVPN = () => { data={devices.filter((d) => !d.blocked)} getKey={(r) => r.deviceName} buttons={[ - , + , ]} columns={[ { diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index 253f497..99367c8 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -12,6 +12,7 @@ import { getFullOrigin } from "../../utils/routes"; import IsLoggedIn from "../../isLoggedIn"; import { ServAppIcon } from "../../utils/servapp-icon"; import Chart from 'react-apexcharts'; +import { useClientInfos } from "../../utils/hooks"; export const HomeBackground = () => { @@ -87,6 +88,8 @@ const HomePage = () => { const theme = useTheme(); const isDark = theme.palette.mode === 'dark'; const isMd = useMediaQuery(theme.breakpoints.up('md')); + const {role} = useClientInfos(); + const isAdmin = role === 1; const blockStyle = { margin: 0, @@ -112,9 +115,13 @@ const HomePage = () => { } const refreshConfig = () => { - API.docker.list().then((res) => { - setServApps(res.data); - }); + if(isAdmin) { + API.docker.list().then((res) => { + setServApps(res.data); + }); + } else { + setServApps([]); + } API.config.get().then((res) => { setConfig(res.data); }); @@ -213,20 +220,20 @@ const HomePage = () => { - {coStatus && !coStatus.database && ( + {isAdmin && coStatus && !coStatus.database && ( No Database is setup for Cosmos! User Management and Authentication will not work.
You can either setup the database, or disable user management in the configuration panel.
)} - {coStatus && coStatus.letsencrypt && ( + {isAdmin && coStatus && coStatus.letsencrypt && ( You have enabled Let's Encrypt for automatic HTTPS Certificate. You need to provide the configuration with an email address to use for Let's Encrypt in the configs. )} - {coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && ( + {isAdmin && coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && ( There are errors with your Let's Encrypt configuration or one of your routes, please fix them as soon as possible: {coStatus.LetsEncryptErrors.map((err) => { @@ -235,25 +242,25 @@ const HomePage = () => { )} - {coStatus && coStatus.newVersionAvailable && ( + {isAdmin && coStatus && coStatus.newVersionAvailable && ( A new version of Cosmos is available! Please update to the latest version to get the latest features and bug fixes. )} - {coStatus && coStatus.needsRestart && ( + {isAdmin && coStatus && coStatus.needsRestart && ( You have made changes to the configuration that require a restart to take effect. Please restart Cosmos to apply the changes. )} - {coStatus && coStatus.domain && ( + {isAdmin && coStatus && coStatus.domain && ( You are using localhost or 0.0.0.0 as a hostname in the configuration. It is recommended that you use a domain name or an IP instead. )} - {coStatus && !coStatus.docker && ( + {isAdmin && coStatus && !coStatus.docker && ( Docker is not connected! Please check your docker connection.
Did you forget to add
-v /var/run/docker.sock:/var/run/docker.sock
to your docker run command?
diff --git a/client/src/pages/market/listing.jsx b/client/src/pages/market/listing.jsx index 6a8d6e7..df430d3 100644 --- a/client/src/pages/market/listing.jsx +++ b/client/src/pages/market/listing.jsx @@ -12,6 +12,7 @@ import { Link as LinkMUI } from '@mui/material' import DockerComposeImport from '../servapps/containers/docker-compose'; import { AppstoreAddOutlined, SearchOutlined } from "@ant-design/icons"; import ResponsiveButton from "../../components/responseiveButton"; +import { useClientInfos } from "../../utils/hooks"; function Screenshots({ screenshots }) { return screenshots.length > 1 ? ( @@ -23,17 +24,17 @@ function Screenshots({ screenshots }) { : } -function Showcases({ showcase, isDark }) { +function Showcases({ showcase, isDark, isAdmin }) { return ( { - showcase.map((item, i) => ) + showcase.map((item, i) => ) } ) } -function ShowcasesItem({ isDark, item }) { +function ShowcasesItem({ isDark, item, isAdmin }) { return (

-
+ {isAdmin &&
-
+
} @@ -110,6 +111,8 @@ const MarketPage = () => { const isDark = theme.palette.mode === 'dark'; const { appName, appStore } = useParams(); const [search, setSearch] = useState(""); + const {role} = useClientInfos(); + const isAdmin = role === 1; const backgroundStyle = isDark ? { backgroundColor: 'rgb(0,0,0)', @@ -178,7 +181,7 @@ const MarketPage = () => {
- +
@@ -202,9 +205,9 @@ const MarketPage = () => {
-
+ {isAdmin &&
-
+
}
} @@ -223,7 +226,7 @@ const MarketPage = () => { size={100} /> } - {showcase && showcase.length > 0 && } + {showcase && showcase.length > 0 && }
= 16.3.0" + } + }, "node_modules/react-copy-to-clipboard": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", @@ -10368,6 +10387,15 @@ "node": ">=4" } }, + "node_modules/universal-cookie": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-6.1.1.tgz", + "integrity": "sha512-33S9x3CpdUnnjwTNs2Fgc41WGve2tdLtvaK2kPSbZRc5pGpz2vQFbRWMxlATsxNNe/Cy8SzmnmbuBM85jpZPtA==", + "dependencies": { + "@types/cookie": "^0.5.1", + "cookie": "^0.5.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 6a39eec..6d59db9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable16", + "version": "0.10.0-unstable17", "description": "", "main": "test-server.js", "bugs": { @@ -36,6 +36,7 @@ "react": "^18.2.0", "react-apexcharts": "^1.4.0", "react-color": "^2.19.3", + "react-cookie": "^6.1.1", "react-copy-to-clipboard": "^5.1.0", "react-device-detect": "^2.2.2", "react-dom": "^18.2.0", diff --git a/src/constellation/DNS.go b/src/constellation/DNS.go index c7f23ac..40864cd 100644 --- a/src/constellation/DNS.go +++ b/src/constellation/DNS.go @@ -176,7 +176,7 @@ func InitDNS() { utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains") } - if(config.ConstellationConfig.DNS) { + if(!config.ConstellationConfig.DNSDisabled) { go (func() { dns.HandleFunc(".", handleDNSRequest) server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"} diff --git a/src/constellation/api_devices_block.go b/src/constellation/api_devices_block.go index 9465c76..14568de 100644 --- a/src/constellation/api_devices_block.go +++ b/src/constellation/api_devices_block.go @@ -39,6 +39,8 @@ func DeviceBlock(w http.ResponseWriter, req *http.Request) { return } + utils.Log("ConstellationDeviceBlocking: Blocking Device " + deviceName) + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") if errCo != nil { utils.Error("Database Connect", errCo) diff --git a/src/constellation/api_devices_config.go b/src/constellation/api_devices_config.go new file mode 100644 index 0000000..b9fe8f5 --- /dev/null +++ b/src/constellation/api_devices_config.go @@ -0,0 +1,41 @@ +package constellation + +// import ( +// "net/http" +// "encoding/json" +// "math/rand" +// "time" +// "net" + +// "github.com/azukaar/cosmos-server/src/utils" +// ) + +// func DeviceConfig(w http.ResponseWriter, req *http.Request) { +// time.Sleep(time.Duration(rand.Float64()*2)*time.Second) + +// if(req.Method == "GET") { + +// ip, _, err := net.SplitHostPort(req.RemoteAddr) +// if err != nil { +// http.Error(w, "Invalid request", http.StatusBadRequest) +// return +// } + +// // get authorization header +// auth := req.Header.Get("Authorization") +// if auth == "" { +// http.Error(w, "Unauthorized", http.StatusUnauthorized) +// return +// } + +// // remove "Bearer " from auth header +// auth = strings.Replace(auth, "Bearer ", "", 1) + + + +// } else { +// utils.Error("DeviceConfig: Method not allowed" + req.Method, nil) +// utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") +// return +// } +// } \ No newline at end of file diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index bc81754..995669b 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -44,11 +44,14 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { nickname := utils.Sanitize(request.Nickname) deviceName := utils.Sanitize(request.DeviceName) + APIKey := utils.GenerateRandomString(32) if utils.AdminOrItselfOnly(w, req, nickname) != nil { return } + utils.Log("ConstellationDeviceCreation: Creating Device " + deviceName) + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") if errCo != nil { utils.Error("Database Connect", errCo) @@ -100,6 +103,8 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { "PublicHostname": request.PublicHostname, "Port": request.Port, "Fingerprint": fingerprint, + "APIKey": APIKey, + "Blocked": false, }) if err3 != nil { @@ -123,7 +128,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { } // read configYml from config/nebula.yml - configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, utils.ConstellationDevice{ + configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, APIKey, utils.ConstellationDevice{ Nickname: nickname, DeviceName: deviceName, PublicKey: key, @@ -132,6 +137,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { IsRelay: request.IsRelay, PublicHostname: request.PublicHostname, Port: request.Port, + APIKey: APIKey, }) if err != nil { diff --git a/src/constellation/api_nebula_connect.go b/src/constellation/api_nebula_connect.go index 8972c74..2053db4 100644 --- a/src/constellation/api_nebula_connect.go +++ b/src/constellation/api_nebula_connect.go @@ -26,7 +26,7 @@ func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) { config := utils.ReadConfigFromFile() config.ConstellationConfig.Enabled = true config.ConstellationConfig.SlaveMode = true - config.ConstellationConfig.DNS = false + config.ConstellationConfig.DNSDisabled = false // ConstellationHostname = // output utils.CONFIGFOLDER + "nebula.yml" diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index e4cc6e9..fa4fa7b 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -235,7 +235,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st return nil } -func getYAMLClientConfig(name, configPath, capki, cert, key string, device utils.ConstellationDevice) (string, error) { +func getYAMLClientConfig(name, configPath, capki, cert, key, APIKey string, device utils.ConstellationDevice) (string, error) { utils.Log("Exporting YAML config for " + name + " with file " + configPath) // Read the YAML config file @@ -321,9 +321,11 @@ func getYAMLClientConfig(name, configPath, capki, cert, key string, device utils return "", errors.New("listen not found in nebula.yml") } - configMap["deviceName"] = name - configMap["local_dns_overwrite"] = "192.168.201.1" - configMap["public_hostname"] = device.PublicHostname + configMap["constellation_device_name"] = name + configMap["constellation_local_dns_overwrite"] = true + configMap["constellation_local_dns_overwrite_address"] = "192.168.201.1" + configMap["constellation_public_hostname"] = device.PublicHostname + configMap["constellation_api_key"] = APIKey // export configMap as YML yamlData, err = yaml.Marshal(configMap) diff --git a/src/market/index.go b/src/market/index.go index 7081b74..ee5d4af 100644 --- a/src/market/index.go +++ b/src/market/index.go @@ -12,7 +12,7 @@ type marketGetResult struct { } func MarketGet(w http.ResponseWriter, req *http.Request) { - if utils.AdminOnly(w, req) != nil { + if utils.LoggedInOnly(w, req) != nil { return } @@ -22,7 +22,6 @@ func MarketGet(w http.ResponseWriter, req *http.Request) { return } - // return the first 10 results of each market marketGetResult := marketGetResult{ All: make(map[string]interface{}), Showcase: []appDefinition{}, diff --git a/src/user/token.go b/src/user/token.go index dd467bb..ad30ab3 100644 --- a/src/user/token.go +++ b/src/user/token.go @@ -5,6 +5,7 @@ import ( "github.com/azukaar/cosmos-server/src/utils" "github.com/golang-jwt/jwt" "errors" + "strconv" "strings" "time" "encoding/json" @@ -189,13 +190,25 @@ func logOutUser(w http.ResponseWriter, req *http.Request) { HttpOnly: true, } + clientCookie := http.Cookie{ + Name: "client-infos", + Value: "{}", + Expires: time.Now().Add(-time.Hour * 24 * 365), + Path: "/", + Secure: utils.IsHTTPS, + HttpOnly: false, + } + if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" { cookie.Domain = "" + clientCookie.Domain = "" } else { cookie.Domain = "." + reqHostNoPort + clientCookie.Domain = "." + reqHostNoPort } http.SetCookie(w, &cookie) + http.SetCookie(w, &clientCookie) // TODO: logout every other device if asked by increasing passwordcycle } @@ -254,13 +267,24 @@ func SendUserToken(w http.ResponseWriter, req *http.Request, user utils.User, mf HttpOnly: true, } + clientCookie := http.Cookie{ + Name: "client-infos", + Value: user.Nickname + "," + strconv.Itoa(int(user.Role)), + Expires: expiration, + Path: "/", + Secure: utils.IsHTTPS, + HttpOnly: false, + } + utils.Log("UserLogin: Setting cookie for " + reqHostNoPort) if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" { cookie.Domain = "" + clientCookie.Domain = "" } else { if utils.IsValidHostname(reqHostNoPort) { cookie.Domain = "." + reqHostNoPort + clientCookie.Domain = "." + reqHostNoPort } else { utils.Error("UserLogin: Invalid hostname", nil) utils.HTTPError(w, "User Logging Error", http.StatusInternalServerError, "UL001") @@ -269,4 +293,5 @@ func SendUserToken(w http.ResponseWriter, req *http.Request, user utils.User, mf } http.SetCookie(w, &cookie) + http.SetCookie(w, &clientCookie) } \ No newline at end of file diff --git a/src/utils/types.go b/src/utils/types.go index 0a6bcc7..0c187d8 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -213,7 +213,7 @@ type ConstellationConfig struct { Enabled bool SlaveMode bool PrivateNode bool - DNS bool + DNSDisabled bool DNSPort string DNSFallback string DNSBlockBlacklist bool @@ -239,6 +239,7 @@ type ConstellationDevice struct { Port string `json:"port"` Blocked bool `json:"blocked"` Fingerprint string `json:"fingerprint"` + APIKey string `json:"-"` } type NebulaFirewallRule struct { diff --git a/src/utils/utils.go b/src/utils/utils.go index 1a32bed..cf5b0b3 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -64,7 +64,7 @@ var DefaultConfig = Config{ }, ConstellationConfig: ConstellationConfig{ Enabled: false, - DNS: true, + DNSDisabled: false, DNSFallback: "8.8.8.8:53", DNSAdditionalBlocklists: []string{ "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt", From 8d359ca00860e3c7353dc844dd1bce6acb863e2b Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 4 Oct 2023 18:35:26 +0100 Subject: [PATCH 22/36] [release] v0.10.0-unstable18 --- .../MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx | 2 +- client/src/pages/config/users/configman.jsx | 2 +- client/src/pages/constellation/addDevice.jsx | 2 +- client/src/pages/constellation/index.jsx | 2 +- client/src/pages/constellation/vpn.jsx | 2 +- client/src/pages/home/index.jsx | 2 +- client/src/pages/market/listing.jsx | 2 +- package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx index deb2ddf..0537d1e 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx @@ -19,7 +19,7 @@ const NavItem = ({ item, level }) => { const menu = useSelector((state) => state.menu); const { drawerOpen, openItem } = menu; const {role} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; if (item.adminOnly && !isAdmin) { return null; diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index e3a57af..d42acfb 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -40,7 +40,7 @@ const ConfigManagement = () => { const [uploadingBackground, setUploadingBackground] = React.useState(false); const [saveLabel, setSaveLabel] = React.useState("Save"); const {role} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; function refresh() { API.config.get().then((res) => { diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 1000dbc..3609d86 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -79,7 +79,7 @@ const AddDeviceModal = ({ users, config, refreshConfig, devices }) => { const [isDone, setIsDone] = useState(null); const canvasRef = React.useRef(null); const {role, nickname} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; let firstIP = "192.168.201.2/24"; if (devices && devices.length > 0) { diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index 6b76c75..b96d012 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -15,7 +15,7 @@ import { ConstellationDNS } from './dns'; const ConstellationIndex = () => { const {role} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; return isAdmin ?
diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx index c60c0af..4efaff7 100644 --- a/client/src/pages/constellation/vpn.jsx +++ b/client/src/pages/constellation/vpn.jsx @@ -31,7 +31,7 @@ export const ConstellationVPN = () => { const [users, setUsers] = useState(null); const [devices, setDevices] = useState(null); const {role} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; const refreshConfig = async () => { let configAsync = await API.config.get(); diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index 99367c8..0f75539 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -89,7 +89,7 @@ const HomePage = () => { const isDark = theme.palette.mode === 'dark'; const isMd = useMediaQuery(theme.breakpoints.up('md')); const {role} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; const blockStyle = { margin: 0, diff --git a/client/src/pages/market/listing.jsx b/client/src/pages/market/listing.jsx index df430d3..7597a02 100644 --- a/client/src/pages/market/listing.jsx +++ b/client/src/pages/market/listing.jsx @@ -112,7 +112,7 @@ const MarketPage = () => { const { appName, appStore } = useParams(); const [search, setSearch] = useState(""); const {role} = useClientInfos(); - const isAdmin = role === 1; + const isAdmin = role === 2; const backgroundStyle = isDark ? { backgroundColor: 'rgb(0,0,0)', diff --git a/package.json b/package.json index 6d59db9..4b847bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable17", + "version": "0.10.0-unstable18", "description": "", "main": "test-server.js", "bugs": { From cf8a29c1aa04ef41ac511fdaa6f70419c9f8f373 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 4 Oct 2023 18:46:57 +0100 Subject: [PATCH 23/36] [release] v0.10.0-unstable19 --- .../MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx | 2 +- client/src/pages/config/users/configman.jsx | 2 +- client/src/pages/constellation/addDevice.jsx | 2 +- client/src/pages/constellation/index.jsx | 2 +- client/src/pages/constellation/vpn.jsx | 2 +- client/src/pages/home/index.jsx | 2 +- client/src/pages/market/listing.jsx | 2 +- package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx index 0537d1e..34c1a38 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx @@ -19,7 +19,7 @@ const NavItem = ({ item, level }) => { const menu = useSelector((state) => state.menu); const { drawerOpen, openItem } = menu; const {role} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; if (item.adminOnly && !isAdmin) { return null; diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index d42acfb..c61ee6c 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -40,7 +40,7 @@ const ConfigManagement = () => { const [uploadingBackground, setUploadingBackground] = React.useState(false); const [saveLabel, setSaveLabel] = React.useState("Save"); const {role} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; function refresh() { API.config.get().then((res) => { diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 3609d86..0c32453 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -79,7 +79,7 @@ const AddDeviceModal = ({ users, config, refreshConfig, devices }) => { const [isDone, setIsDone] = useState(null); const canvasRef = React.useRef(null); const {role, nickname} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; let firstIP = "192.168.201.2/24"; if (devices && devices.length > 0) { diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index b96d012..93d6e83 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -15,7 +15,7 @@ import { ConstellationDNS } from './dns'; const ConstellationIndex = () => { const {role} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; return isAdmin ?
diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx index 4efaff7..d0913b3 100644 --- a/client/src/pages/constellation/vpn.jsx +++ b/client/src/pages/constellation/vpn.jsx @@ -31,7 +31,7 @@ export const ConstellationVPN = () => { const [users, setUsers] = useState(null); const [devices, setDevices] = useState(null); const {role} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; const refreshConfig = async () => { let configAsync = await API.config.get(); diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index 0f75539..5905e7f 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -89,7 +89,7 @@ const HomePage = () => { const isDark = theme.palette.mode === 'dark'; const isMd = useMediaQuery(theme.breakpoints.up('md')); const {role} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; const blockStyle = { margin: 0, diff --git a/client/src/pages/market/listing.jsx b/client/src/pages/market/listing.jsx index 7597a02..f7587d9 100644 --- a/client/src/pages/market/listing.jsx +++ b/client/src/pages/market/listing.jsx @@ -112,7 +112,7 @@ const MarketPage = () => { const { appName, appStore } = useParams(); const [search, setSearch] = useState(""); const {role} = useClientInfos(); - const isAdmin = role === 2; + const isAdmin = role === "2"; const backgroundStyle = isDark ? { backgroundColor: 'rgb(0,0,0)', diff --git a/package.json b/package.json index 4b847bc..24fd6d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable18", + "version": "0.10.0-unstable19", "description": "", "main": "test-server.js", "bugs": { From 3f7527b6ef5ecd1546ebbea787c9838e995a2520 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Fri, 6 Oct 2023 13:20:29 +0100 Subject: [PATCH 24/36] [release] v0.10.0-unstable20 --- changelog.md | 4 +++- client/src/api/constellation.tsx | 4 ++-- client/src/index.css | 4 ++-- client/src/pages/constellation/addDevice.jsx | 2 +- package.json | 2 +- readme.md | 1 - src/constellation/api_nebula_connect.go | 2 +- src/constellation/index.go | 12 ++++++++++++ 8 files changed, 22 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index ad8ed2b..bda6e3c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,10 +5,12 @@ ======= ## Version 0.10.0 - Added Constellation - - DNS Challenge is now used for all certificates when enabled + - DNS Challenge is now used for all certificates when enabled [breaking change] - Rework headers for better compatibility - Improve experience for non-admin users - Fix bug with redirect on logout + + > **Note: If you use the ARM (:latest-arm) you need to manually update to using the :latest tag instead** ## Version 0.9.20 - 0.9.21 - Add option to disable CORS hardening (with empty value) diff --git a/client/src/api/constellation.tsx b/client/src/api/constellation.tsx index 261362d..006037f 100644 --- a/client/src/api/constellation.tsx +++ b/client/src/api/constellation.tsx @@ -60,9 +60,9 @@ function connect(file) { return wrap(fetch('/cosmos/api/constellation/connect', { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/yaml' }, - body: JSON.stringify(file), + body: file, })) } diff --git a/client/src/index.css b/client/src/index.css index 26dece5..39e1eae 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -124,8 +124,8 @@ } .loading-image:empty { - background: url('assets/images/icons/cosmos_gray.png') no-repeat center center; - background-size: contain; + /* background: url('assets/images/icons/cosmos_gray.png') no-repeat center center; + background-size: contain; */ } .raw-table table { diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index 0c32453..7ee3c5f 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -275,7 +275,7 @@ const AddDeviceModal = ({ users, config, refreshConfig, devices }) => { - + {!isDone && } diff --git a/package.json b/package.json index 24fd6d8..916009f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable19", + "version": "0.10.0-unstable20", "description": "", "main": "test-server.js", "bugs": { diff --git a/readme.md b/readme.md index ad32c52..7f1d879 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,6 @@ null Clayton Stone null -Adrian

--- diff --git a/src/constellation/api_nebula_connect.go b/src/constellation/api_nebula_connect.go index 2053db4..168bb14 100644 --- a/src/constellation/api_nebula_connect.go +++ b/src/constellation/api_nebula_connect.go @@ -26,7 +26,7 @@ func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) { config := utils.ReadConfigFromFile() config.ConstellationConfig.Enabled = true config.ConstellationConfig.SlaveMode = true - config.ConstellationConfig.DNSDisabled = false + config.ConstellationConfig.DNSDisabled = true // ConstellationHostname = // output utils.CONFIGFOLDER + "nebula.yml" diff --git a/src/constellation/index.go b/src/constellation/index.go index 6f6bbba..6fffcbd 100644 --- a/src/constellation/index.go +++ b/src/constellation/index.go @@ -3,10 +3,22 @@ package constellation import ( "github.com/azukaar/cosmos-server/src/utils" "os" + "time" ) func Init() { var err error + + // if date is > 1st of January 2024 + timeNow := time.Now() + if timeNow.Year() > 2024 || (timeNow.Year() == 2024 && timeNow.Month() > 1) { + utils.Error("Constellation: this preview version has expired, please update to use the lastest version of Constellation.", nil) + // disable constellation + configFile := utils.ReadConfigFromFile() + configFile.ConstellationConfig.Enabled = false + utils.SetBaseMainConfig(configFile) + return + } // if Constellation is enabled if utils.GetMainConfig().ConstellationConfig.Enabled { From 1f7c588a2b19c633352004b4326da796a182e64b Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Fri, 6 Oct 2023 13:34:03 +0100 Subject: [PATCH 25/36] [release] v0.10.0-unstable21 --- client/src/api/constellation.tsx | 34 ++++++++++++++++++++------ client/src/pages/constellation/vpn.jsx | 4 ++- package.json | 2 +- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/client/src/api/constellation.tsx b/client/src/api/constellation.tsx index 006037f..36774c0 100644 --- a/client/src/api/constellation.tsx +++ b/client/src/api/constellation.tsx @@ -57,13 +57,33 @@ function getLogs() { } function connect(file) { - return wrap(fetch('/cosmos/api/constellation/connect', { - method: 'POST', - headers: { - 'Content-Type': 'application/yaml' - }, - body: file, - })) + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = () => { + fetch('/cosmos/api/constellation/connect', { + method: 'POST', + headers: { + 'Content-Type': 'text/plain', + }, + body: reader.result, + }) + .then(response => { + // Add additional response handling here if needed. + resolve(response); + }) + .catch(error => { + // Handle the error. + reject(error); + }); + }; + + reader.onerror = () => { + reject(new Error('Failed to read the file.')); + }; + + reader.readAsText(file); + }); } function block(nickname, devicename, block) { diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx index d0913b3..289f38a 100644 --- a/client/src/pages/constellation/vpn.jsx +++ b/client/src/pages/constellation/vpn.jsx @@ -147,7 +147,9 @@ export const ConstellationVPN = () => { OnChange={async (e) => { let file = e.target.files[0]; await API.constellation.connect(file); - refreshConfig(); + setTimeout(() => { + refreshConfig(); + }, 1000); }} /> diff --git a/package.json b/package.json index 916009f..f6e5666 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable20", + "version": "0.10.0-unstable21", "description": "", "main": "test-server.js", "bugs": { From 4f7421e64191269c133d488c2a1b476d7cb3a81c Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Fri, 6 Oct 2023 13:55:03 +0100 Subject: [PATCH 26/36] [release] v0.10.0-unstable22 --- client/src/pages/constellation/vpn.jsx | 3 +++ package.json | 2 +- src/constellation/nebula.go | 24 ++++++++++++++++++------ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx index 289f38a..f74a4da 100644 --- a/client/src/pages/constellation/vpn.jsx +++ b/client/src/pages/constellation/vpn.jsx @@ -89,6 +89,9 @@ export const ConstellationVPN = () => { newConfig.ConstellationConfig.PrivateNode = values.PrivateNode; newConfig.ConstellationConfig.NebulaConfig.Relay.AMRelay = values.IsRelay; newConfig.ConstellationConfig.ConstellationHostname = values.ConstellationHostname; + setTimeout(() => { + refreshConfig(); + }, 1500); return API.config.set(newConfig); }} > diff --git a/package.json b/package.json index f6e5666..629e438 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable21", + "version": "0.10.0-unstable22", "description": "", "main": "test-server.js", "bugs": { diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index fa4fa7b..ac20c16 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -108,6 +108,13 @@ func ResetNebula() error { if err != nil { return err } + + config := utils.ReadConfigFromFile() + config.ConstellationConfig.Enabled = false + config.ConstellationConfig.SlaveMode = false + config.ConstellationConfig.DNSDisabled = false + + utils.SetBaseMainConfig(config) Init() @@ -194,6 +201,8 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st finalConfig.PKI.Blocklist = append(finalConfig.PKI.Blocklist, d.Fingerprint) } + finalConfig.Lighthouse.AMLighthouse = !overwriteConfig.PrivateNode + // add other lighthouses finalConfig.Lighthouse.Hosts = []string{} for _, l := range lh { @@ -204,7 +213,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st finalConfig.Relay.Relays = []string{} for _, l := range lh { - if l.IsRelay && l.IsLighthouse { + if l.IsRelay { finalConfig.Relay.Relays = append(finalConfig.Relay.Relays, cleanIp(l.IP)) } } @@ -257,8 +266,10 @@ func getYAMLClientConfig(name, configPath, capki, cert, key, APIKey string, devi } if staticHostMap, ok := configMap["static_host_map"].(map[interface{}]interface{}); ok { - staticHostMap["192.168.201.1"] = []string{ - utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242", + if !utils.GetMainConfig().ConstellationConfig.PrivateNode { + staticHostMap["192.168.201.1"] = []string{ + utils.GetMainConfig().ConstellationConfig.ConstellationHostname + ":4242", + } } for _, l := range lh { @@ -274,10 +285,11 @@ func getYAMLClientConfig(name, configPath, capki, cert, key, APIKey string, devi if lighthouseMap, ok := configMap["lighthouse"].(map[interface{}]interface{}); ok { lighthouseMap["am_lighthouse"] = device.IsLighthouse - lighthouseMap["hosts"] = []string{ - "192.168.201.1", + lighthouseMap["hosts"] = []string{} + if !utils.GetMainConfig().ConstellationConfig.PrivateNode { + lighthouseMap["hosts"] = append(lighthouseMap["hosts"].([]string), "192.168.201.1") } - + for _, l := range lh { if cleanIp(l.IP) != cleanIp(device.IP) { lighthouseMap["hosts"] = append(lighthouseMap["hosts"].([]string), cleanIp(l.IP)) From a3df4704c4b1ef0f1c7933e5a50c18521560847b Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Fri, 6 Oct 2023 16:58:30 +0100 Subject: [PATCH 27/36] [release] v0.10.0-unstable23 --- changelog.md | 2 + client/src/pages/config/routes/routeman.jsx | 34 +++++++++++++- client/src/pages/constellation/dns.jsx | 4 +- package.json | 2 +- src/proxy/routeTo.go | 25 +++++++--- src/proxy/routerGen.go | 2 +- src/utils/middleware.go | 51 ++++++++++++++++----- src/utils/types.go | 2 + 8 files changed, 99 insertions(+), 23 deletions(-) diff --git a/changelog.md b/changelog.md index bda6e3c..3ad1a4d 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,8 @@ - Rework headers for better compatibility - Improve experience for non-admin users - Fix bug with redirect on logout + - Added OverwriteHostHeader to routes to override the host header sent to the target app + - Added WhitelistInboundIPs to routes to filter incoming requests based on IP per URL > **Note: If you use the ARM (:latest-arm) you need to manually update to using the :latest tag instead** diff --git a/client/src/pages/config/routes/routeman.jsx b/client/src/pages/config/routes/routeman.jsx index 83349da..1962ba9 100644 --- a/client/src/pages/config/routes/routeman.jsx +++ b/client/src/pages/config/routes/routeman.jsx @@ -11,7 +11,7 @@ import { FormHelperText, } from '@mui/material'; import RestartModal from '../users/restart'; -import { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../users/formShortcuts'; +import { CosmosCheckbox, CosmosCollapse, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../users/formShortcuts'; import { CosmosContainerPicker } from '../users/containerPicker'; import { snackit } from '../../../api/wrap'; import { ValidateRouteSchema, sanitizeRoute } from '../../../utils/routes'; @@ -73,12 +73,19 @@ const RouteManagement = ({ routeConfig, routeNames, config, TargetContainer, noC AuthEnabled: routeConfig.AuthEnabled, _SmartShield_Enabled: (routeConfig.SmartShield ? routeConfig.SmartShield.Enabled : false), RestrictToConstellation: routeConfig.RestrictToConstellation, + OverwriteHostHeader: routeConfig.OverwriteHostHeader, + WhitelistInboundIPs: routeConfig.WhitelistInboundIPs.join(', '), }} validationSchema={ValidateRouteSchema} onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => { if(!submitButton) { return false; } else { + let commaSepIps = values.WhitelistInboundIPs; + if(commaSepIps) { + values.WhitelistInboundIPs = commaSepIps.split(',').map((ip) => ip.trim()); + } + let fullValues = { ...routeConfig, ...values, @@ -263,6 +270,31 @@ const RouteManagement = ({ routeConfig, routeNames, config, TargetContainer, noC label="Restrict access to Constellation VPN" formik={formik} /> + + + + These settings are for advanced users only. Please do not change these unless you know what you are doing. + + + + This setting will filter out all requests that do not come from the specified IPs. + This requires your setup to report the true IP of the client. By default it will, but some exotic setup (like installing docker/cosmos on Windows, or behind Cloudlfare) + will prevent Cosmos from knowing what is the client's real IP. If you used "Restrict to Constellation" above, Constellation IPs will always be allowed regardless of this setting. + + + + + {submitButton &&