From 9295e147873872ac518b3472cdb6dc95f0def72c Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Sun, 9 Nov 2025 21:13:30 -0600 Subject: [PATCH 1/3] flake: Add crowdsec flake --- flake.lock | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 10 ++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/flake.lock b/flake.lock index ced1ca7..a1cd1d3 100644 --- a/flake.lock +++ b/flake.lock @@ -27,6 +27,27 @@ "type": "github" } }, + "crowdsec": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1752497357, + "narHash": "sha256-9epXn1+T6U4Kfyw8B9zMzbERxDB3VfaPXhVebtai6CE=", + "ref": "refs/heads/main", + "rev": "84db7dcea77f7f477d79e69e35fb0bb560232667", + "revCount": 42, + "type": "git", + "url": "https://codeberg.org/kampka/nix-flake-crowdsec.git" + }, + "original": { + "type": "git", + "url": "https://codeberg.org/kampka/nix-flake-crowdsec.git" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -48,6 +69,23 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -200,6 +238,7 @@ "root": { "inputs": { "agenix": "agenix", + "crowdsec": "crowdsec", "home-manager": "home-manager", "nix-darwin": "nix-darwin", "nixpkgs": "nixpkgs", @@ -247,6 +286,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index c792d2b..735b5b9 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,10 @@ }; # Linux-specific inputs + crowdsec = { + url = "git+https://codeberg.org/kampka/nix-flake-crowdsec.git"; + inputs.nixpkgs.follows = "nixpkgs"; + }; plasma-manager = { url = "github:nix-community/plasma-manager"; inputs = { @@ -47,8 +51,8 @@ }; outputs = { self, nix-darwin, nixpkgs, nixpkgs-darwin, nixpkgs-unstable - , home-manager, agenix, rycee-nurpkgs, nur, plasma-manager, quadlet-nix, ... - }: + , home-manager, agenix, rycee-nurpkgs, nur, crowdsec, plasma-manager + , quadlet-nix, ... }: let mkHomeConfiguration = { hostname, arch ? "x86_64", os ? "linux" , desktop ? false, extraModules ? [ ] }: @@ -142,6 +146,8 @@ ./systems/linux/mcentire.nix agenix.nixosModules.default quadlet-nix.nixosModules.quadlet + crowdsec.nixosModules.crowdsec + crowdsec.nixosModules.crowdsec-firewall-bouncer ]; }; }; From 3d0c03efb0b013e982b461afc9b859029ed492d9 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Sun, 9 Nov 2025 23:22:37 -0600 Subject: [PATCH 2/3] secrets: Remove bosephus root from administrators --- secrets.nix | 3 --- secrets/ansible-vault-password.age | 26 ++++++++++++-------------- secrets/darwin-policies-json.age | Bin 944 -> 834 bytes secrets/network-information.age | Bin 1017 -> 907 bytes secrets/pihole.age | Bin 1036 -> 926 bytes 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/secrets.nix b/secrets.nix index cb1d980..3b3ede4 100644 --- a/secrets.nix +++ b/secrets.nix @@ -4,8 +4,6 @@ let "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM8G6okW/vpl3DTBwL64aPb+oxJsr2Wl6KzHYsLPecBc millironx@millironx.com"; bosephus-host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIxTfeg+GZsfmG8TuEV1xW1gXknAIKzZ3UjZ3guRY+EW root@nixos"; - bosephus-root = - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFFl4zOdlKkpoccPZTX8195068gJVhylvV9pUYxy2kM+ root@bosephus"; bosephus-millironx = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKaDPqRJHoqgY2pseh/mnhjaGWXprHk2s5I52LhHpHcF millironx@bosephus"; odyssey-millironx = @@ -17,7 +15,6 @@ let system-administrators = [ anderson-millironx - bosephus-root bosephus-millironx odyssey-millironx corianne-millironx diff --git a/secrets/ansible-vault-password.age b/secrets/ansible-vault-password.age index d0be48c..99a96bc 100644 --- a/secrets/ansible-vault-password.age +++ b/secrets/ansible-vault-password.age @@ -1,15 +1,13 @@ age-encryption.org/v1 --> ssh-ed25519 il3lzQ oDA/rl4XZJY+vIIdnMBaAuSMD+DnMX2n6+B3geHw/1Q -Jpv2pN6KwJAHgwWBbAAgmGVrZeO+wLmuwFRpJLsUDcU --> ssh-ed25519 bN6E9A jKvJPR87Eojde1aq2FFxRj+cxy+0S7Eix5JwRPRpX34 -48eC+KdOBcIGU0y2ui4iq+g8K9SG7qc3U60ApLU+w+Q --> ssh-ed25519 1g/xww UiK5nDMJg+tTc7zdE5zlEXmoBPE5dV2EpHxvhWBENmU -ljQEJ+tiZPdFrpiZER5EOIsdnhpj05EKryhzm0vM3LU --> ssh-ed25519 +kBihw ZIcwOBbRMJ8jiu9Vcq8BvGyOT1xuqG+Mf/DXUHMeCAA -wlXcO4kQpHmka49CARH0xvm/Lh0AcQ1j/bPx12wZVBY --> ssh-ed25519 dbKeHw +G44jyudYu9opDuMcTs05j5Ha91m9lm5g551uIAACEk -9DXJxDc2L3PDCAi3cLfVajqPseaxmBpb+Uo3AW2R05I --> ssh-ed25519 Svnssw PZN+FpZsFnCqerEgW7B4RFHo7iumlUXL4pYt54/XxjM -bKrgyqBpIMnntB0CHA560TvraQE9bPF06oOXR+wocIA ---- RW8fUuT62TXXKS8k8MgKISzwORr/3hEl0XK2XSZFzpA -W: iZSth ՞0JEgPT#8-'ȰL8kp \ No newline at end of file +-> ssh-ed25519 il3lzQ Ni2CHjeijXHfF62cUqVTm8MAOn6rRg8UrhqN6xvdkyk +DsT0Ysx88FlCLeRzoOGctX7KqatX9/UCr5WdtdLJAf4 +-> ssh-ed25519 1g/xww jRn91F29sISMyi41anAlzVCzt1t1DnUqxtryqkTQPlM +cysgZLQR0YhiJYXBl59DjKbm+N8FnjA46wkQtnAzBFA +-> ssh-ed25519 +kBihw t6wlSnDKGgSzGhNJnryXVbDR40DATaV3fHovtI/u7zo +zOyCZtzfLKeer9K6SMpfTxn6El4HB7gQFQqLOxIYB5U +-> ssh-ed25519 dbKeHw cn+8WTwis58bYm2pfEra6LeLvzEZ8GhZrOEeN+kkhCM +fnlUAj8JtG8+r7Cj8xYUgF+JM6Pwqawn4sGI1LOeN78 +-> ssh-ed25519 Svnssw zmDBR8TdRZ9NzNhwPYRN6c8naTxAkULyUZpKgk7Gshk +0XCwpegEIlGXhnzLLUtmciKQiYiZRgnSOSvCcYeXXk8 +--- D/lZ36n5sVste2NWfdOx8/klPh0CTmMjVQN74KIqDRY +]%C}NO"v#˱t_Q;^*!+<+dB/K` \ No newline at end of file diff --git a/secrets/darwin-policies-json.age b/secrets/darwin-policies-json.age index e1a202f79127d3ce74c514251d3c5003beedce5d..9fa2d2bb8cc42f93a14b737c82f652ccaa1914d0 100644 GIT binary patch delta 745 zcmV~$S!mM$002;Bpit)tI>dp)6Rfq(lJo$t98KG_X&T!!O<+o!CTY{OO`E1o!er{7 zet69Zf+FG+71Zg+KpDbBL=Zp5yr&HJp@RMlQAF{0Z*1|yMJKm&VN=Fo$3iW{Y=j6~ z*heA&S>V%UFwX^Y2@6o$MFNHcSXy)y+)g{{(+w+N5Fnc5Or)cxDfd2G#`+ZxYL$gY5y6g|(`J!M9A6VGKHCYjA+pJOwCrfY;D%Me>QUHTG z5zx_uR13wke4?r`0EH+n(X9YDz$39hQo{3{Rt`fkhILph7B5%Dawp=hASjxxB&hK4 zvxPlFqE1E=;R?urStkHw36V!SjY2Bk0(C;ygtSb1b)%GlWt4Ks8HKd_IV-8l5aWyI zDSm3mxS~QmS%`VdWhMu!zADYbcFV6cw0y(QChC5f zYOq;M;o(q-cX>RXJ`yYi0k##7kqVTL7yY1Qw^6g2CgGwxsKuyC1Zm0nEE{3!u2lBo z@lOX=-5MI(xNPG{7xQHP{2}(}z@@LhNp?};%FdZib#Uqx{fk-MMQ!~#Q8_a5ZDl|V zP28CL@a@4pxOe30-IjfL^7fvmgR^JM@#5{dEh~?mUZ7Iq$L?J>d!1|l&fmfRtY5Zw z-YGqre>eT|-1oJ;gD1XhSiV`nkB}EG6D+u5U~U1;ZTl>3dc%yao$fjv3?EzfY=Svd zo1Tj3&xdFBU-+6{GyXgGxcB6aPxoKH`1@mrHP*eHU! z(ueiQ2W1TB4X5G<>^60xcoT%p3A(YDDVwqfaiCA9$h{_1VAR_wc*4b#2?r z&rW4pX^B=QSxDxn3dtmK#Zm~wsnME@D@v6?v6}4FIf>*MAfmafF`oEbfGPz|rA3__ z)f#nn2&y*Jf;loh=qF)`;YSXT+MDn^Bc*jRFr3B`Efy(Yc!KicM(nFhKN%I!GQql2 z7PnnW#IT~AA5*zy9U2UM5RO901NvEP*f(?e$nOoYaSl;P7Ab}tmo`Gk$_`YBBHEOX z1$0sUKbO|QC=3&^L%Ma_*J2jR^!QS#)@{MPc89M8qR1h*N(gY*P^=ox8afbG0#@mC zTn_g;waPeIru$>oLt&=aMZF3U%T%UUjTNJ$s>klk$Q2=qb1p6t%ZMIFW)&~lri8RT zSP#sQj_9q7_GC z6}5OFSN1)!ZzB?4<{D!i75rhSu3X(RGv$~p(hYpVlc^*sz>$)qX(G~k(Ks?m&8BeF zi3gP7PDmc;%A!wqT!{dCLxzS;TENnX+|cTyWFtcO3XIC5ih#sW>A&z)D&b7p#Gue_ zEbuZzm^AE^L&|P<89l#bxm>O&<((5ky{-SW7K`N*?UjY*&$ImI zI~VTk**JUt(zY-Cmwvy}JFZ{wVO|MPW4XzR=&FD!MnF^e7g7Hb))&fi)VNL E3#eB<3;+NC diff --git a/secrets/network-information.age b/secrets/network-information.age index 66c199cda8321ed6cc6a0a464ebf26e80c5123f1..0de69b7ec6d1ce8f3a29567c6da2e54b876b8b17 100644 GIT binary patch literal 907 zcmZY4&8yo4003}hw+K!_dB_m5gD8ZWHffsV;2X(nzMH1$N16l`(|k8=(llw3<}mOy za3b<$*mQU<wd2S%Ut05(o~u z9E5TOL#YqU!J6)2=Y#NSD#8kK4I+Rx`=XNQ>Bnny6|95j{lFOH17v*D$qieLy>xu3J z&=AHE7#`rraO^L85}W&o8%>ZpLii3W|CndwIe3ta8f>$c7O-8zvIa0E+Nr_q=BLVIUthcbUnj!o$sZ|nNz}eOS_8bx8y9BN< zaMz^xHe+*GyYl@0sOu;-o}*DpxSS_jTyj*K&ze+E1(&iz;l1&k8+lB*rA5yr$|*^0 zD7qCGZIg9bJf($x!D~%Gh0HwNE6^0}SUi-X>&|R!leljbEYlh`vm`bmbTqa-yA3oN z4Um@*L?euu+Klwg+)Jya*c;QS3tNJSNz!JE5fNm~3ISn&M>p?XetADX{?g9fQ!f_Z zJpXq7XSHAx_FR#)k zy;GOeiyh)Qlsc0<_BH*PaN@@shX+FZ>QmpEAK!ey`z1arvnSKzZ~ir+6Acval65_vnoZ`MGrv~nuKFq zob@ayZ3w7A>tQhE0n!>xCkU(RLBcGS-DX5aGB++t)K4r`6}B}~B601e0bq?3f)Yx7*3KRt?+!98{))*#@~$mcV95ld2=T zNp2yBu4_7Dfcs7X6E?$U{2YL|tB~&w*}%i0Rh{RT3G_ zLTp>J&4I&!$|zo_tPyyGQHx?C7$aA&Q+9#?(T)?ZC>pX=4Hzo|tq^R2i-1 zu#VzD=e3-n;7}CYWz$q)m+mb$#6oa*WLq1p<1_`8JR3%hp-y9j$@x=VaYn&JQzey# zxw;c)jM3nS3qKL;xdDNA%^e24Lu)FTDc-dWS@f)&NZOOJq$FNdwuW&!P&BwnZ)<^x zP`pwxqC&%%=|(FpPoMQyzO@cE+^CRQ8>dp);u8nslENHQA>=5@T+z5pqMbFcBZVP~ zWxqP;F6128j9|rbtRR%m4tN07YPCujEyfB>eeupK^OM2rSFaswAAfiJ&Dk5jUS{tfefjX>n>!EAzodRK_zUb_esUjr z9Qv1Tee(C8x9`0z`G5RQpWOXyZ?APrf9rtVpT6_$zx(6s2ht0~4_7Xpo^!80YG3=P n^X%!TSHALhzjywrz5C(w?{yv>TsS@1eJ=R%`q6<(lu!QyN9SaQ diff --git a/secrets/pihole.age b/secrets/pihole.age index e7f0c78b2569b6f3ace0c9aa98cf8edfa4319a35..babead9c0fb07c98baceb5bcbb57ce3194d346de 100644 GIT binary patch literal 926 zcmZY4%gfsY003}?J80N9bvg#(p)wHnWi`#KZNOL3=Fzlio2Jd99!6eGnxsk7v`HG! z!^YEkQLG?opw2Oalr{oZUigu$$7BGJlWMkThE)XAZevgB>W(vQj@* z^I_W2nd&$&fMv=L#X|3n*HM9Fsnb@$)|l0M zbjZUwo92BjkKu~GfJXZ@(5I$uRua{2t`R-aZV5!a7tgpzha|)o#<8S=q2=W4y1~ym z9&{YQK%2A4D9P4+u|gzsyq2P((4`rut)m$sdPoBM>`0o1GjYFW8Eu=(N;q41ZO0p_ zxLxhq%UT*K)Y@;rwPIje64PXiW+G*CoFPX5n+<6P)dmD-nIhMBK-KFb9S5f7rb1%! zDm2CtW#zK2-E@=EFiEn~bT;ssc!OU-UQajCB}Dc)L!-iE&C8-?8-r>aN60E`Wdaa_ zBH`1tuMMabT_}N+86MwFJf^;C2a!FZ6)opc$k!Pl;h*^rph%-`!I;ZO1az^SxxNvbI0d6@2-u{q$! z4UjW$pFD75_nG&$A2^O7e;nUDd7E+fKfBNU^5&I0xF1gyTW7bnzu0)<`>j_m{C?n- z{MetTYvkGco`#1T$om+n*tK9p|-l3iMi?6}YF5a{AZsWU+ug+Z)FWz|P6L$A4_2ZZP u*PP8ae!jj@K63b)cV_Ry({~0}{zb1o3xCPKeCfY~!UsM2smBjLEd39L>qy!F literal 1036 zcmZY7&#T)60Kjn(555i^!kojQ)E&mK)Fe&%3q(lsvq_pXO`9K2p4v8P+9XZV{B8un z=~LwCMA-A-JSY?P<1m>X9L%|!L*X%0m`wD&?cza%IZ?bgr|0Ki@Hu?HL(?c5?l@Tb z(adw7@&Xe`phEeXVi-=#hF+;wp=NRBz)mg}tw0VGvXRgA%8X_#o$?6?ANakhNi7}3 ztzvbq@0V<-1&<5pD5{1qRxQDDV=}J!LeI9-Y)Ql8AQDlpQ3+7Q$Jth}8t&Fce2r+L z#a1fvN^W?JO4>2I9MEA?iGpD>R+oV!iFPhi!qQ7L9UT{ZbUIe)R7w8fDbUm4gf~em5kK6R7e76yjv^Tt=Tj!Dnoz_ zsWiq*LKo@dy(K!R8wsm9LDH@>xnp&S$p9ugx!Nv}U00Q&8c0KqlBl7V$%*3{u|Ap1 zXPFm_k$#E8TC`&J(A}CoVhuViHnmWV__hv;nU_cjC=)6kE|Ny>ciRjM#XUPCyjT)y zSX$tCY{)}t2FNBy5~!aL9j?R8`eGg|@MhkuNqVaZK+d2o?$)Hl4a2m^L81)MtF1)@ zOm&w42NB4-Qh>wr-lFSvAwH?r5nO8%AW@)lh=WVjw$^tUxrG74i9ie?StbH@2y!K# zwEGpP6y>)0ze}tUh;m{PHrzTN%M!s!U9`teR+ENPQG$7wVKO6|Aev!K6TUhe4Z=bS zHQSods|0Cl;WTOfzXWq|=2|4VI%7P`6Qv#w#SEuOX`x&$7XidjdsRDJ5J*JZh!mJY zzzP({T5w6tR~gJQre!>YuER#JuIAh|33A} zwY#Ub|NQpyM*EeUZ@hW>)dPn<-Pwqa_~&n*d}-xHV;gzvj=uNxkIvBFtl#Ai`%ipv z|DNM#hhNn$#o*07w~oDc^2F2b1M{C&Avpzjg=o>+kdNj bW&gXApU Date: Sun, 9 Nov 2025 23:45:04 -0600 Subject: [PATCH 3/3] feat: Add Crowdsec modules --- flake.nix | 1 + services/crowdsec.nix | 90 ++++++++++++++++++++++++++++++++++++++ systems/linux/mcentire.nix | 1 + 3 files changed, 92 insertions(+) create mode 100644 services/crowdsec.nix diff --git a/flake.nix b/flake.nix index 735b5b9..a5b90d9 100644 --- a/flake.nix +++ b/flake.nix @@ -148,6 +148,7 @@ quadlet-nix.nixosModules.quadlet crowdsec.nixosModules.crowdsec crowdsec.nixosModules.crowdsec-firewall-bouncer + { nixpkgs.overlays = [ crowdsec.overlays.default ]; } ]; }; }; diff --git a/services/crowdsec.nix b/services/crowdsec.nix new file mode 100644 index 0000000..f7e2e5a --- /dev/null +++ b/services/crowdsec.nix @@ -0,0 +1,90 @@ +{ pkgs, config, ... }: +let + crowdsec-url = "127.0.0.1:2763"; + firewall-bouncer-name = "fw-bouncer"; + # Although this key can be reproduced by anyone who actually cares to, the + # Crowdsec API will not be exposed to the outside world, so keeping this key + # super secret really isn't that important to me. Still make it look random + # so that hungry botnets can't just slurp up the password in plaintext. + firewall-bouncer-key = builtins.hashString "sha256" + "${config.networking.hostName}-crowdsec-bouncer-salt"; + toMultiYAML = items: + pkgs.lib.concatMapStrings (item: + '' + + --- + '' + (pkgs.lib.generators.toYAML { } item) + "\n") items; +in { + services = { + crowdsec = { + enable = true; + settings = { + api.server = { listen_uri = crowdsec-url; }; + allowLocalJournalAccess = true; + crowdsec_service.acquisition_path = pkgs.writeText "acquisitions.yaml" + (toMultiYAML [ + { + source = "journalctl"; + journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; + labels.type = "syslog"; + } + { + filenames = [ "/var/log/auth.log" ]; + labels.type = "syslog"; + } + { + filenames = [ "/var/log/syslog" "/var/log/kern.log" ]; + labels.type = "syslog"; + } + ]); + }; + }; + crowdsec-firewall-bouncer = { + enable = true; + settings = { + api_url = firewall-bouncer-name; + api_key = firewall-bouncer-key; + }; + }; + }; + + systemd.services.crowdsec.serviceConfig = { + ExecStartPre = let + bouncer-script = pkgs.writeScriptBin "register-bouncer" '' + #!${pkgs.runtimeShell} + set -eu + set -o pipefail + + if ! cscli bouncers list | grep -q "${firewall-bouncer-name}"; then + cscli bouncers add "${firewall-bouncer-name}" --key "${firewall-bouncer-key}" + fi + ''; + collection-check = collection: '' + + if ! cscli collections list | grep -q "${collection}"; then + cscli collections install "${collection}" + fi + + ''; + collections = [ + "crowdsecurity/base-http-scenarios" + "crowdsecurity/http-cve" + "crowdsecurity/http-dos" + "crowdsecurity/iptables" + "crowdsecurity/linux" + "crowdsecurity/sshd" + "crowdsecurity/whitelist-good-actors" + ]; + collection-script = pkgs.writeScriptBin "install-collections" '' + #!${pkgs.runtimeShell} + set -eu + set -o pipefail + + ${pkgs.lib.concatMapStrings collection-check collections} + ''; + in [ + "${bouncer-script}/bin/register-bouncer" + "${collection-script}/bin/install-collections" + ]; + }; +} diff --git a/systems/linux/mcentire.nix b/systems/linux/mcentire.nix index 6ee0ae4..07135d1 100644 --- a/systems/linux/mcentire.nix +++ b/systems/linux/mcentire.nix @@ -4,6 +4,7 @@ imports = [ # Include the results of the hardware scan. ./hardware-configuration/mcentire.nix ./../../services/nixos-update.nix + ./../../services/crowdsec.nix ]; # Use the GRUB 2 boot loader.