From 36bf83c96b83458a049c20eb0ce4c0c156214250 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Sat, 20 Apr 2024 23:21:04 +0200 Subject: [PATCH 01/16] carotte: cross-compile more the graphical profile --- nixos/profiles/hardware/cubieboard2.nix | 3 +- nixos/profiles/systems/crossCompilation.nix | 51 ++------------------- 2 files changed, 7 insertions(+), 47 deletions(-) diff --git a/nixos/profiles/hardware/cubieboard2.nix b/nixos/profiles/hardware/cubieboard2.nix index 00a5829..f5bb633 100644 --- a/nixos/profiles/hardware/cubieboard2.nix +++ b/nixos/profiles/hardware/cubieboard2.nix @@ -99,6 +99,7 @@ boot.loader.generic-extlinux-compatible.enable = true; # nix -L build .#nixosConfigurations.${hostName}.config.boot.kernelPackages.kernel.configfile boot.kernelPackages = lib.mkForce ( + # FIXME: config.boot.zfs.package.latestCompatibleLinuxPackages pkgs.linuxPackages_latest.extend (finalKernel: previousKernel: { kernel = previousKernel.kernel.override { defconfig = "sunxi_defconfig"; @@ -116,7 +117,7 @@ REGULATOR = lib.mkForce no; MFD_CORE = no; PCI = yes; - # TODO: Enable wdctl to work when /dev/watchdog is used by systemd + # FIXME: Enable wdctl to work when /dev/watchdog is used by systemd #WATCHDOG_SYSFS = yes; # diff --git a/nixos/profiles/systems/crossCompilation.nix b/nixos/profiles/systems/crossCompilation.nix index fbde13a..63099ab 100644 --- a/nixos/profiles/systems/crossCompilation.nix +++ b/nixos/profiles/systems/crossCompilation.nix @@ -1,52 +1,11 @@ -{ lib, ... }: { nixpkgs.overlays = [ - (final: super: { - gnupg = super.gnupg.override { - # Wants polkit which wants spidermonkey (slow to compile) - pcsclite = final.hello; - }; - systemd = super.systemd.override { - # Wants tpm2-tss which does not cross-compile - withTpm2Tss = false; - # Does not cross-compile :( - withEfi = false; - }; - # https://logs.nix.samueldr.com/nixos/2019-07-23#2416964; - xorg = super.xorg.overrideScope (_ofinal: _osuper: { - fontadobe100dpi = final.hello; - fontadobe75dpi = final.hello; - fontcursormisc = final.hello; - fontmiscmisc = final.hello; - }); - # Perl's ModuleBuild does not cross-compile - # https://github.com/NixOS/nixpkgs/issues/66741#issuecomment-944831760 - xdg-utils = final.hello; - procmail = final.hello; - noto-fonts-emoji = final.hello; - x11_ssh_askpass = final.hello; - dconf = super.dconf.overrideAttrs (_old: { - doCheck = false; - }); - # Need gobject-instrospection which does not cross-compile - arandr = final.hello; - # Depends on judy which does not cross-compile - stress-ng = final.hello; - hwinfo = final.hello; - # Fails to cross-compile: perl5.34.0-IO-Tty-armv7l-unknown-linux-gnueabihf - mosh = final.hello; - # error: Error loading target specification: - # Could not find specification for target "armv7l-unknown-linux-gnueabihf". - # Run `rustc --print target-list` for a list of built-in targets - gparted = final.hello; + (finalPkgs: previousPkgs: { + # libx86emu-armv7l-unknown-linux-gnueabihf> /nix/store/r9h133c9m8f6jnlsqzwf89zg9w0w78s8-bash-5.2-p15/bin/bash: line 1: gcc: command not found + hwinfo = finalPkgs.hello; + # x11-ssh-askpass-armv7l-unknown-linux-gnueabihf> /build/imakeUMfSVM: line 1: syntax error: unexpected word (expecting ")") + x11_ssh_askpass = finalPkgs.hello; }) ]; - #environment.noXlibs = true; - fonts.fontconfig.enable = false; - # lesspipe does not cross-compile - programs.less.enable = lib.mkForce false; - programs.mosh.enable = false; - programs.traceroute.enable = false; security.apparmor.enable = false; - services.udisks2.enable = false; } -- 2.47.2 From 8ca525ebdab60602ac2d34320c54e402441d5d4a Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Sat, 20 Apr 2024 23:22:24 +0200 Subject: [PATCH 02/16] fail2ban: tweak parameters --- hosts/mermet/fail2ban.nix | 2 +- nixos/profiles/services/fail2ban.nix | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hosts/mermet/fail2ban.nix b/hosts/mermet/fail2ban.nix index a5d883c..e99c307 100644 --- a/hosts/mermet/fail2ban.nix +++ b/hosts/mermet/fail2ban.nix @@ -21,7 +21,7 @@ enabled = true; bantime = "5m"; filter = "postfix"; - findtime = "1d"; + findtime = "10d"; mode = "aggressive"; port = 465; }; diff --git a/nixos/profiles/services/fail2ban.nix b/nixos/profiles/services/fail2ban.nix index b346800..3b926ec 100644 --- a/nixos/profiles/services/fail2ban.nix +++ b/nixos/profiles/services/fail2ban.nix @@ -8,7 +8,6 @@ enable = true; factor = "1"; formula = "ban.Time * (1 << min(ban.Count, 20)) * banFactor"; - maxtime = "1y"; overalljails = false; rndtime = ""; }; -- 2.47.2 From 57cbdc53eb805acd5bb4d375d96ef36c7df7f53c Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Wed, 22 May 2024 23:13:02 +0200 Subject: [PATCH 03/16] mermet: croc: disable service --- hosts/mermet.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/mermet.nix b/hosts/mermet.nix index 5530923..af11093 100644 --- a/hosts/mermet.nix +++ b/hosts/mermet.nix @@ -9,7 +9,7 @@ (inputs.julm-nix + "/nixos/profiles/networking/remote.nix") mermet/acme.nix mermet/calibre.nix - mermet/croc.nix + # mermet/croc.nix mermet/coturn.nix mermet/dovecot.nix mermet/fail2ban.nix -- 2.47.2 From 2dc94b6c3a9e41fd67bd65bc4a6bed7c906394e7 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Thu, 23 May 2024 00:10:11 +0200 Subject: [PATCH 04/16] nix: rename pre-commit-hooks => git-hooks --- flake.lock | 112 +++++++++++++++++++---------------------------------- flake.nix | 6 +-- 2 files changed, 42 insertions(+), 76 deletions(-) diff --git a/flake.lock b/flake.lock index 3458b4d..cb53c71 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -32,21 +32,30 @@ "type": "github" } }, - "flake-utils": { + "git-hooks": { "inputs": { - "systems": "systems" + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "julm-nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "julm-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1685518550, - "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "lastModified": 1716213921, + "narHash": "sha256-xrsYFST8ij4QWaV6HEokCUNIZLjjLP1bYC60K8XiBVA=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "0e8fcc54b842ad8428c9e705cb5994eaf05c26a0", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "cachix", + "repo": "git-hooks.nix", "type": "github" } }, @@ -54,16 +63,16 @@ "inputs": { "nixpkgs": [ "julm-nix", - "pre-commit-hooks", + "git-hooks", "nixpkgs" ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -80,11 +89,11 @@ ] }, "locked": { - "lastModified": 1712386041, - "narHash": "sha256-dA82pOMQNnCJMAsPG7AXG35VmCSMZsJHTFlTHizpKWQ=", + "lastModified": 1715381426, + "narHash": "sha256-wPuqrAQGdv3ISs74nJfGb+Yprm23U/rFpcHFFNWgM94=", "owner": "nix-community", "repo": "home-manager", - "rev": "d6bb9f934f2870e5cbc5b94c79e9db22246141ff", + "rev": "ab5542e9dbd13d0100f8baae2bc2d68af901f4b4", "type": "github" }, "original": { @@ -97,19 +106,19 @@ "julm-nix": { "inputs": { "doom-emacs": "doom-emacs", + "git-hooks": "git-hooks", "home-manager": "home-manager", "nix-formatter-pack": "nix-formatter-pack", "nixpkgs": [ "nixpkgs" - ], - "pre-commit-hooks": "pre-commit-hooks" + ] }, "locked": { - "lastModified": 1713647333, - "narHash": "sha256-ezqlRMLULyIORoSYY+yYbJKAOn87c7WFXTKD8pDgJWc=", + "lastModified": 1716411576, + "narHash": "sha256-7aqWzBks/3Ze6L+6AdCmlofhjZ8+l6C0ZPtjNa2ACQU=", "ref": "main", - "rev": "ea0dfb29c85d5c7d653b5f75c577e77a3f152591", - "revCount": 883, + "rev": "64a55f91a4c0c13ffc3dce40d7f7974704e3a9b2", + "revCount": 901, "type": "git", "url": "file:///home/julm/work/sourcephile/nix/julm-nix" }, @@ -190,40 +199,16 @@ "type": "gitlab" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils", - "gitignore": "gitignore", - "nixpkgs": [ - "julm-nix", - "nixpkgs" - ], - "nixpkgs-stable": [ - "julm-nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1692274144, - "narHash": "sha256-BxTQuRUANQ81u8DJznQyPmRsg63t4Yc+0kcyq6OLz8s=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "7e3517c03d46159fdbf8c0e5c97f82d5d4b0c8fa", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "doom-emacs": [ "julm-nix", "doom-emacs" ], + "git-hooks": [ + "julm-nix", + "git-hooks" + ], "home-manager": [ "julm-nix", "home-manager" @@ -233,26 +218,7 @@ "julm-nix", "nix-formatter-pack" ], - "nixpkgs": "nixpkgs", - "pre-commit-hooks": [ - "julm-nix", - "pre-commit-hooks" - ] - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" + "nixpkgs": "nixpkgs" } } }, diff --git a/flake.nix b/flake.nix index 04d4a35..c2491ad 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,7 @@ nix-formatter-pack.follows = "julm-nix/nix-formatter-pack"; #nixpkgs.follows = "julm-nix/nixpkgs"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; - pre-commit-hooks.follows = "julm-nix/pre-commit-hooks"; + git-hooks.follows = "julm-nix/git-hooks"; }; outputs = inputs: @@ -113,7 +113,7 @@ devShell = forAllSystems ({ pkgs, system, ... }: pkgs.callPackage ./shell.nix { inherit pkgs inputs system nixpkgsPath; - inherit (inputs.self.checks.${system}.pre-commit-check) shellHook; + inherit (inputs.self.checks.${system}.git-hooks-check) shellHook; }); # nix -L run .#mermet.switch @@ -191,7 +191,7 @@ # nix flake check checks = forAllSystems (args: with args; { - pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { + git-hooks-check = inputs.git-hooks.lib.${system}.run { src = inputs.self; hooks = { nixpkgs-fmt.enable = true; -- 2.47.2 From c75f187590adcc9dec0f8a9288b8437534c33002 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Thu, 23 May 2024 00:10:53 +0200 Subject: [PATCH 05/16] mermet: gitolite: update --- hosts/mermet/gitolite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/mermet/gitolite b/hosts/mermet/gitolite index b306ea2..ecf6f1e 160000 --- a/hosts/mermet/gitolite +++ b/hosts/mermet/gitolite @@ -1 +1 @@ -Subproject commit b306ea28c5f0a2b575aadd201104c06291572c35 +Subproject commit ecf6f1e89f830ba7cf53a5457383682b8ce5118f -- 2.47.2 From 5f3de2dea15600ed708ee8fac9bfa7fdbe209144 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Thu, 23 May 2024 00:11:13 +0200 Subject: [PATCH 06/16] mermet: matrirc: disable service --- hosts/mermet.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/mermet.nix b/hosts/mermet.nix index af11093..c7b1fb5 100644 --- a/hosts/mermet.nix +++ b/hosts/mermet.nix @@ -18,7 +18,7 @@ mermet/hardware.nix mermet/knot.nix # mermet/iodine.nix - mermet/matrirc.nix + # mermet/matrirc.nix mermet/miniflux.nix #mermet/mlmmj.nix #mermet/murmur.nix -- 2.47.2 From 68f05a09142d553d39656125bd56b4c51f554a9c Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Thu, 23 May 2024 13:23:51 +0200 Subject: [PATCH 07/16] mermet: radicle: nginx: add radicle-explorer Web site --- flake.lock | 168 ++++++++++++++++++++++++++- flake.nix | 23 ++-- hosts/mermet.nix | 1 + hosts/mermet/knot/sourcephile.fr.nix | 2 + hosts/mermet/radicle.nix | 25 ++++ 5 files changed, 203 insertions(+), 16 deletions(-) create mode 100644 hosts/mermet/radicle.nix diff --git a/flake.lock b/flake.lock index cb53c71..4c0c807 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,43 @@ { "nodes": { + "advisory-db": { + "flake": false, + "locked": { + "lastModified": 1714183630, + "narHash": "sha256-1BVft7ggSN2XXFeXQjazU3jN9wVECd9qp2mZx/8GDMk=", + "owner": "rustsec", + "repo": "advisory-db", + "rev": "35e7459a331d3e0c585e56dabd03006b9b354088", + "type": "github" + }, + "original": { + "owner": "rustsec", + "repo": "advisory-db", + "type": "github" + } + }, + "crane": { + "inputs": { + "nixpkgs": [ + "julm-nix", + "heartwood", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1715274763, + "narHash": "sha256-3Iv1PGHJn9sV3HO4FlOVaaztOxa9uGLfOmUWrH7v7+A=", + "owner": "ipetkov", + "repo": "crane", + "rev": "27025ab71bdca30e7ed0a16c88fd74c5970fc7f5", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "doom-emacs": { "flake": false, "locked": { @@ -32,6 +70,24 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "git-hooks": { "inputs": { "flake-compat": "flake-compat", @@ -81,6 +137,31 @@ "type": "github" } }, + "heartwood": { + "inputs": { + "advisory-db": "advisory-db", + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": [ + "julm-nix", + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1716379007, + "narHash": "sha256-3+10+g0qQ3Ivn56U32jqxaSMZKN/AtS+6Yy6p4UQBgo=", + "ref": "refs/heads/master", + "rev": "3403a66d0fc9c9cfab97eddaf5adeab40dc3bf23", + "revCount": 1973, + "type": "git", + "url": "https://seed.radicle.garden/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" + }, + "original": { + "type": "git", + "url": "https://seed.radicle.garden/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -107,18 +188,20 @@ "inputs": { "doom-emacs": "doom-emacs", "git-hooks": "git-hooks", + "heartwood": "heartwood", "home-manager": "home-manager", "nix-formatter-pack": "nix-formatter-pack", "nixpkgs": [ "nixpkgs" - ] + ], + "radicle-explorer": "radicle-explorer" }, "locked": { - "lastModified": 1716411576, - "narHash": "sha256-7aqWzBks/3Ze6L+6AdCmlofhjZ8+l6C0ZPtjNa2ACQU=", + "lastModified": 1716459584, + "narHash": "sha256-yKwo71IxijKjp3P14lBE5bZci79di/toJj8Qvnr/p4Q=", "ref": "main", - "rev": "64a55f91a4c0c13ffc3dce40d7f7974704e3a9b2", - "revCount": 901, + "rev": "a851689001c69d66159a35a61e72bc582c0b0ca4", + "revCount": 902, "type": "git", "url": "file:///home/julm/work/sourcephile/nix/julm-nix" }, @@ -199,6 +282,39 @@ "type": "gitlab" } }, + "radicle-explorer": { + "inputs": { + "flake-utils": [ + "julm-nix", + "radicle-explorer", + "heartwood", + "flake-utils" + ], + "heartwood": [ + "julm-nix", + "heartwood" + ], + "nixpkgs": [ + "julm-nix", + "radicle-explorer", + "heartwood", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1716392706, + "narHash": "sha256-xN2Uzsfid7jdNCmE3aZ2A0l0Rhbai5XrtJ70p+n+bhc=", + "ref": "refs/heads/master", + "rev": "77cc3ce031dc261c25cde9ab88bf187eea33f433", + "revCount": 1796, + "type": "git", + "url": "https://seed.radicle.garden/z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git" + }, + "original": { + "type": "git", + "url": "https://seed.radicle.garden/z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git" + } + }, "root": { "inputs": { "doom-emacs": [ @@ -220,6 +336,48 @@ ], "nixpkgs": "nixpkgs" } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "julm-nix", + "heartwood", + "flake-utils" + ], + "nixpkgs": [ + "julm-nix", + "heartwood", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1715307487, + "narHash": "sha256-yuDAys3JuJmhQUQGMMsl3BDQNZUYZDw0eA71OVh9FeY=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ec7a7caf50877bc32988c82653d6b3e6952a8c3f", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "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 c2491ad..6ae925d 100644 --- a/flake.nix +++ b/flake.nix @@ -30,17 +30,20 @@ }; profile = "/nix/var/nix/profiles/system"; inherit (inputs.nixpkgs) lib; + overlays = system: + import nixpkgs/overlays.nix ++ + import (inputs.julm-nix + "/nixpkgs/overlays.nix") ++ [ + (finalPkgs: previousPkgs: + inputs.julm-nix.inputs.heartwood.packages.${system} // + inputs.julm-nix.inputs.radicle-explorer.packages.${system} + ) + ]; #nixosSystem = lib.nixosSystem; nixosSystem = import (nixpkgsPath + "/nixos/lib/eval-config.nix"); forAllSystems = f: lib.genAttrs lib.systems.flakeExposed (system: f rec { inherit system; #pkgs = inputs.nixpkgs.legacyPackages.${system}; - pkgs = import nixpkgsPath { - inherit system; - overlays = - import nixpkgs/overlays.nix ++ - import (inputs.julm-nix + "/nixpkgs/overlays.nix"); - }; + pkgs = import nixpkgsPath { inherit system; overlays = overlays system; }; }); in { @@ -65,19 +68,17 @@ nixos/default.nix (inputs.julm-nix + "/nixos/default.nix") (inputs.self + "/hosts/${hostName}.nix") - { + ({ config, ... }: { _module.args = { inherit hostName; hosts = inputs.self.nixosConfigurations; host = inputs.self.nixosConfigurations.${hostName}._module.args; info = import ./info.nix; }; - nixpkgs.overlays = - import nixpkgs/overlays.nix ++ - import (inputs.julm-nix + "/nixpkgs/overlays.nix"); + nixpkgs.overlays = overlays config.nixpkgs.hostPlatform.system; #nixpkgs.buildPlatform = "x86_64-linux"; nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "hplip" ]; - } + }) inputs.nixpkgs.nixosModules.notDetected inputs.home-manager.nixosModules.home-manager { diff --git a/hosts/mermet.nix b/hosts/mermet.nix index c7b1fb5..ed0e367 100644 --- a/hosts/mermet.nix +++ b/hosts/mermet.nix @@ -31,6 +31,7 @@ mermet/postgresql.nix mermet/prosody.nix mermet/public-inbox.nix + mermet/radicle.nix mermet/rspamd.nix mermet/sanoid.nix mermet/security.nix diff --git a/hosts/mermet/knot/sourcephile.fr.nix b/hosts/mermet/knot/sourcephile.fr.nix index da6168d..0fd19d1 100644 --- a/hosts/mermet/knot/sourcephile.fr.nix +++ b/hosts/mermet/knot/sourcephile.fr.nix @@ -51,6 +51,7 @@ let whoami A ${hosts.mermet._module.args.ipv4} code A ${hosts.mermet._module.args.ipv4} miniflux A ${hosts.mermet._module.args.ipv4} + radicle-mermet A ${hosts.mermet._module.args.ipv4} ; CNAME (Canonical Name) openconcerto CNAME losurdo @@ -68,6 +69,7 @@ let nix-extracache CNAME losurdo nix-localcache CNAME lan.losurdo sftp CNAME losurdo + radicle CNAME radicle-mermet ; DMARC (Domain-based Message Authentication, Reporting and Conformance) _dmarc 3600 IN TXT "v=DMARC1; p=none; pct=100; rua=mailto:root+dmarc+aggregate@sourcephile.fr; ruf=mailto:root+dmarc+forensic@sourcephile.fr" diff --git a/hosts/mermet/radicle.nix b/hosts/mermet/radicle.nix new file mode 100644 index 0000000..c1e1212 --- /dev/null +++ b/hosts/mermet/radicle.nix @@ -0,0 +1,25 @@ +{ config, pkgs, lib, hostName, ... }: +let + domain = "sourcephile.fr"; + srv = "radicle"; +in +{ + services.nginx.virtualHosts."${srv}.${domain}" = { + serverAliases = [ "${srv}-${hostName}.${domain}" ]; + forceSSL = true; + useACMEHost = domain; + extraConfig = '' + access_log off; + error_log /var/log/nginx/${domain}/${srv}/error.log warn; + ''; + locations."/" = { + root = pkgs.radicle-explorer; + index = "index.html"; + extraConfig = '' + try_files $uri $uri/ /index.html; + ''; + }; + }; + systemd.services.nginx.serviceConfig.LogsDirectory = + lib.mkForce [ "nginx/${domain}/${srv}" ]; +} -- 2.47.2 From caf928cd75ca3a4a67428997cd1bfebe1e458007 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Thu, 23 May 2024 21:06:49 +0200 Subject: [PATCH 08/16] Add given password for hosts/mermet/radicle/key to store. --- hosts/mermet/radicle/key.gpg | Bin 0 -> 1001 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hosts/mermet/radicle/key.gpg diff --git a/hosts/mermet/radicle/key.gpg b/hosts/mermet/radicle/key.gpg new file mode 100644 index 0000000000000000000000000000000000000000..7eb87fba00f48f56b069371a9680d1c5a1dd2383 GIT binary patch literal 1001 zcmVz)1;cT^wv!$;1vO+~eWSu)RHKKz zx@!-x=8cEr6SC;s0Bo+Zb9i^jU^X31J*=D|zJ8D=I?^$Q&x*kldD|;lx7z>-#@?L3 zz`DMd1lKSG}o<2Xp*jqwV>)X+ZC=J#G;-)a-x#RU}2`6fXcmTzgnNE4C z)}Qo726=$U;qMCa-0X~v$%2I&=fVj&&nm*en^L(nTUP&6vn%SiyUD{;HFP-8UGq;8j z!uW;_b{C2OV4(!lmO}8Yz+x@yfz)u2VwH>!WO`Y&dAK~P=}Ax?6iUJ3TQu=(vT5=O z#!bt8z++`fd0^7LX?3<}NOBD$+0lj|vA+E~)jft%k+QR1Y0MgprD=O$S?9Bjf@xAL zAm1Rq1u<|*Ho_zTT-=*RI7N{?Me}2y=VJMaa?cUCOeja-Wc8rfLyn?6_G2r8fXo|d zUL2BE=J{ref=*fAx^IN~*k>AANf_c)ji$?@*D%BvZi8}fx;-9qpBKSUdz{^k=v`7J z&24vBOyErNU=oDpG~G;n)WrKI!92O>KE1zz9BF94WN zr7O8jo1hD*fqmdbtl31aRX(|HQEOWCi^;tk4^Iyq50AWqmqcG45Smcf&zlh5t#PLf z(=7lsl_E)Uo8Z6#qh{5p4a-8LLaBz73s*lk%%EoQ29w32J)0J&Y9d7kL_Em*%L}5C zKJD+j8lA=_8UB*8v6F{o6Bv#p;8y?Hyzx2ZJO5@=Q$3h2(H?PB=Sy);&)s#5aF-@H z2i%Sl$txWZype}dtWa9K1FXq<+(;pPM}8*YPcHy+pvz+P@R}$Wey<3Nh8G}3YLoHIjYTlg|i^qJH%zPuYmP~PymI&LPd5kb|B5@#OJ_TUPZS5m4P2zhFJ zm{b3-i+LK(TeH?iBxkvowbj%KbYNj|d2y=OyNjfnD`m6o11j+_MWn#V(SR(GAlsik zMJNdrY}j#c@58l3OgkV}kr5(QCCX1j?-e*boE95jWJWicEEP@!43*xNn^CyL*e%IS zMX$%|+3;crgNh`~Z5cCr*?>Cw2p$^?jRd_NkU3L_nkRHx%lu!I$R8#JP>H;Sl@2YD Xa)h&$6g~nb(QG%;-hV<1IT3^^pcCPz literal 0 HcmV?d00001 -- 2.47.2 From d87bc799d111638d3dd9feb88ef99b0febf94360 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Tue, 28 May 2024 19:01:22 +0200 Subject: [PATCH 09/16] mermet: radicle: add service --- hosts/mermet/knot/sourcephile.fr.nix | 5 +- hosts/mermet/radicle.nix | 179 +++++++- hosts/mermet/radicle/key.cred | Bin 0 -> 683 bytes hosts/mermet/radicle/key.pub | Bin 0 -> 111 bytes nixos/modules.nix | 2 + nixos/modules/services/misc/radicle.nix | 524 ++++++++++++++++++++++++ 6 files changed, 699 insertions(+), 11 deletions(-) create mode 100644 hosts/mermet/radicle/key.cred create mode 100644 hosts/mermet/radicle/key.pub create mode 100644 nixos/modules/services/misc/radicle.nix diff --git a/hosts/mermet/knot/sourcephile.fr.nix b/hosts/mermet/knot/sourcephile.fr.nix index 0fd19d1..1de6a71 100644 --- a/hosts/mermet/knot/sourcephile.fr.nix +++ b/hosts/mermet/knot/sourcephile.fr.nix @@ -51,7 +51,6 @@ let whoami A ${hosts.mermet._module.args.ipv4} code A ${hosts.mermet._module.args.ipv4} miniflux A ${hosts.mermet._module.args.ipv4} - radicle-mermet A ${hosts.mermet._module.args.ipv4} ; CNAME (Canonical Name) openconcerto CNAME losurdo @@ -69,7 +68,9 @@ let nix-extracache CNAME losurdo nix-localcache CNAME lan.losurdo sftp CNAME losurdo - radicle CNAME radicle-mermet + radicle-mermet CNAME mermet + radicle CNAME mermet + radicle-explorer CNAME radicle ; DMARC (Domain-based Message Authentication, Reporting and Conformance) _dmarc 3600 IN TXT "v=DMARC1; p=none; pct=100; rua=mailto:root+dmarc+aggregate@sourcephile.fr; ruf=mailto:root+dmarc+forensic@sourcephile.fr" diff --git a/hosts/mermet/radicle.nix b/hosts/mermet/radicle.nix index c1e1212..8d64550 100644 --- a/hosts/mermet/radicle.nix +++ b/hosts/mermet/radicle.nix @@ -1,25 +1,186 @@ -{ config, pkgs, lib, hostName, ... }: +{ config, pkgs, lib, host, hostName, ... }: let - domain = "sourcephile.fr"; + inherit (config.networking) domain; srv = "radicle"; + radicle = config.services.radicle; + seed = "${srv}-${hostName}.${domain}"; in { - services.nginx.virtualHosts."${srv}.${domain}" = { - serverAliases = [ "${srv}-${hostName}.${domain}" ]; + services.radicle = { + enable = true; + privateKeyFile = "key:${radicle/key.cred}"; + publicKeyFile = radicle/key.pub; + #package = pkgs.radicle-node; + node = { }; + # FIXME: because radicle-node from the heartwood's flake.nix does not include rad + # Should be re-enabled once radicle-node comes from Nixpkgs + checkConfig = false; + httpd = { + enable = true; + package = pkgs.radicle-httpd; + nginx = { + serverName = seed; + forceSSL = true; + enableACME = false; + useACMEHost = domain; + extraConfig = '' + access_log off; + error_log /var/log/nginx/${domain}/${srv}-${hostName}/error.log warn; + ''; + }; + }; + settings = { + preferredSeeds = [ + "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776" + #"z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@ash.radicle.garden:8776" + ]; + publicExplorer = "https://${srv}.${domain}/nodes/$host/$rid$path"; + node = { + policy = "block"; + scope = "all"; + # Relaying produces a constant network stream! + relay = "never"; + # Make this a public node + #externalAddresses = [ + # "${seed}:${toString radicle.node.listenPort}" + # #"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion:${toString radicle.node.listenPort}" + #]; + peers = { + type = "dynamic"; + target = 0; + }; + limits = { + routingMaxSize = 1000; + routingMaxAge = 1 * 7 * 24 * 60 * 60; # 1 week + gossipMaxAge = 1 * 7 * 24 * 60 * 60; # 1 week + fetchConcurrency = 1; + maxOpenFiles = 4096; + rate = { + inbound = { + fillRate = 1; + capacity = 1; + }; + outbound = { + fillRate = 1; + capacity = 1; + }; + }; + connection = { + inbound = 16; + outbound = 8; + }; + }; + workers = host.CPUs; + /* + onion = { + mode = "proxy"; + address = "127.0.0.1:9050"; + }; + */ + }; + web = { + pinned = { + # Pinned repositories must be `rad clone`-d before. + repositories = [ + "rad:z2364hmzZUAGy1nKdSFa1gLSoUE2M" # literate-phylomemy + "rad:z3795BqJN8hSMGkyAUr8hHviEEi2H" # logic + "rad:z4NtwMC1GmUuCRLngaZrVrSZLmUvh" # symantic-base + ]; + }; + }; + }; + }; + systemd.services.radicle-node = { + environment.RUST_LOG = "debug"; + serviceConfig = { + CPUAccounting = true; + CPUWeight = "idle"; + #CPUQuota = "60%"; + MemoryAccounting = true; + MemoryHigh = "500M"; + MemoryMax = "600M"; + CPUSchedulingPolicy = "idle"; + IOSchedulingClass = "idle"; + # 0: high priority, 7: low priority + IOSchedulingPriority = 3; + Nice = 15; + }; + }; + services.sanoid.datasets."rpool/var/lib/${srv}" = { + use_template = [ "snap" ]; + hourly = 0; + daily = 7; + monthly = 0; + recursive = true; + }; + environment.systemPackages = [ + pkgs.radicle-node + ]; + + networking.nftables.ruleset = '' + table inet filter { + chain input-net { + tcp dport ${toString radicle.node.listenPort} counter accept comment "radicle-node" + } + chain input-neb-sourcephile { + tcp dport ${toString radicle.node.listenPort} counter accept comment "radicle-node" + } + chain output-net { + skuid @nixos_radicle_node_uids meta l4proto tcp counter accept comment "radicle-node" + } + } + ''; + + services.nginx.virtualHosts."${srv}-explorer.${domain}" = { + serverAliases = [ "${srv}.${domain}" ]; forceSSL = true; useACMEHost = domain; extraConfig = '' access_log off; - error_log /var/log/nginx/${domain}/${srv}/error.log warn; + error_log /var/log/nginx/${domain}/${srv}-explorer/error.log warn; ''; locations."/" = { - root = pkgs.radicle-explorer; - index = "index.html"; extraConfig = '' try_files $uri $uri/ /index.html; ''; + index = "index.html"; + root = pkgs.radicle-explorer.overrideAttrs (previousAttrs: { + postPatch = (previousAttrs.postPatch or "") + '' + cp ${pkgs.writeText "local.json" '' + { + "nodes": { + "fallbackPublicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path", + "defaultHttpdPort": 443, + "defaultLocalHttpdPort": 8080, + "defaultHttpdHostname": "localhost", + "defaultHttpdScheme": "https", + "defaultNodePort": 8776, + "pinned": [ + { + "baseUrl": { + "hostname": "${seed}", + "port": 443, + "scheme": "https" + } + } + ] + }, + "supportWebsite": "https://radicle.zulipchat.com", + "reactions": ["👍", "👎", "😄", "🙁", "👀"], + "fallbackPreferredSeed": { + "hostname": "${seed}", + "port": 443, + "scheme": "https" + } + } + ''} config/local.json + ''; + }); }; }; - systemd.services.nginx.serviceConfig.LogsDirectory = - lib.mkForce [ "nginx/${domain}/${srv}" ]; + systemd.services.nginx.serviceConfig.LogsDirectory = lib.mkForce [ + "nginx/${domain}/${srv}-${hostName}" + "nginx/${domain}/${srv}-explorer" + ]; + } diff --git a/hosts/mermet/radicle/key.cred b/hosts/mermet/radicle/key.cred new file mode 100644 index 0000000000000000000000000000000000000000..d85a77a7e18ab5e1b9f9aee9e3e4b2a8a1c2bcc1 GIT binary patch literal 683 zcmV;c0#y9~M@dveQdv+`0EZg9Uah_Mu0Ap_{Z-RmLHl>SO8hWOSEr4A*^_F3pI>0p zDSv+Qa;T^j(FLgDN1|R1g@hL5#52Ss3re{iNh%wF24etf~y z;l1Xuu51hXGzJxu>y?Ko`^3Lu&z5s6!dx22Wjd|ohm>cgW%AWHa)}t0z&`&|hAcGa z&?Q7Sw(oFAxGE^=pd%)aYT&4-$EO_wOG>S>Gb9&xA_!kUb5gE_==;MPE7GR zYOF8fpRK2`@!CZ==YwYWVmdUH?Ia3CuGR~?U7sej+(d21&RBn=)NbQJ3hxgzHb-9$ zwq&>+0m?z|gSYfKtz6E%%F4~>4&`U)7gzyB${7XWHCJ~@ldNN4bJOQut-nF6yr6OFV7OygJ|-M@Um~db~pA; zUqS2+E#hn7fbmss&vTt;x1fkuvS>~u?s{$obtW^DU)wDv6;J)mBYoTNiHx%FN(c>gUyVb@KTL4u>1EgspE`jJ{yyE%#;*2!b!EDKbh6{v?LD_34i=k)1Jq{>) R$U9qq|I%HR6)_>ZHg-j!PKW>i literal 0 HcmV?d00001 diff --git a/hosts/mermet/radicle/key.pub b/hosts/mermet/radicle/key.pub new file mode 100644 index 0000000000000000000000000000000000000000..0dd66203c16ab1449c75625b31e38dc97ad5c191 GIT binary patch literal 111 zcmV-#0FeIxM@dveQdv+`0A7SAL9&TzwRP5HG}{F literal 0 HcmV?d00001 diff --git a/nixos/modules.nix b/nixos/modules.nix index 68016d9..52f919d 100644 --- a/nixos/modules.nix +++ b/nixos/modules.nix @@ -5,6 +5,7 @@ imports = [ #modules/services/databases/openldap.nix #modules/services/mail/public-inbox.nix + modules/services/misc/radicle.nix #modules/services/databases/redis.nix modules/services/mail/postfix.nix modules/services/networking/upnpc.nix @@ -42,6 +43,7 @@ #"services/mail/public-inbox.nix" #"services/security/tor.nix" "services/backup/syncoid.nix" + "services/misc/radicle.nix" "services/networking/prosody.nix" #"services/networking/biboumi.nix" #"services/networking/croc.nix" diff --git a/nixos/modules/services/misc/radicle.nix b/nixos/modules/services/misc/radicle.nix new file mode 100644 index 0000000..3886f7c --- /dev/null +++ b/nixos/modules/services/misc/radicle.nix @@ -0,0 +1,524 @@ +{ config, lib, pkgs, options, ... }: +with lib; +let + cfg = config.services.radicle; + + json = pkgs.formats.json { }; + + configFile = (json.generate "config.json" cfg.settings).overrideAttrs (previousAttrs: { + preferLocalBuild = true; + # None of the usual phases are run here because runCommandWith uses buildCommand, + # so just append to buildCommand what would usually be a checkPhase. + buildCommand = previousAttrs.buildCommand + optionalString cfg.checkConfig '' + ln -s $out config.json + install -D -m 644 /dev/stdin keys/radicle.pub <<<"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBgFMhajUng+Rjj/sCFXI9PzG8BQjru2n7JgUVF1Kbv5 snakeoil" + export RAD_HOME=$PWD + ${getExe' pkgs.buildPackages.radicle-node "rad"} config >/dev/null + ''; + }); + + env = rec { + # rad fails if it cannot stat $HOME/.gitconfig + HOME = "/var/lib/radicle"; + RAD_HOME = HOME; + }; + + # Convenient wrapper to run `rad` in the namespaces of `radicle-node.service` + rad-system = pkgs.writeShellScriptBin "rad-system" '' + set -o allexport + ${toShellVars env} + # Note that --env is not used to preserve host's envvars like $TERM + exec ${getExe' pkgs.util-linux "nsenter"} -a \ + -t "$(${getExe' config.systemd.package "systemctl"} show -P MainPID radicle-node.service)" \ + -S "$(${getExe' config.systemd.package "systemctl"} show -P UID radicle-node.service)" \ + -G "$(${getExe' config.systemd.package "systemctl"} show -P GID radicle-node.service)" \ + ${getExe' cfg.package "rad"} "$@" + ''; +in +{ + options = { + services.radicle = { + enable = mkEnableOption "Radicle Seed Node"; + package = mkPackageOption pkgs "radicle-node" { }; + privateKeyFile = mkOption { + type = with types; either path str; + description = '' + SSH private key generated by `rad auth`. + + If it contains a colon (`:`) the string before the colon + is taken as the credential name + and the string after as a path encrypted with `systemd-creds`. + ''; + }; + publicKeyFile = mkOption { + type = with types; either path str; + description = '' + SSH public key generated by `rad auth`. + ''; + }; + node = { + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + example = "127.0.0.1"; + description = "The IP address on which `radicle-node` listens."; + }; + listenPort = mkOption { + type = types.port; + default = 8776; + description = "The port on which `radicle-node` listens."; + }; + openFirewall = mkEnableOption "opening the firewall for `radicle-node`"; + extraArgs = mkOption { + type = with types; listOf str; + default = [ ]; + description = "Extra arguments for `radicle-node`"; + }; + }; + checkConfig = mkEnableOption "checking the {file}`config.json` file resulting from {option}`services.radicle.settings`" // { default = true; }; + settings = mkOption { + description = '' + See https://app.radicle.xyz/nodes/seed.radicle.garden/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5/tree/radicle/src/node/config.rs#L275 + ''; + default = { }; + type = types.submodule { + freeformType = json.type; + options.cli = { + hints = mkOption { + type = types.bool; + default = true; + description = "Whether to show hints or not in the CLI"; + }; + }; + options.node = { + alias = mkOption { + type = types.str; + default = config.networking.hostName; + defaultText = "config.networking.hostName"; + description = "Node alias"; + }; + connect = mkOption { + type = with types; listOf str; + default = [ ]; + description = '' + Peers to connect to on startup. + Connections to these peers will be maintained + ''; + }; + db = { + journalMode = mkOption { + type = types.str; + default = "rollback"; + description = ""; + }; + }; + externalAddresses = mkOption { + type = with types; listOf str; + default = [ ]; + description = "Specify the node's public addresses"; + }; + limits = { + connection = { + inbound = mkOption { + type = types.ints.unsigned; + default = 128; + description = "Max inbound connections"; + }; + outbound = mkOption { + type = types.ints.unsigned; + default = 16; + description = "Max outbound connections"; + }; + }; + fetchConcurrency = mkOption { + type = types.ints.positive; + default = 1; + description = "Maximum number of concurrent fetches per peer connection"; + }; + gossipMaxAge = mkOption { + type = types.ints.positive; + default = 2 * 7 * 24 * 60 * 60; # 2 weeks + defaultText = "2 * 7 * 24 * 60 * 60"; + description = "How many seconds to keep a gossip message entry before pruning it"; + }; + maxOpenFiles = mkOption { + type = types.ints.positive; + default = 4096; + description = "Maximum number of open files"; + }; + rate = { + inbound = { + capacity = mkOption { + type = types.ints.positive; + default = 1024; + description = "Inbound capacity limit for a single connection"; + }; + fillRate = mkOption { + type = types.numbers.positive; + default = 5.0; + description = "Inbound fill rate for a single connection"; + }; + }; + outbound = { + capacity = mkOption { + type = types.ints.positive; + default = 2048; + description = "Outbound capacity limit for a single connection"; + }; + fillRate = mkOption { + type = types.numbers.positive; + default = 10.0; + description = "Outbound fill rate for a single connection"; + }; + }; + }; + routingMaxAge = mkOption { + type = types.ints.positive; + default = 7 * 24 * 60 * 60; # 1 week + defaultText = "7 * 24 * 60 * 60"; + description = "How many seconds to keep a routing table entry before being pruned"; + }; + routingMaxSize = mkOption { + type = types.ints.positive; + default = 1000; + description = "Number of routing table entries before we start pruning"; + }; + }; + listen = mkOption { + type = with types; listOf str; + default = [ ]; + description = "Address to listen on"; + }; + log = mkOption { + type = types.enum [ "ERROR" "WARN" "INFO" "DEBUG" "TRACE" ]; + default = "INFO"; + description = "Log level"; + }; + network = mkOption { + type = types.str; + default = "main"; + description = "Peer-to-peer network"; + }; + onion = mkOption { + type = types.nullOr (types.submodule { + options.mode = mkOption { + type = with types; nullOr (enum [ "proxy" "forward" ]); + description = '' + "proxy" proxies connections to the given address. + + "forward" forward address to the next layer. Either this is the global proxy, + or the operating system, via DNS. + ''; + }; + options.address = mkOption { + type = with types; nullOr str; + default = null; + description = "Address for the \"proxy\" mode"; + }; + }); + default = null; + description = "Onion address config"; + }; + peers = { + type = mkOption { + type = types.enum [ "dynamic" "static" ]; + default = "dynamic"; + description = '' + "dynamic" for a dynamic peer set + + "static" for a static peer set. + Connect to the configured peers and maintain the connections. + ''; + }; + target = mkOption { + type = types.ints.unsigned; + default = 8; + description = "Number of outbound dynamic peers"; + }; + }; + policy = mkOption { + type = types.enum [ "allow" "block" ]; + default = "block"; + description = "Default seeding policy"; + }; + proxy = mkOption { + type = with types; nullOr str; + default = null; + description = "Global proxy"; + }; + relay = mkOption { + type = types.enum [ "auto" "always" "never" ]; + default = "never"; + description = "Whether or not our node should relay messages"; + }; + scope = mkOption { + type = types.enum [ "all" "followed" ]; + default = "all"; + description = "Default seeding scope"; + }; + workers = mkOption { + type = types.ints.positive; + default = 8; + description = "Number of worker threads to spawn"; + }; + }; + options.preferredSeeds = mkOption { + type = with types; listOf str; + default = [ + "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776" + "z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@ash.radicle.garden:8776" + ]; + description = '' + Preferred seeds. These seeds will be used for explorer links + and in other situations when a seed needs to be chosen + ''; + }; + options.publicExplorer = mkOption { + type = types.str; + default = "https://app.radicle.xyz/nodes/$host/$rid$path"; + description = '' + Public explorer. This is used for generating links + ''; + }; + options.web = { + pinned = { + repositories = mkOption { + type = with types; listOf str; + default = [ ]; + description = '' + Pinned repositories on a Web client. + ''; + }; + }; + }; + }; + }; + httpd = { + enable = mkEnableOption "Radicle HTTP gateway"; + # TODO: use radicale-httpd when the packages have been split + package = mkPackageOption pkgs "radicle-node" { }; + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "The IP address on which `radicle-httpd` listens."; + }; + listenPort = mkOption { + type = types.port; + default = 8080; + description = "The port on which `radicle-httpd` listens."; + }; + nginx = mkOption { + # Type of a single virtual host, or null. + type = types.nullOr options.services.nginx.virtualHosts.type.functor.wrapped; + default = null; + example = literalExpression '' + { + serverAliases = [ + "seed.''${config.networking.domain}" + ]; + enableACME = false; + useACMEHost = config.networking.domain; + } + ''; + description = '' + With this option, you can customize an nginx virtual host which already has sensible defaults for `radicle-httpd`. + Set to `{}` if you do not need any customization to the virtual host. + If enabled, then by default, the {option}`serverName` is + `radicle-''${config.networking.hostName}.''${config.networking.domain}`, + TLS is active, and certificates are acquired via ACME. + If this is set to null (the default), no nginx virtual host will be configured. + ''; + }; + extraArgs = mkOption { + type = with types; listOf str; + default = [ ]; + description = "Extra arguments for `radicle-httpd`"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services = + let + commonConfig = serviceName: + { + environment = env // { + RUST_LOG = mkDefault "info"; + }; + path = [ + pkgs.gitMinimal + ]; + documentation = [ + "https://docs.radicle.xyz/guides/seeder" + ]; + after = [ + "network.target" + "network-online.target" + ]; + requires = [ + "network-online.target" + ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = mkMerge [ + { + BindReadOnlyPaths = [ + "${configFile}:${env.RAD_HOME}/config.json" + "${if isPath cfg.publicKeyFile then cfg.publicKeyFile else pkgs.writeText "radicle.pub" cfg.publicKeyFile}:${env.RAD_HOME}/keys/radicle.pub" + ]; + KillMode = "process"; + StateDirectory = [ "radicle" ]; + DynamicUser = true; + # The "radicale" user will be allocated when the first of the "radicle-*" services starts, + # it will be shared among them, and be released when the last one of them exits. + User = "radicle"; + Group = "radicle"; + WorkingDirectory = env.HOME; + } + # The following options are only for optimizing: + # systemd-analyze security ${serviceName} + { + BindReadOnlyPaths = [ + "-/etc/resolv.conf" + "/etc/ssl/certs/ca-certificates.crt" + "/run/systemd" + ]; + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DeviceAllow = ""; # ProtectClock= adds DeviceAllow=char-rtc r + LockPersonality = true; + MemoryDenyWriteExecute = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RuntimeDirectoryMode = "700"; + SocketBindDeny = [ "any" ]; + StateDirectoryMode = "0750"; + SystemCallFilter = [ + "@system-service" + "~@aio" + "~@chown" + "~@keyring" + "~@memlock" + "~@privileged" + "~@resources" + "~@setuid" + "~@timer" + ]; + SystemCallArchitectures = "native"; + # This is for BindPaths= and BindReadOnlyPaths= + # to allow traversal of directories they create inside RootDirectory= + UMask = "0066"; + } + ]; + confinement = { + enable = true; + mode = "full-apivfs"; + packages = [ + pkgs.gitMinimal + cfg.package + pkgs.iana-etc + (getLib pkgs.nss) + pkgs.tzdata + ]; + }; + }; + in + { + radicle-node = mkMerge [ + (commonConfig "radicle-node") + { + description = "Radicle Node"; + documentation = [ "man:radicle-node(1)" ]; + serviceConfig = { + ExecStart = "${getExe' cfg.package "radicle-node"} --force --listen ${cfg.node.listenAddress}:${toString cfg.node.listenPort} ${escapeShellArgs cfg.node.extraArgs}"; + NFTSet = optionals config.networking.nftables.enable [ + "user:inet:filter:nixos_radicle_node_uids" + ]; + Restart = mkDefault "on-failure"; + RestartSec = "30"; + SocketBindAllow = [ "tcp:${toString cfg.node.listenPort}" ]; + SystemCallFilter = mkAfter [ + # Needed by git upload-pack which calls alarm() and setitimer() when providing a rad clone + "@timer" + ]; + }; + } + # Give only access to the private key to radicle-node. + { + serviceConfig = + let keyCred = builtins.split ":" "${cfg.privateKeyFile}"; in + if length keyCred > 1 + then { + LoadCredentialEncrypted = [ cfg.privateKeyFile ]; + # Note that neither %d nor ${CREDENTIALS_DIRECTORY} works in BindReadOnlyPaths= + BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/${head keyCred}:${env.RAD_HOME}/keys/radicle" ]; + } + else { + LoadCredential = [ "radicle:${cfg.privateKeyFile}" ]; + BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/radicle:${env.RAD_HOME}/keys/radicle" ]; + }; + } + ]; + radicle-httpd = mkMerge [ + (commonConfig "radicle-httpd") + { + description = "Radicle HTTP gateway to radicle-node"; + documentation = [ "man:radicle-httpd(1)" ]; + serviceConfig = { + ExecStart = "${getExe' cfg.httpd.package "radicle-httpd"} --listen ${cfg.httpd.listenAddress}:${toString cfg.httpd.listenPort} ${escapeShellArgs cfg.httpd.extraArgs}"; + Restart = mkDefault "on-failure"; + RestartSec = "10"; + SocketBindAllow = [ "tcp:${toString cfg.httpd.listenPort}" ]; + SystemCallFilter = mkAfter [ + # Needed by git upload-pack which calls alarm() and setitimer() when providing a git clone + "@timer" + ]; + }; + } + ]; + }; + + environment.systemPackages = [ + rad-system + ]; + + networking.firewall = mkIf cfg.node.openFirewall { + allowedTCPPorts = [ cfg.node.listenPort ]; + }; + + networking.nftables.ruleset = mkIf config.networking.nftables.enable (mkBefore '' + table inet filter { + # A set containing the dynamic UID of the radicle-node systemd service of NixOS + set nixos_radicle_node_uids { typeof meta skuid; } + } + ''); + + services.radicle.httpd.nginx.serverName = mkDefault + "radicle-${config.networking.hostName}.${config.networking.domain}"; + services.nginx.virtualHosts = mkIf (cfg.httpd.nginx != null) { + "${cfg.httpd.nginx.serverName}" = lib.mkMerge [ + cfg.httpd.nginx + { + forceSSL = mkDefault true; + enableACME = mkDefault true; + locations."/" = { + proxyPass = "http://${cfg.httpd.listenAddress}:${toString cfg.httpd.listenPort}"; + recommendedProxySettings = true; + }; + } + ]; + }; + + meta.maintainers = with lib.maintainers; [ + julm + lorenzleutgeb + ]; + }; +} -- 2.47.2 From 49bbfafcd0bfb4865f972315ac874923d009c98d Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Sun, 2 Jun 2024 00:24:23 +0200 Subject: [PATCH 10/16] mermet: dovecot: fix sieve with nixos-24.05 --- hosts/mermet/dovecot.nix | 83 ++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/hosts/mermet/dovecot.nix b/hosts/mermet/dovecot.nix index 2c0c6d8..4180b13 100644 --- a/hosts/mermet/dovecot.nix +++ b/hosts/mermet/dovecot.nix @@ -6,14 +6,6 @@ let stateDir = "/var/lib/dovecot"; - sieve_pipe_bin_dir = pkgs.buildEnv { - name = "sieve_pipe_bin_dir"; - pathsToLink = [ "/bin" ]; - paths = [ - learn-spam - learn-ham - ]; - }; learn-spam = pkgs.writeShellScriptBin "learn-spam.sh" '' exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/learner.sock learn_spam ''; @@ -21,22 +13,8 @@ let exec ${pkgs.rspamd}/bin/rspamc -h /run/rspamd/learner.sock learn_ham ''; - dovecot-virtual = pkgs.buildEnv { - name = "dovecot-virtual"; - pathsToLink = [ "/" ]; - paths = [ - dovecot-virtual-all - dovecot-virtual-recents - ]; - }; - dovecot-virtual-all = pkgs.writeTextFile { - name = "dovecot-virtual-all"; - destination = "/All/dovecot-virtual"; - text = '' - * - all - ''; - }; + dovecot-virtual = pkgs.buildEnv { name = "dovecot-virtual"; pathsToLink = [ "/" ]; paths = [ dovecot-virtual-all dovecot-virtual-recents ]; }; + dovecot-virtual-all = pkgs.writeTextFile { name = "dovecot-virtual-all"; destination = "/All/dovecot-virtual"; text = '' * all ''; }; dovecot-virtual-recents = pkgs.writeTextFile { name = "dovecot-virtual-recents"; destination = "/Recents/dovecot-virtual"; @@ -123,8 +101,49 @@ in mailUser = ""; mailGroup = ""; sslServerCert = null; - sieveScripts = { - global = dovecot/sieve/global; + sieve = { + plugins = [ + #"sieve_extprograms" + "sieve_imapsieve" + ]; + extensions = [ + "copy" + "envelope" + "environment" + "fileinto" + "imap4flags" + "imapsieve" + "include" + "mailbox" + "subaddress" + "variables" + "vnd.dovecot.environment" + ]; + globalExtensions = [ + "vnd.dovecot.pipe" + ]; + pipeBins = builtins.map lib.getExe [ + learn-ham + learn-spam + ]; + scripts = { + global = dovecot/sieve/global; + }; + }; + mailPlugins = { + globally.enable = [ + "virtual" + "acl" + "quota" + #"fts" + #"fts_xapian" + ]; + perProtocol = { + lda.enable = [ "sieve" ]; + lmtp.enable = [ "sieve" ]; + imap.enable = [ "imap_acl" "imap_quota" "imap_sieve" "virtual" ]; + pop3.enable = [ "virtual" ]; + }; }; extraConfig = '' #auth_verbose = yes @@ -225,7 +244,6 @@ in separator = + subscriptions = yes } - mail_plugins = $mail_plugins virtual namespace Virtual { prefix = Virtual+ separator = + @@ -235,7 +253,6 @@ in location = virtual:${dovecot-virtual}:UTF-8:INDEX=${stateDir}/index/%d/%n/virtual } - mail_plugins = $mail_plugins acl plugin { acl = vfile:/etc/dovecot/acl/global acl_anyone = allow @@ -245,7 +262,6 @@ in acl_shared_dict = file:${stateDir}/acl/%d/acl.db } - #mail_plugins = $mail_plugins fts fts_xapian # Default VSZ (virtual memory size) limit for service processes. This is mainly # intended to catch and kill processes that leak memory before they eat up everything. # Increased for fts_xapian. @@ -268,7 +284,6 @@ in # #fts_dovecot_fs = posix:prefix=%h/fts/ #} - mail_plugins = $mail_plugins quota plugin { quota = maildir:User quota quota_rule = *:storage=256M @@ -282,7 +297,6 @@ in protocol lda { hostname = ${networking.domain} - mail_plugins = $mail_plugins sieve postmaster_address = root+dovecot+lda@${networking.domain} syslog_facility = mail } @@ -290,7 +304,6 @@ in lda_mailbox_autosubscribe = yes protocol lmtp { - mail_plugins = $mail_plugins sieve postmaster_address = root+dovecot+lmtp@${networking.domain} } service lmtp { @@ -330,7 +343,6 @@ in imapc_features = $imapc_features fetch-headers protocol imap { #mail_max_userip_connections = 10 - mail_plugins = $mail_plugins imap_acl imap_quota imap_sieve virtual imap_metadata = yes # DOC: https://wiki.dovecot.org/MailboxSettings @@ -412,13 +424,9 @@ in } } plugin { - sieve_plugins = sieve_imapsieve sieve_extprograms - sieve_global_extensions = +vnd.dovecot.environment +vnd.dovecot.pipe - #sieve_extensions = +editheader sieve = file:~/sieve;active=~/active.sieve sieve_default = file:${stateDir}/sieve/global/default.sieve sieve_default_name = main - sieve_pipe_bin_dir = ${sieve_pipe_bin_dir}/bin sieve_max_script_size = 1M sieve_quota_max_scripts = 0 sieve_quota_max_storage = 10M @@ -508,7 +516,6 @@ in } protocol pop3 { - mail_plugins = $mail_plugins virtual #mail_max_userip_connections = 10 # Virtual namespace for the virtual INBOX. # Use a global directory for dovecot-virtual files. -- 2.47.2 From 72a6caf0d2e53a84075bb4bffddf0d2d2ff84913 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Sun, 2 Jun 2024 00:27:37 +0200 Subject: [PATCH 11/16] nix: update to nixos-24.05 --- flake.lock | 114 ++++++++++++++------------- flake.nix | 13 +-- nixos/modules.nix | 2 + nixos/profiles/services/fail2ban.nix | 2 +- nixpkgs/overlays.nix | 2 +- 5 files changed, 72 insertions(+), 61 deletions(-) diff --git a/flake.lock b/flake.lock index 4c0c807..7fa3281 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "advisory-db": { "flake": false, "locked": { - "lastModified": 1714183630, - "narHash": "sha256-1BVft7ggSN2XXFeXQjazU3jN9wVECd9qp2mZx/8GDMk=", + "lastModified": 1701193254, + "narHash": "sha256-Hr7efA3GjwqBkGYKmd3XmGckdPQikbcCmOrq7fmTp3A=", "owner": "rustsec", "repo": "advisory-db", - "rev": "35e7459a331d3e0c585e56dabd03006b9b354088", + "rev": "43af5fef0591531a72ebb86c5f1c623ee95c62fe", "type": "github" }, "original": { @@ -19,17 +19,17 @@ "crane": { "inputs": { "nixpkgs": [ - "julm-nix", + "radicle-explorer", "heartwood", "nixpkgs" ] }, "locked": { - "lastModified": 1715274763, - "narHash": "sha256-3Iv1PGHJn9sV3HO4FlOVaaztOxa9uGLfOmUWrH7v7+A=", + "lastModified": 1701622587, + "narHash": "sha256-o3XhxCCyrUHZ0tlta2W7/MuXzy+n0+BUt3rKFK3DIK4=", "owner": "ipetkov", "repo": "crane", - "rev": "27025ab71bdca30e7ed0a16c88fd74c5970fc7f5", + "rev": "c09d2cbe84cc2adfe1943cb2a0b55a71c835ca9a", "type": "github" }, "original": { @@ -75,11 +75,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", "type": "github" }, "original": { @@ -142,24 +142,22 @@ "advisory-db": "advisory-db", "crane": "crane", "flake-utils": "flake-utils", - "nixpkgs": [ - "julm-nix", - "nixpkgs" - ], + "nixpkgs": "nixpkgs_2", "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1716379007, - "narHash": "sha256-3+10+g0qQ3Ivn56U32jqxaSMZKN/AtS+6Yy6p4UQBgo=", - "ref": "refs/heads/master", - "rev": "3403a66d0fc9c9cfab97eddaf5adeab40dc3bf23", - "revCount": 1973, + "lastModified": 1714650527, + "narHash": "sha256-F2n7ui0EgXK8fT76M14RVhXXGeRYub+VpH+puDUJ1pQ=", + "ref": "refs/namespaces/z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/refs/tags/v1.0.0-rc.8", + "rev": "6ce4cecdf3229f4651c33839e073ff383bc40369", + "revCount": 1903, "type": "git", - "url": "https://seed.radicle.garden/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" + "url": "https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" }, "original": { + "ref": "refs/namespaces/z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/refs/tags/v1.0.0-rc.8", "type": "git", - "url": "https://seed.radicle.garden/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" + "url": "https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git" } }, "home-manager": { @@ -170,16 +168,16 @@ ] }, "locked": { - "lastModified": 1715381426, - "narHash": "sha256-wPuqrAQGdv3ISs74nJfGb+Yprm23U/rFpcHFFNWgM94=", + "lastModified": 1716736833, + "narHash": "sha256-rNObca6dm7Qs524O4st8VJH6pZ/Xe1gxl+Rx6mcWYo0=", "owner": "nix-community", "repo": "home-manager", - "rev": "ab5542e9dbd13d0100f8baae2bc2d68af901f4b4", + "rev": "a631666f5ec18271e86a5cde998cba68c33d9ac6", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-23.11", + "ref": "release-24.05", "repo": "home-manager", "type": "github" } @@ -188,20 +186,18 @@ "inputs": { "doom-emacs": "doom-emacs", "git-hooks": "git-hooks", - "heartwood": "heartwood", "home-manager": "home-manager", "nix-formatter-pack": "nix-formatter-pack", "nixpkgs": [ "nixpkgs" - ], - "radicle-explorer": "radicle-explorer" + ] }, "locked": { - "lastModified": 1716459584, - "narHash": "sha256-yKwo71IxijKjp3P14lBE5bZci79di/toJj8Qvnr/p4Q=", + "lastModified": 1717273786, + "narHash": "sha256-94WAI0OaBHnHWhNsGgtaI5/vd0Y5LLlD7kVJI78i73E=", "ref": "main", - "rev": "a851689001c69d66159a35a61e72bc582c0b0ca4", - "revCount": 902, + "rev": "76b56214e01025698d1b38fed791e87fa0eba642", + "revCount": 910, "type": "git", "url": "file:///home/julm/work/sourcephile/nix/julm-nix" }, @@ -236,16 +232,32 @@ }, "nixpkgs": { "locked": { - "lastModified": 1712310679, - "narHash": "sha256-XgC/a/giEeNkhme/AV1ToipoZ/IVm1MV2ntiK4Tm+pw=", + "lastModified": 1716793392, + "narHash": "sha256-ex3nO87EEQhshXd19QSVW5UIXL0pbPuew4q8TdEJQBY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "72da83d9515b43550436891f538ff41d68eecc7f", + "rev": "67a8b308bae9c26be660ccceff3e53a65e01afe1", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1701961404, + "narHash": "sha256-ieWHyh6kJtabQYUam/dXXi22MgcgLQvl+f2x/95n+us=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8fef9eee026f0d95c06b5880ef9c1af0f643aadf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.11", "repo": "nixpkgs", "type": "github" } @@ -285,32 +297,27 @@ "radicle-explorer": { "inputs": { "flake-utils": [ - "julm-nix", "radicle-explorer", "heartwood", "flake-utils" ], - "heartwood": [ - "julm-nix", - "heartwood" - ], + "heartwood": "heartwood", "nixpkgs": [ - "julm-nix", "radicle-explorer", "heartwood", "nixpkgs" ] }, "locked": { - "lastModified": 1716392706, - "narHash": "sha256-xN2Uzsfid7jdNCmE3aZ2A0l0Rhbai5XrtJ70p+n+bhc=", - "ref": "refs/heads/master", - "rev": "77cc3ce031dc261c25cde9ab88bf187eea33f433", - "revCount": 1796, + "lastModified": 1716541749, + "narHash": "sha256-dM739qbAtbER/u4yYjMlwDGeLpxbwyZIzRs2hr+wWsc=", + "rev": "fb32e01a2fa84d104c114becf1a6a65636240305", + "revCount": 1798, "type": "git", "url": "https://seed.radicle.garden/z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git" }, "original": { + "rev": "fb32e01a2fa84d104c114becf1a6a65636240305", "type": "git", "url": "https://seed.radicle.garden/z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git" } @@ -334,28 +341,29 @@ "julm-nix", "nix-formatter-pack" ], - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "radicle-explorer": "radicle-explorer" } }, "rust-overlay": { "inputs": { "flake-utils": [ - "julm-nix", + "radicle-explorer", "heartwood", "flake-utils" ], "nixpkgs": [ - "julm-nix", + "radicle-explorer", "heartwood", "nixpkgs" ] }, "locked": { - "lastModified": 1715307487, - "narHash": "sha256-yuDAys3JuJmhQUQGMMsl3BDQNZUYZDw0eA71OVh9FeY=", + "lastModified": 1711419061, + "narHash": "sha256-+5M/czgYGqs/jKmi8bvYC+JUYboUKNTfkRiesXopeXQ=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ec7a7caf50877bc32988c82653d6b3e6952a8c3f", + "rev": "4c11d2f698ff1149f76b69e72852d5d75f492d0c", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6ae925d..cc9168e 100644 --- a/flake.nix +++ b/flake.nix @@ -6,8 +6,9 @@ julm-nix.url = "git+file:///home/julm/work/sourcephile/nix/julm-nix?ref=main"; nix-formatter-pack.follows = "julm-nix/nix-formatter-pack"; #nixpkgs.follows = "julm-nix/nixpkgs"; - nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; git-hooks.follows = "julm-nix/git-hooks"; + radicle-explorer.url = "git+https://seed.radicle.garden/z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git?rev=fb32e01a2fa84d104c114becf1a6a65636240305"; }; outputs = inputs: @@ -31,13 +32,13 @@ profile = "/nix/var/nix/profiles/system"; inherit (inputs.nixpkgs) lib; overlays = system: - import nixpkgs/overlays.nix ++ - import (inputs.julm-nix + "/nixpkgs/overlays.nix") ++ [ + [ (finalPkgs: previousPkgs: - inputs.julm-nix.inputs.heartwood.packages.${system} // - inputs.julm-nix.inputs.radicle-explorer.packages.${system} + inputs.radicle-explorer.packages.${system} ) - ]; + ] ++ + import (inputs.julm-nix + "/nixpkgs/overlays.nix") ++ + import nixpkgs/overlays.nix; #nixosSystem = lib.nixosSystem; nixosSystem = import (nixpkgsPath + "/nixos/lib/eval-config.nix"); forAllSystems = f: lib.genAttrs lib.systems.flakeExposed (system: f rec { diff --git a/nixos/modules.nix b/nixos/modules.nix index 52f919d..c33316e 100644 --- a/nixos/modules.nix +++ b/nixos/modules.nix @@ -31,6 +31,7 @@ #modules/config/console.nix #modules/services/x11/display-managers/default.nix modules/services/networking/prosody.nix + #modules/security/systemd-confinement.nix (inputs.julm-nix + "/nixos/modules/security/systemd-creds.nix") (inputs.julm-nix + "/nixos/modules/services/networking/wireguard.nix") ]; @@ -45,6 +46,7 @@ "services/backup/syncoid.nix" "services/misc/radicle.nix" "services/networking/prosody.nix" + #"security/systemd-confinement.nix" #"services/networking/biboumi.nix" #"services/networking/croc.nix" #"services/networking/netns.nix" diff --git a/nixos/profiles/services/fail2ban.nix b/nixos/profiles/services/fail2ban.nix index 3b926ec..83f14cf 100644 --- a/nixos/profiles/services/fail2ban.nix +++ b/nixos/profiles/services/fail2ban.nix @@ -16,7 +16,7 @@ systemd.services.nftables.postStart = '' systemctl reload fail2ban ''; */ services.openssh.settings.LogLevel = "VERBOSE"; - services.postgresql.logLinePrefix = "%h "; + services.postgresql.settings.log_line_prefix = "%h "; environment.etc."fail2ban/action.d/nftables-common.local".text = '' [Init] blocktype = drop diff --git a/nixpkgs/overlays.nix b/nixpkgs/overlays.nix index 6c90fc0..2a6f77c 100644 --- a/nixpkgs/overlays.nix +++ b/nixpkgs/overlays.nix @@ -2,7 +2,7 @@ map import [ overlays/lib/filesystem.nix overlays/lib/strings.nix - overlays/gnupg.nix + #overlays/gnupg.nix overlays/iodine.nix overlays/matrirc.nix #overlays/public-inbox.nix -- 2.47.2 From 85c5e7a44317506e60791c74c71b8531b48b8fa6 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Sun, 30 Jun 2024 18:31:32 +0200 Subject: [PATCH 12/16] mermet: radicle: update --- hosts/mermet/radicle.nix | 3 +- nixos/modules/services/misc/radicle.nix | 597 ++++++------------ .../web-servers/nginx/location-options.nix | 141 +++++ .../web-servers/nginx/vhost-options.nix | 370 +++++++++++ 4 files changed, 723 insertions(+), 388 deletions(-) create mode 100644 nixos/modules/services/web-servers/nginx/location-options.nix create mode 100644 nixos/modules/services/web-servers/nginx/vhost-options.nix diff --git a/hosts/mermet/radicle.nix b/hosts/mermet/radicle.nix index 8d64550..4eeeefd 100644 --- a/hosts/mermet/radicle.nix +++ b/hosts/mermet/radicle.nix @@ -1,6 +1,7 @@ { config, pkgs, lib, host, hostName, ... }: let inherit (config.networking) domain; + inherit (config.users) users; srv = "radicle"; radicle = config.services.radicle; seed = "${srv}-${hostName}.${domain}"; @@ -126,7 +127,7 @@ in tcp dport ${toString radicle.node.listenPort} counter accept comment "radicle-node" } chain output-net { - skuid @nixos_radicle_node_uids meta l4proto tcp counter accept comment "radicle-node" + skuid ${users.radicle.name} meta l4proto tcp counter accept comment "radicle-node" } } ''; diff --git a/nixos/modules/services/misc/radicle.nix b/nixos/modules/services/misc/radicle.nix index 3886f7c..3fedcac 100644 --- a/nixos/modules/services/misc/radicle.nix +++ b/nixos/modules/services/misc/radicle.nix @@ -1,22 +1,10 @@ -{ config, lib, pkgs, options, ... }: +{ config, lib, pkgs, ... }: with lib; let cfg = config.services.radicle; json = pkgs.formats.json { }; - configFile = (json.generate "config.json" cfg.settings).overrideAttrs (previousAttrs: { - preferLocalBuild = true; - # None of the usual phases are run here because runCommandWith uses buildCommand, - # so just append to buildCommand what would usually be a checkPhase. - buildCommand = previousAttrs.buildCommand + optionalString cfg.checkConfig '' - ln -s $out config.json - install -D -m 644 /dev/stdin keys/radicle.pub <<<"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBgFMhajUng+Rjj/sCFXI9PzG8BQjru2n7JgUVF1Kbv5 snakeoil" - export RAD_HOME=$PWD - ${getExe' pkgs.buildPackages.radicle-node "rad"} config >/dev/null - ''; - }); - env = rec { # rad fails if it cannot stat $HOME/.gitconfig HOME = "/var/lib/radicle"; @@ -34,6 +22,96 @@ let -G "$(${getExe' config.systemd.package "systemctl"} show -P GID radicle-node.service)" \ ${getExe' cfg.package "rad"} "$@" ''; + + commonServiceConfig = serviceName: { + environment = env // { + RUST_LOG = mkDefault "info"; + }; + path = [ + pkgs.gitMinimal + ]; + documentation = [ + "https://docs.radicle.xyz/guides/seeder" + ]; + after = [ + "network.target" + "network-online.target" + ]; + requires = [ + "network-online.target" + ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = mkMerge [ + { + BindReadOnlyPaths = [ + "${cfg.configFile}:${env.RAD_HOME}/config.json" + "${if isPath cfg.publicKeyFile then cfg.publicKeyFile else pkgs.writeText "radicle.pub" cfg.publicKeyFile}:${env.RAD_HOME}/keys/radicle.pub" + ]; + KillMode = "process"; + StateDirectory = [ "radicle" ]; + User = config.users.users.radicle.name; + Group = config.users.groups.radicle.name; + WorkingDirectory = env.HOME; + } + # The following options are only for optimizing: + # systemd-analyze security ${serviceName} + { + BindReadOnlyPaths = [ + "-/etc/resolv.conf" + "/etc/ssl/certs/ca-certificates.crt" + "/run/systemd" + ]; + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + DeviceAllow = ""; # ProtectClock= adds DeviceAllow=char-rtc r + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RuntimeDirectoryMode = "700"; + SocketBindDeny = [ "any" ]; + StateDirectoryMode = "0750"; + SystemCallFilter = [ + "@system-service" + "~@aio" + "~@chown" + "~@keyring" + "~@memlock" + "~@privileged" + "~@resources" + "~@setuid" + "~@timer" + ]; + SystemCallArchitectures = "native"; + # This is for BindPaths= and BindReadOnlyPaths= + # to allow traversal of directories they create inside RootDirectory= + UMask = "0066"; + } + ]; + confinement = { + enable = true; + mode = "full-apivfs"; + packages = [ + pkgs.gitMinimal + cfg.package + pkgs.iana-etc + (getLib pkgs.nss) + pkgs.tzdata + ]; + }; + }; in { options = { @@ -75,6 +153,27 @@ in description = "Extra arguments for `radicle-node`"; }; }; + configFile = mkOption { + type = types.package; + internal = true; + default = (json.generate "config.json" cfg.settings).overrideAttrs (previousAttrs: { + preferLocalBuild = true; + # None of the usual phases are run here because runCommandWith uses buildCommand, + # so just append to buildCommand what would usually be a checkPhase. + buildCommand = previousAttrs.buildCommand + optionalString cfg.checkConfig '' + ln -s $out config.json + install -D -m 644 /dev/stdin keys/radicle.pub <<<"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBgFMhajUng+Rjj/sCFXI9PzG8BQjru2n7JgUVF1Kbv5 snakeoil" + export RAD_HOME=$PWD + ${getExe' pkgs.buildPackages.radicle-node "rad"} config >/dev/null || { + cat -n config.json + echo "Invalid config.json according to rad." + echo "Please double-check your services.radicle.settings (producing the config.json above)," + echo "some settings may be missing or have the wrong type." + exit 1 + } >&2 + ''; + }); + }; checkConfig = mkEnableOption "checking the {file}`config.json` file resulting from {option}`services.radicle.settings`" // { default = true; }; settings = mkOption { description = '' @@ -83,220 +182,11 @@ in default = { }; type = types.submodule { freeformType = json.type; - options.cli = { - hints = mkOption { - type = types.bool; - default = true; - description = "Whether to show hints or not in the CLI"; - }; - }; - options.node = { - alias = mkOption { - type = types.str; - default = config.networking.hostName; - defaultText = "config.networking.hostName"; - description = "Node alias"; - }; - connect = mkOption { - type = with types; listOf str; - default = [ ]; - description = '' - Peers to connect to on startup. - Connections to these peers will be maintained - ''; - }; - db = { - journalMode = mkOption { - type = types.str; - default = "rollback"; - description = ""; - }; - }; - externalAddresses = mkOption { - type = with types; listOf str; - default = [ ]; - description = "Specify the node's public addresses"; - }; - limits = { - connection = { - inbound = mkOption { - type = types.ints.unsigned; - default = 128; - description = "Max inbound connections"; - }; - outbound = mkOption { - type = types.ints.unsigned; - default = 16; - description = "Max outbound connections"; - }; - }; - fetchConcurrency = mkOption { - type = types.ints.positive; - default = 1; - description = "Maximum number of concurrent fetches per peer connection"; - }; - gossipMaxAge = mkOption { - type = types.ints.positive; - default = 2 * 7 * 24 * 60 * 60; # 2 weeks - defaultText = "2 * 7 * 24 * 60 * 60"; - description = "How many seconds to keep a gossip message entry before pruning it"; - }; - maxOpenFiles = mkOption { - type = types.ints.positive; - default = 4096; - description = "Maximum number of open files"; - }; - rate = { - inbound = { - capacity = mkOption { - type = types.ints.positive; - default = 1024; - description = "Inbound capacity limit for a single connection"; - }; - fillRate = mkOption { - type = types.numbers.positive; - default = 5.0; - description = "Inbound fill rate for a single connection"; - }; - }; - outbound = { - capacity = mkOption { - type = types.ints.positive; - default = 2048; - description = "Outbound capacity limit for a single connection"; - }; - fillRate = mkOption { - type = types.numbers.positive; - default = 10.0; - description = "Outbound fill rate for a single connection"; - }; - }; - }; - routingMaxAge = mkOption { - type = types.ints.positive; - default = 7 * 24 * 60 * 60; # 1 week - defaultText = "7 * 24 * 60 * 60"; - description = "How many seconds to keep a routing table entry before being pruned"; - }; - routingMaxSize = mkOption { - type = types.ints.positive; - default = 1000; - description = "Number of routing table entries before we start pruning"; - }; - }; - listen = mkOption { - type = with types; listOf str; - default = [ ]; - description = "Address to listen on"; - }; - log = mkOption { - type = types.enum [ "ERROR" "WARN" "INFO" "DEBUG" "TRACE" ]; - default = "INFO"; - description = "Log level"; - }; - network = mkOption { - type = types.str; - default = "main"; - description = "Peer-to-peer network"; - }; - onion = mkOption { - type = types.nullOr (types.submodule { - options.mode = mkOption { - type = with types; nullOr (enum [ "proxy" "forward" ]); - description = '' - "proxy" proxies connections to the given address. - - "forward" forward address to the next layer. Either this is the global proxy, - or the operating system, via DNS. - ''; - }; - options.address = mkOption { - type = with types; nullOr str; - default = null; - description = "Address for the \"proxy\" mode"; - }; - }); - default = null; - description = "Onion address config"; - }; - peers = { - type = mkOption { - type = types.enum [ "dynamic" "static" ]; - default = "dynamic"; - description = '' - "dynamic" for a dynamic peer set - - "static" for a static peer set. - Connect to the configured peers and maintain the connections. - ''; - }; - target = mkOption { - type = types.ints.unsigned; - default = 8; - description = "Number of outbound dynamic peers"; - }; - }; - policy = mkOption { - type = types.enum [ "allow" "block" ]; - default = "block"; - description = "Default seeding policy"; - }; - proxy = mkOption { - type = with types; nullOr str; - default = null; - description = "Global proxy"; - }; - relay = mkOption { - type = types.enum [ "auto" "always" "never" ]; - default = "never"; - description = "Whether or not our node should relay messages"; - }; - scope = mkOption { - type = types.enum [ "all" "followed" ]; - default = "all"; - description = "Default seeding scope"; - }; - workers = mkOption { - type = types.ints.positive; - default = 8; - description = "Number of worker threads to spawn"; - }; - }; - options.preferredSeeds = mkOption { - type = with types; listOf str; - default = [ - "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776" - "z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@ash.radicle.garden:8776" - ]; - description = '' - Preferred seeds. These seeds will be used for explorer links - and in other situations when a seed needs to be chosen - ''; - }; - options.publicExplorer = mkOption { - type = types.str; - default = "https://app.radicle.xyz/nodes/$host/$rid$path"; - description = '' - Public explorer. This is used for generating links - ''; - }; - options.web = { - pinned = { - repositories = mkOption { - type = with types; listOf str; - default = [ ]; - description = '' - Pinned repositories on a Web client. - ''; - }; - }; - }; }; }; httpd = { - enable = mkEnableOption "Radicle HTTP gateway"; - # TODO: use radicale-httpd when the packages have been split - package = mkPackageOption pkgs "radicle-node" { }; + enable = mkEnableOption "Radicle HTTP gateway to radicle-node"; + package = mkPackageOption pkgs "radicle-httpd" { }; listenAddress = mkOption { type = types.str; default = "127.0.0.1"; @@ -309,7 +199,14 @@ in }; nginx = mkOption { # Type of a single virtual host, or null. - type = types.nullOr options.services.nginx.virtualHosts.type.functor.wrapped; + type = types.nullOr (types.submodule ( + recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { + options.serverName = { + default = "radicle-${config.networking.hostName}.${config.networking.domain}"; + defaultText = "radicle-\${config.networking.hostName}.\${config.networking.domain}"; + }; + } + )); default = null; example = literalExpression '' { @@ -338,136 +235,68 @@ in }; }; - config = mkIf cfg.enable { - systemd.services = - let - commonConfig = serviceName: - { - environment = env // { - RUST_LOG = mkDefault "info"; - }; - path = [ - pkgs.gitMinimal - ]; - documentation = [ - "https://docs.radicle.xyz/guides/seeder" - ]; - after = [ - "network.target" - "network-online.target" - ]; - requires = [ - "network-online.target" - ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = mkMerge [ - { - BindReadOnlyPaths = [ - "${configFile}:${env.RAD_HOME}/config.json" - "${if isPath cfg.publicKeyFile then cfg.publicKeyFile else pkgs.writeText "radicle.pub" cfg.publicKeyFile}:${env.RAD_HOME}/keys/radicle.pub" - ]; - KillMode = "process"; - StateDirectory = [ "radicle" ]; - DynamicUser = true; - # The "radicale" user will be allocated when the first of the "radicle-*" services starts, - # it will be shared among them, and be released when the last one of them exits. - User = "radicle"; - Group = "radicle"; - WorkingDirectory = env.HOME; - } - # The following options are only for optimizing: - # systemd-analyze security ${serviceName} - { - BindReadOnlyPaths = [ - "-/etc/resolv.conf" - "/etc/ssl/certs/ca-certificates.crt" - "/run/systemd" - ]; - AmbientCapabilities = ""; - CapabilityBoundingSet = ""; - DeviceAllow = ""; # ProtectClock= adds DeviceAllow=char-rtc r - LockPersonality = true; - MemoryDenyWriteExecute = true; - ProcSubset = "pid"; - ProtectClock = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectProc = "invisible"; - RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RuntimeDirectoryMode = "700"; - SocketBindDeny = [ "any" ]; - StateDirectoryMode = "0750"; - SystemCallFilter = [ - "@system-service" - "~@aio" - "~@chown" - "~@keyring" - "~@memlock" - "~@privileged" - "~@resources" - "~@setuid" - "~@timer" - ]; - SystemCallArchitectures = "native"; - # This is for BindPaths= and BindReadOnlyPaths= - # to allow traversal of directories they create inside RootDirectory= - UMask = "0066"; - } + config = mkIf cfg.enable (mkMerge [ + { + systemd.services.radicle-node = mkMerge [ + (commonServiceConfig "radicle-node") + { + description = "Radicle Node"; + documentation = [ "man:radicle-node(1)" ]; + serviceConfig = { + ExecStart = "${getExe' cfg.package "radicle-node"} --force --listen ${cfg.node.listenAddress}:${toString cfg.node.listenPort} ${escapeShellArgs cfg.node.extraArgs}"; + Restart = mkDefault "on-failure"; + RestartSec = "30"; + SocketBindAllow = [ "tcp:${toString cfg.node.listenPort}" ]; + SystemCallFilter = mkAfter [ + # Needed by git upload-pack which calls alarm() and setitimer() when providing a rad clone + "@timer" ]; - confinement = { - enable = true; - mode = "full-apivfs"; - packages = [ - pkgs.gitMinimal - cfg.package - pkgs.iana-etc - (getLib pkgs.nss) - pkgs.tzdata - ]; - }; }; - in - { - radicle-node = mkMerge [ - (commonConfig "radicle-node") - { - description = "Radicle Node"; - documentation = [ "man:radicle-node(1)" ]; - serviceConfig = { - ExecStart = "${getExe' cfg.package "radicle-node"} --force --listen ${cfg.node.listenAddress}:${toString cfg.node.listenPort} ${escapeShellArgs cfg.node.extraArgs}"; - NFTSet = optionals config.networking.nftables.enable [ - "user:inet:filter:nixos_radicle_node_uids" - ]; - Restart = mkDefault "on-failure"; - RestartSec = "30"; - SocketBindAllow = [ "tcp:${toString cfg.node.listenPort}" ]; - SystemCallFilter = mkAfter [ - # Needed by git upload-pack which calls alarm() and setitimer() when providing a rad clone - "@timer" - ]; + confinement.packages = [ + cfg.package + ]; + } + # Give only access to the private key to radicle-node. + { + serviceConfig = + let keyCred = builtins.split ":" "${cfg.privateKeyFile}"; in + if length keyCred > 1 + then { + LoadCredentialEncrypted = [ cfg.privateKeyFile ]; + # Note that neither %d nor ${CREDENTIALS_DIRECTORY} works in BindReadOnlyPaths= + BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/${head keyCred}:${env.RAD_HOME}/keys/radicle" ]; + } + else { + LoadCredential = [ "radicle:${cfg.privateKeyFile}" ]; + BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/radicle:${env.RAD_HOME}/keys/radicle" ]; }; - } - # Give only access to the private key to radicle-node. - { - serviceConfig = - let keyCred = builtins.split ":" "${cfg.privateKeyFile}"; in - if length keyCred > 1 - then { - LoadCredentialEncrypted = [ cfg.privateKeyFile ]; - # Note that neither %d nor ${CREDENTIALS_DIRECTORY} works in BindReadOnlyPaths= - BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/${head keyCred}:${env.RAD_HOME}/keys/radicle" ]; - } - else { - LoadCredential = [ "radicle:${cfg.privateKeyFile}" ]; - BindReadOnlyPaths = [ "/run/credentials/radicle-node.service/radicle:${env.RAD_HOME}/keys/radicle" ]; - }; - } - ]; - radicle-httpd = mkMerge [ - (commonConfig "radicle-httpd") + } + ]; + + environment.systemPackages = [ + rad-system + ]; + + networking.firewall = mkIf cfg.node.openFirewall { + allowedTCPPorts = [ cfg.node.listenPort ]; + }; + + users = { + users.radicle = { + isSystemUser = true; + group = "radicle"; + description = "Radicle"; + home = env.HOME; + }; + groups.radicle = { + }; + }; + } + + (mkIf cfg.httpd.enable (mkMerge [ + { + systemd.services.radicle-httpd = mkMerge [ + (commonServiceConfig "radicle-httpd") { description = "Radicle HTTP gateway to radicle-node"; documentation = [ "man:radicle-httpd(1)" ]; @@ -481,44 +310,38 @@ in "@timer" ]; }; + confinement.packages = [ + cfg.httpd.package + ]; } ]; - }; - - environment.systemPackages = [ - rad-system - ]; - - networking.firewall = mkIf cfg.node.openFirewall { - allowedTCPPorts = [ cfg.node.listenPort ]; - }; - - networking.nftables.ruleset = mkIf config.networking.nftables.enable (mkBefore '' - table inet filter { - # A set containing the dynamic UID of the radicle-node systemd service of NixOS - set nixos_radicle_node_uids { typeof meta skuid; } } - ''); - services.radicle.httpd.nginx.serverName = mkDefault - "radicle-${config.networking.hostName}.${config.networking.domain}"; - services.nginx.virtualHosts = mkIf (cfg.httpd.nginx != null) { - "${cfg.httpd.nginx.serverName}" = lib.mkMerge [ - cfg.httpd.nginx - { - forceSSL = mkDefault true; - enableACME = mkDefault true; - locations."/" = { - proxyPass = "http://${cfg.httpd.listenAddress}:${toString cfg.httpd.listenPort}"; - recommendedProxySettings = true; - }; - } - ]; - }; + (mkIf (cfg.httpd.nginx != null) { + services.nginx.virtualHosts.${cfg.httpd.nginx.serverName} = lib.mkMerge [ + cfg.httpd.nginx + { + forceSSL = mkDefault true; + enableACME = mkDefault true; + locations."/" = { + proxyPass = "http://${cfg.httpd.listenAddress}:${toString cfg.httpd.listenPort}"; + recommendedProxySettings = true; + }; + } + ]; - meta.maintainers = with lib.maintainers; [ - julm - lorenzleutgeb - ]; - }; + services.radicle.settings = { + node.alias = mkDefault cfg.httpd.nginx.serverName; + node.externalAddresses = mkDefault [ + "${cfg.httpd.nginx.serverName}:${toString cfg.node.listenPort}" + ]; + }; + }) + ])) + ]); + + meta.maintainers = with lib.maintainers; [ + julm + lorenzleutgeb + ]; } diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix new file mode 100644 index 0000000..8cefd48 --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/location-options.nix @@ -0,0 +1,141 @@ +# This file defines the options that can be used both for the Nginx +# main server configuration, and for the virtual hosts. (The latter +# has additional options that affect the web server as a whole, like +# the user/group to run under.) + +{ lib, config }: + +with lib; + +{ + options = { + basicAuth = mkOption { + type = types.attrsOf types.str; + default = {}; + example = literalExpression '' + { + user = "password"; + }; + ''; + description = '' + Basic Auth protection for a vhost. + + WARNING: This is implemented to store the password in plain text in the + Nix store. + ''; + }; + + basicAuthFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Basic Auth password file for a vhost. + Can be created via: {command}`htpasswd -c `. + + WARNING: The generate file contains the users' passwords in a + non-cryptographically-securely hashed way. + ''; + }; + + proxyPass = mkOption { + type = types.nullOr types.str; + default = null; + example = "http://www.example.org/"; + description = '' + Adds proxy_pass directive and sets recommended proxy headers if + recommendedProxySettings is enabled. + ''; + }; + + proxyWebsockets = mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Whether to support proxying websocket connections with HTTP/1.1. + ''; + }; + + index = mkOption { + type = types.nullOr types.str; + default = null; + example = "index.php index.html"; + description = '' + Adds index directive. + ''; + }; + + tryFiles = mkOption { + type = types.nullOr types.str; + default = null; + example = "$uri =404"; + description = '' + Adds try_files directive. + ''; + }; + + root = mkOption { + type = types.nullOr types.path; + default = null; + example = "/your/root/directory"; + description = '' + Root directory for requests. + ''; + }; + + alias = mkOption { + type = types.nullOr types.path; + default = null; + example = "/your/alias/directory"; + description = '' + Alias directory for requests. + ''; + }; + + return = mkOption { + type = with types; nullOr (oneOf [ str int ]); + default = null; + example = "301 http://example.com$request_uri"; + description = '' + Adds a return directive, for e.g. redirections. + ''; + }; + + fastcgiParams = mkOption { + type = types.attrsOf (types.either types.str types.path); + default = {}; + description = '' + FastCGI parameters to override. Unlike in the Nginx + configuration file, overriding only some default parameters + won't unset the default values for other parameters. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + These lines go to the end of the location verbatim. + ''; + }; + + priority = mkOption { + type = types.int; + default = 1000; + description = '' + Order of this location block in relation to the others in the vhost. + The semantics are the same as with `lib.mkOrder`. Smaller values have + a greater priority. + ''; + }; + + recommendedProxySettings = mkOption { + type = types.bool; + default = config.services.nginx.recommendedProxySettings; + defaultText = literalExpression "config.services.nginx.recommendedProxySettings"; + description = '' + Enable recommended proxy settings. + ''; + }; + }; +} diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix new file mode 100644 index 0000000..24fcb10 --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -0,0 +1,370 @@ +# This file defines the options that can be used both for the Nginx +# main server configuration, and for the virtual hosts. (The latter +# has additional options that affect the web server as a whole, like +# the user/group to run under.) + +{ config, lib, ... }: + +with lib; +{ + options = { + serverName = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Name of this virtual host. Defaults to attribute name in virtualHosts. + ''; + example = "example.org"; + }; + + serverAliases = mkOption { + type = types.listOf types.str; + default = []; + example = [ "www.example.org" "example.org" ]; + description = '' + Additional names of virtual hosts served by this virtual host configuration. + ''; + }; + + listen = mkOption { + type = with types; listOf (submodule { + options = { + addr = mkOption { + type = str; + description = "Listen address."; + }; + port = mkOption { + type = types.nullOr port; + description = '' + Port number to listen on. + If unset and the listen address is not a socket then nginx defaults to 80. + ''; + default = null; + }; + ssl = mkOption { + type = bool; + description = "Enable SSL."; + default = false; + }; + proxyProtocol = mkOption { + type = bool; + description = "Enable PROXY protocol."; + default = false; + }; + extraParameters = mkOption { + type = listOf str; + description = "Extra parameters of this listen directive."; + default = [ ]; + example = [ "backlog=1024" "deferred" ]; + }; + }; + }); + default = []; + example = [ + { addr = "195.154.1.1"; port = 443; ssl = true; } + { addr = "192.154.1.1"; port = 80; } + { addr = "unix:/var/run/nginx.sock"; } + ]; + description = '' + Listen addresses and ports for this virtual host. + IPv6 addresses must be enclosed in square brackets. + Note: this option overrides `addSSL` + and `onlySSL`. + + If you only want to set the addresses manually and not + the ports, take a look at `listenAddresses`. + ''; + }; + + listenAddresses = mkOption { + type = with types; listOf str; + + description = '' + Listen addresses for this virtual host. + Compared to `listen` this only sets the addresses + and the ports are chosen automatically. + + Note: This option overrides `enableIPv6` + ''; + default = []; + example = [ "127.0.0.1" "[::1]" ]; + }; + + enableACME = mkOption { + type = types.bool; + default = false; + description = '' + Whether to ask Let's Encrypt to sign a certificate for this vhost. + Alternately, you can use an existing certificate through {option}`useACMEHost`. + ''; + }; + + useACMEHost = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + A host of an existing Let's Encrypt certificate to use. + This is useful if you have many subdomains and want to avoid hitting the + [rate limit](https://letsencrypt.org/docs/rate-limits). + Alternately, you can generate a certificate through {option}`enableACME`. + *Note that this option does not create any certificates, nor it does add subdomains to existing ones – you will need to create them manually using [](#opt-security.acme.certs).* + ''; + }; + + acmeRoot = mkOption { + type = types.nullOr types.str; + default = "/var/lib/acme/acme-challenge"; + description = '' + Directory for the ACME challenge, which is **public**. Don't put certs or keys in here. + Set to null to inherit from config.security.acme. + ''; + }; + + acmeFallbackHost = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Host which to proxy requests to if ACME challenge is not found. Useful + if you want multiple hosts to be able to verify the same domain name. + + With this option, you could request certificates for the present domain + with an ACME client that is running on another host, which you would + specify here. + ''; + }; + + addSSL = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable HTTPS in addition to plain HTTP. This will set defaults for + `listen` to listen on all interfaces on the respective default + ports (80, 443). + ''; + }; + + onlySSL = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable HTTPS and reject plain HTTP connections. This will set + defaults for `listen` to listen on all interfaces on port 443. + ''; + }; + + enableSSL = mkOption { + type = types.bool; + visible = false; + default = false; + }; + + forceSSL = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add a separate nginx server block that redirects (defaults + to 301, configurable with `redirectCode`) all plain HTTP traffic to + HTTPS. This will set defaults for `listen` to listen on all interfaces + on the respective default ports (80, 443), where the non-SSL listens + are used for the redirect vhosts. + ''; + }; + + rejectSSL = mkOption { + type = types.bool; + default = false; + description = '' + Whether to listen for and reject all HTTPS connections to this vhost. Useful in + [default](#opt-services.nginx.virtualHosts._name_.default) + server blocks to avoid serving the certificate for another vhost. Uses the + `ssl_reject_handshake` directive available in nginx versions + 1.19.4 and above. + ''; + }; + + kTLS = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable kTLS support. + Implementing TLS in the kernel (kTLS) improves performance by significantly + reducing the need for copying operations between user space and the kernel. + Required Nginx version 1.21.4 or later. + ''; + }; + + sslCertificate = mkOption { + type = types.path; + example = "/var/host.cert"; + description = "Path to server SSL certificate."; + }; + + sslCertificateKey = mkOption { + type = types.path; + example = "/var/host.key"; + description = "Path to server SSL certificate key."; + }; + + sslTrustedCertificate = mkOption { + type = types.nullOr types.path; + default = null; + example = literalExpression ''"''${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"''; + description = "Path to root SSL certificate for stapling and client certificates."; + }; + + http2 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the HTTP/2 protocol. + Note that (as of writing) due to nginx's implementation, to disable + HTTP/2 you have to disable it on all vhosts that use a given + IP address / port. + If there is one server block configured to enable http2, then it is + enabled for all server blocks on this IP. + See https://stackoverflow.com/a/39466948/263061. + ''; + }; + + http3 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the HTTP/3 protocol. + This requires using `pkgs.nginxQuic` package + which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;` + and activate the QUIC transport protocol + `services.nginx.virtualHosts..quic = true;`. + Note that HTTP/3 support is experimental and *not* yet recommended for production. + Read more at https://quic.nginx.org/ + HTTP/3 availability must be manually advertised, preferably in each location block. + ''; + }; + + http3_hq = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the HTTP/0.9 protocol negotiation used in QUIC interoperability tests. + This requires using `pkgs.nginxQuic` package + which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;` + and activate the QUIC transport protocol + `services.nginx.virtualHosts..quic = true;`. + Note that special application protocol support is experimental and *not* yet recommended for production. + Read more at https://quic.nginx.org/ + ''; + }; + + quic = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the QUIC transport protocol. + This requires using `pkgs.nginxQuic` package + which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;`. + Note that QUIC support is experimental and + *not* yet recommended for production. + Read more at https://quic.nginx.org/ + ''; + }; + + reuseport = mkOption { + type = types.bool; + default = false; + description = '' + Create an individual listening socket . + It is required to specify only once on one of the hosts. + ''; + }; + + root = mkOption { + type = types.nullOr types.path; + default = null; + example = "/data/webserver/docs"; + description = '' + The path of the web root directory. + ''; + }; + + default = mkOption { + type = types.bool; + default = false; + description = '' + Makes this vhost the default. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + These lines go to the end of the vhost verbatim. + ''; + }; + + globalRedirect = mkOption { + type = types.nullOr types.str; + default = null; + example = "newserver.example.org"; + description = '' + If set, all requests for this host are redirected (defaults to 301, + configurable with `redirectCode`) to the given hostname. + ''; + }; + + redirectCode = mkOption { + type = types.ints.between 300 399; + default = 301; + example = 308; + description = '' + HTTP status used by `globalRedirect` and `forceSSL`. Possible usecases + include temporary (302, 307) redirects, keeping the request method and + body (307, 308), or explicitly resetting the method to GET (303). + See . + ''; + }; + + basicAuth = mkOption { + type = types.attrsOf types.str; + default = {}; + example = literalExpression '' + { + user = "password"; + }; + ''; + description = '' + Basic Auth protection for a vhost. + + WARNING: This is implemented to store the password in plain text in the + Nix store. + ''; + }; + + basicAuthFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Basic Auth password file for a vhost. + Can be created via: {command}`htpasswd -c `. + + WARNING: The generate file contains the users' passwords in a + non-cryptographically-securely hashed way. + ''; + }; + + locations = mkOption { + type = types.attrsOf (types.submodule (import ./location-options.nix { + inherit lib config; + })); + default = {}; + example = literalExpression '' + { + "/" = { + proxyPass = "http://localhost:3000"; + }; + }; + ''; + description = "Declarative location config"; + }; + }; +} -- 2.47.2 From 417afd4255992158729c991784509502497fa449 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Sun, 30 Jun 2024 22:56:17 +0200 Subject: [PATCH 13/16] mermet: pleroma: increase DB timeout --- hosts/mermet/pleroma.nix | 6 ++++++ hosts/mermet/postgresql.nix | 3 +++ 2 files changed, 9 insertions(+) diff --git a/hosts/mermet/pleroma.nix b/hosts/mermet/pleroma.nix index 3293106..c0762f2 100644 --- a/hosts/mermet/pleroma.nix +++ b/hosts/mermet/pleroma.nix @@ -64,6 +64,12 @@ let socket_dir: "/run/postgresql", database: "${db}", pool_size: 5, + # Database task queue timeout to avoid timeouts on the front end + # due to a slow postgresql, eg. because of a CPUQuota= hardening. + queue_target: 20_000, + queue_interval: 1_000, + ownership_timeout: 20_000, + timeout: 40_000, prepare: :named, # https://docs-develop.pleroma.social/backend/configuration/postgresql/#disable-generic-query-plans parameters: [ diff --git a/hosts/mermet/postgresql.nix b/hosts/mermet/postgresql.nix index 2522fb8..99185c4 100644 --- a/hosts/mermet/postgresql.nix +++ b/hosts/mermet/postgresql.nix @@ -109,6 +109,9 @@ in }; systemd.services.postgresql = { serviceConfig = { + CPUAccounting = true; + #CPUWeight = "idle"; + CPUQuota = "75%"; MemoryAccounting = true; MemoryHigh = "500M"; MemoryMax = "600M"; -- 2.47.2 From a1fea46746aa9ccda1ba22a5fbab77309c0fad98 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Wed, 3 Jul 2024 19:49:08 +0200 Subject: [PATCH 14/16] nix: update nixpkgs --- flake.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index 7fa3281..19e55c2 100644 --- a/flake.lock +++ b/flake.lock @@ -193,11 +193,11 @@ ] }, "locked": { - "lastModified": 1717273786, - "narHash": "sha256-94WAI0OaBHnHWhNsGgtaI5/vd0Y5LLlD7kVJI78i73E=", + "lastModified": 1719578514, + "narHash": "sha256-KOn9JuLEhyrGalm1pi8siLb/UfzwV2bQmTLFK4asIFc=", "ref": "main", - "rev": "76b56214e01025698d1b38fed791e87fa0eba642", - "revCount": 910, + "rev": "1b6281dd208176a27f127480c96a2b62bd038553", + "revCount": 920, "type": "git", "url": "file:///home/julm/work/sourcephile/nix/julm-nix" }, @@ -232,11 +232,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716793392, - "narHash": "sha256-ex3nO87EEQhshXd19QSVW5UIXL0pbPuew4q8TdEJQBY=", + "lastModified": 1719956923, + "narHash": "sha256-nNJHJ9kfPdzYsCOlHOnbiiyKjZUW5sWbwx3cakg3/C4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "67a8b308bae9c26be660ccceff3e53a65e01afe1", + "rev": "706eef542dec88cc0ed25b9075d3037564b2d164", "type": "github" }, "original": { -- 2.47.2 From f1fee12af3148954d29d16e1e4eb184cd2ab2bac Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Wed, 10 Jul 2024 02:40:44 +0200 Subject: [PATCH 15/16] mermet: git-daemon: fix breakage due to the new safe.directory --- hosts/mermet/gitolite.nix | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hosts/mermet/gitolite.nix b/hosts/mermet/gitolite.nix index a604235..c9a64dc 100644 --- a/hosts/mermet/gitolite.nix +++ b/hosts/mermet/gitolite.nix @@ -68,11 +68,17 @@ in Restart = "always"; RestartSec = 5; }; - script = "${pkgs.git}/bin/git daemon --verbose --reuseaddr" - + " --base-path=${gitolite.dataDir}/repositories" - #+ (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ") - #+ "--port=${toString cfg.port} " - ; + script = lib.escapeShellArgs [ + "${pkgs.git}/bin/git" + "-c" + "safe.directory=*" + "daemon" + "--verbose" + "--reuseaddr" + "--base-path=${gitolite.dataDir}/repositories" + #(optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress}") + #"--port=${toString cfg.port}" + ]; }; users.users."git-daemon" = { uid = config.ids.uids.git; -- 2.47.2 From d66808dfe0fb7438842313948c6bfdf4f95285c0 Mon Sep 17 00:00:00 2001 From: Julien Moutinho Date: Thu, 15 Aug 2024 19:10:36 +0200 Subject: [PATCH 16/16] mermet: knot: sourcephile.fr: add MX --- hosts/mermet/knot/sourcephile.fr.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hosts/mermet/knot/sourcephile.fr.nix b/hosts/mermet/knot/sourcephile.fr.nix index 1de6a71..c9d9b18 100644 --- a/hosts/mermet/knot/sourcephile.fr.nix +++ b/hosts/mermet/knot/sourcephile.fr.nix @@ -52,6 +52,9 @@ let code A ${hosts.mermet._module.args.ipv4} miniflux A ${hosts.mermet._module.args.ipv4} + ; MX (Mail eXchange) + @ 500 MX 5 mail + ; CNAME (Canonical Name) openconcerto CNAME losurdo xmpp CNAME mermet -- 2.47.2