diff options
-rw-r--r-- | .envrc | 1 | ||||
-rw-r--r-- | flake.lock | 216 | ||||
-rw-r--r-- | flake.nix | 66 | ||||
-rw-r--r-- | patches/cgit-pink.patch | 26 | ||||
-rw-r--r-- | secrets/acme.age | 10 | ||||
-rw-r--r-- | secrets/binarycache.age | bin | 0 -> 435 bytes | |||
-rw-r--r-- | secrets/dyndns.age | bin | 0 -> 476 bytes | |||
-rw-r--r-- | secrets/identities/se.txt | 4 | ||||
-rw-r--r-- | secrets/paperless.age | 7 | ||||
-rw-r--r-- | secrets/powerdns.age | 7 | ||||
-rw-r--r-- | secrets/secrets.nix | 31 | ||||
-rw-r--r-- | secrets/syncthing.age | bin | 0 -> 608 bytes | |||
-rwxr-xr-x | setup/hetzner.sh | 81 | ||||
-rw-r--r-- | system/linde-hardware.nix | 38 | ||||
-rw-r--r-- | system/linde.nix | 787 | ||||
-rw-r--r-- | system/nanopi-hardware.nix | 35 | ||||
-rwxr-xr-x | system/nanopi.nix | 859 |
17 files changed, 2141 insertions, 27 deletions
diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/flake.lock b/flake.lock index e7ba6924..1e6b6e15 100644 --- a/flake.lock +++ b/flake.lock @@ -1,8 +1,53 @@ { "nodes": { + "agenix": { + "inputs": { + "darwin": "darwin", + "home-manager": "home-manager", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems" + }, + "locked": { + "lastModified": 1712079060, + "narHash": "sha256-/JdiT9t+zzjChc5qQiF+jhrVhRt8figYH29rZO7pFe4=", + "owner": "ryantm", + "repo": "agenix", + "rev": "1381a759b205dff7a6818733118d02253340fd5e", + "type": "github" + }, + "original": { + "owner": "ryantm", + "repo": "agenix", + "type": "github" + } + }, "darwin": { "inputs": { "nixpkgs": [ + "agenix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1700795494, + "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=", + "owner": "lnl7", + "repo": "nix-darwin", + "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d", + "type": "github" + }, + "original": { + "owner": "lnl7", + "ref": "master", + "repo": "nix-darwin", + "type": "github" + } + }, + "darwin_2": { + "inputs": { + "nixpkgs": [ "nixpkgs" ] }, @@ -21,6 +66,26 @@ "type": "github" } }, + "deploy-rs": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs", + "utils": "utils" + }, + "locked": { + "lastModified": 1711973905, + "narHash": "sha256-UFKME/N1pbUtn+2Aqnk+agUt8CekbpuqwzljivfIme8=", + "owner": "serokell", + "repo": "deploy-rs", + "rev": "88b3059b020da69cbe16526b8d639bd5e0b51c8b", + "type": "github" + }, + "original": { + "owner": "serokell", + "repo": "deploy-rs", + "type": "github" + } + }, "emacs-overlay": { "inputs": { "flake-utils": "flake-utils", @@ -30,11 +95,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1711789603, - "narHash": "sha256-5c8prZYLBFgMDoBrBTMuAu6F33HHF9kfK+i4d39gUDA=", + "lastModified": 1712768907, + "narHash": "sha256-o3yQ8ZWR4AOoLPk3+If1F0xmm65LsszDLvC7iUqWVG0=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "f8ad90d467e2a48cf91aa0b3b75ac7929dc07425", + "rev": "d4d9c62d5e11ad212aee995ad56b8885067179e7", "type": "github" }, "original": { @@ -43,9 +108,25 @@ "type": "github" } }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { - "systems": "systems" + "systems": "systems_3" }, "locked": { "lastModified": 1710146030, @@ -64,15 +145,36 @@ "home-manager": { "inputs": { "nixpkgs": [ + "agenix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703113217, + "narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_2": { + "inputs": { + "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1711625603, - "narHash": "sha256-W+9dfqA9bqUIBV5u7jaIARAzMe3kTq/Hp2SpSVXKRQw=", + "lastModified": 1712759992, + "narHash": "sha256-2APpO3ZW4idlgtlb8hB04u/rmIcKA8O7pYqxF66xbNY=", "owner": "nix-community", "repo": "home-manager", - "rev": "c0ef0dab55611c676ad7539bf4e41b3ec6fa87d2", + "rev": "31357486b0ef6f4e161e002b6893eeb4fafc3ca9", "type": "github" }, "original": { @@ -88,11 +190,11 @@ ] }, "locked": { - "lastModified": 1711249705, - "narHash": "sha256-h/NQECj6mIzF4XR6AQoSpkCnwqAM+ol4+qOdYi2ykmQ=", + "lastModified": 1712459390, + "narHash": "sha256-e12bNDottaGoBgd0AdH/bQvk854xunlWAdZwr/oHO1c=", "owner": "Mic92", "repo": "nix-index-database", - "rev": "34519f3bb678a5abbddf7b200ac5347263ee781b", + "rev": "4676d72d872459e1e3a248d049609f110c570e9a", "type": "github" }, "original": { @@ -103,11 +205,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1711352745, - "narHash": "sha256-luvqik+i3HTvCbXQZgB6uggvEcxI9uae0nmrgtXJ17U=", + "lastModified": 1712760404, + "narHash": "sha256-4zhaEW1nB+nGbCNMjOggWeY5nXs/H0Y71q0+h+jdxoU=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "9a763a7acc4cfbb8603bb0231fec3eda864f81c0", + "rev": "e1c4bac14beb8c409d0534382cf967171706b9d9", "type": "github" }, "original": { @@ -118,27 +220,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1711703276, - "narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", + "lastModified": 1702272962, + "narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", + "rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1711668574, - "narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=", + "lastModified": 1712588820, + "narHash": "sha256-y31s5idk3jMJMAVE4Ud9AdI7HT3CgTAeMTJ0StqKN7Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659", + "rev": "d272ca50d1f7424fbfcd1e6f1c9e01d92f6da167", "type": "github" }, "original": { @@ -148,16 +250,34 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1712608508, + "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { - "darwin": "darwin", + "agenix": "agenix", + "darwin": "darwin_2", + "deploy-rs": "deploy-rs", "emacs-overlay": "emacs-overlay", - "home-manager": "home-manager", + "home-manager": "home-manager_2", "nix-index-database": "nix-index-database", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "secrets": "secrets", - "utils": "utils" + "utils": "utils_2" } }, "secrets": { @@ -206,11 +326,59 @@ "type": "github" } }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "utils": { "inputs": { "systems": "systems_2" }, "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "utils_2": { + "inputs": { + "systems": "systems_4" + }, + "locked": { "lastModified": 1710146030, "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", diff --git a/flake.nix b/flake.nix index 9115ef97..0ce7efb8 100644 --- a/flake.nix +++ b/flake.nix @@ -14,6 +14,14 @@ flake = false; }; utils.url = "github:numtide/flake-utils"; + agenix.url = "github:ryantm/agenix"; + agenix.inputs.nixpkgs.follows = "nixpkgs"; + deploy-rs.url = "github:serokell/deploy-rs"; + }; + + nixConfig = { + extra-substituters = [ "https://deploy-rs.cachix.org" ]; + extra-trusted-public-keys = [ "deploy-rs.cachix.org-1:xfNobmiwF/vzvK1gpfediPwpdIP0rpDV2rYqx40zdSI=" ]; }; outputs = @@ -27,6 +35,8 @@ , nix-index-database , secrets , emacs-overlay + , agenix + , deploy-rs , ... }: let @@ -41,7 +51,7 @@ in { nixosConfigurations.prefect = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; + system = utils.lib.system.x86_64-linux; specialArgs = { inherit inputs; }; modules = [ ./system/prefect.nix @@ -56,7 +66,18 @@ nixosConfigurations.nanopi = nixpkgs.lib.nixosSystem { system = utils.lib.system.aarch64-linux; specialArgs = { inherit inputs; }; - modules = [ ./nanopi.nix ]; + modules = [ + agenix.nixosModules.default + ./system/nanopi.nix + ]; + }; + nixosConfigurations.linde = nixpkgs.lib.nixosSystem { + system = utils.lib.system.aarch64-linux; + specialArgs = { inherit inputs; }; + modules = [ + agenix.nixosModules.default + ./system/linde.nix + ]; }; darwinConfigurations.mba = darwin.lib.darwinSystem { system = utils.lib.system.aarch64-darwin; @@ -99,5 +120,44 @@ (secrets + "/default.nix") ]; }; - }; + + checks = builtins.mapAttrs + (system: deployLib: + deployLib.deployChecks self.deploy) + deploy-rs.lib; + + deploy = { + remoteBuild = true; + interactiveSudo = true; + nodes.linde = { + hostname = "linde"; + profiles.system = { + user = "root"; + path = deploy-rs.lib.${utils.lib.system.aarch64-linux}.activate.nixos + self.nixosConfigurations.linde; + }; + }; + nodes.nanopi = { + hostname = "nanopi"; + profiles.system = { + user = "root"; + path = deploy-rs.lib.${utils.lib.system.aarch64-linux}.activate.nixos + self.nixosConfigurations.nanopi; + }; + }; + }; + } // utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + devShells = { + default = pkgs.mkShell { + packages = [ + deploy-rs.packages.${system}.default + agenix.packages.${system}.default + ]; + }; + }; + }); } diff --git a/patches/cgit-pink.patch b/patches/cgit-pink.patch new file mode 100644 index 00000000..0e91525e --- /dev/null +++ b/patches/cgit-pink.patch @@ -0,0 +1,26 @@ +diff --git a/cgit.c b/cgit.c +index dd28a79..451f518 100644 +--- a/cgit.c ++++ b/cgit.c +@@ -489,7 +489,7 @@ static char *guess_defbranch(void) + + ref = resolve_ref_unsafe("HEAD", 0, &oid, NULL); + if (!ref || !skip_prefix(ref, "refs/heads/", &refname)) +- return "master"; ++ return "main"; + return xstrdup(refname); + } + +diff --git a/ui-repolist.c b/ui-repolist.c +index 97b11c5..cde9cd0 100644 +--- a/ui-repolist.c ++++ b/ui-repolist.c +@@ -53,7 +53,7 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) + + strbuf_reset(&path); + strbuf_addf(&path, "%s/refs/heads/%s", repo->path, +- repo->defbranch ? repo->defbranch : "master"); ++ repo->defbranch ? repo->defbranch : "main"); + if (stat(path.buf, &s) == 0) { + *mtime = s.st_mtime; + r->mtime = *mtime; diff --git a/secrets/acme.age b/secrets/acme.age new file mode 100644 index 00000000..0a7be3b7 --- /dev/null +++ b/secrets/acme.age @@ -0,0 +1,10 @@ +age-encryption.org/v1 +-> ssh-ed25519 cvV2sw 9M8YWkJtggtWDra9rnc3iaf9qbXF+pdRaVtQbSMOQkY +/jxEwLo3+qmuyWIpQD65O2Kp0qEKJwydM4tFnXdvRfU +-> ssh-ed25519 hzg5VQ BLUqRuSfJXtSc/M1H1jTBwCWnkSZqm5SC+LrxIXNn34 +D1A2DDFQ7FK3bOPUvJJpumQM7MeESMHqhwZXxug6b34 +-> piv-p256 u9NeZg AkXH20bJj+m6TgPzvsPltDyOIPRAB9YR0MXx/b8DFFD2 +kGH6MvfeDaKgXf5Ba92PF4PwTRotSZglGQZO2impo1Q +--- wYsP2oTEuD/C40pKjx0LAYuoE9/w2LgxuDRGqsmcnCo +5ZPse!$lI}4o!6W1[c͆7;IO hn_"Ɗ[㉪}QܭR@:Mlw_cl]lk:mߊb}#WkUV;NYha!e:8éTC8l5[qc]U}}`NxdX1D# +ck \ No newline at end of file diff --git a/secrets/binarycache.age b/secrets/binarycache.age new file mode 100644 index 00000000..fae59d4d --- /dev/null +++ b/secrets/binarycache.age Binary files differdiff --git a/secrets/dyndns.age b/secrets/dyndns.age new file mode 100644 index 00000000..cd1668f1 --- /dev/null +++ b/secrets/dyndns.age Binary files differdiff --git a/secrets/identities/se.txt b/secrets/identities/se.txt new file mode 100644 index 00000000..e1c6b851 --- /dev/null +++ b/secrets/identities/se.txt @@ -0,0 +1,4 @@ +# created: 2024-04-10T12:44:17Z +# access control: any biometry or passcode +# public key: age1se1qdx3wrvaxevk3g40ngqreqc9n4gl0rwcjdvnptz5vw96jjjuf2rv2wp8c5m +AGE-PLUGIN-SE-1QJPQZ7P3SGQHGVYP75XQYUNTXXQ7UVQTPSPKY6TYQSZ85T758GCYSRQRWP6KYPZPQ3X3WRVAXEVK3G40NGQREQC9N4GL0RWCJDVNPTZ5VW96JJJUF2RV2XR0REV8SUYMVLR9LK9VWDZGRRTNSKQL0ATZYYWS9NAZZACW5QMMXQYQCQMJDDHSYQGQXQRSCQNTWSPQZPPS9CXQYAMTQS599D2V5HHZE0VLL5MW9EW28X23MP9NRSULQL3GAHD0RU0M5EG3F38XWDKEJM6LPWTNQPCVQF3XXQSPPYCQWRQZDDMQYQGZXQTSCQMTD9JQGY8R2D8H498GF5PMR8WYFNAUD7L8XQNSCQMJDDKSGGYSRXZGXMRKCX08VHSJTFQWK28KT7SX2TYS6HLC3CQQUE303RKEEUC85RQZV4JRZAPSWGXQXCTRDSCKKVPFPSPK7CMTXY3RQGQVQD3HQMCVR9ZX2ANFVDJ57AMWV4EYZAT5DPJKUARFVDSHG6T0DCCQJRQYDAJX2MQPQYQNQ2SVQ3HHXEMWXY3RQGQVQD3HQMCVR9ZX2ANFVDJ57AMWV4EYZAT5DPJKUARFVDSHG6T0DCCQWRQZDASSZQGP9T2Q6F \ No newline at end of file diff --git a/secrets/paperless.age b/secrets/paperless.age new file mode 100644 index 00000000..5fe24928 --- /dev/null +++ b/secrets/paperless.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 cvV2sw ytN6X4qbcYAEijPOcZ3CV0BQOU5Osocy5Zv3ebnekF0 +WNzH2Gr0L1qKENdRalb44Xg0Ay4tD38+CED6crF3Nd4 +-> piv-p256 u9NeZg A4sl4hcJrAyDZxWkPn84u3gNXLZBj3guVya3vP60X3WT +8uVbdrw6ZNvpaYc056vqTMDraJYLMWviXt+LnhGQDn4 +--- m1ofHvgDQvjWZV9iU5ran6oG1pK+jfMKKiouQc9SYfo +c&$IנX8pcPoborVE.,.Z*"- `wLeӊxl f-4%9-vC90YP!n fsVO?CfӉCd_iW,lZ>J`9 \ No newline at end of file diff --git a/secrets/powerdns.age b/secrets/powerdns.age new file mode 100644 index 00000000..b4a3de03 --- /dev/null +++ b/secrets/powerdns.age @@ -0,0 +1,7 @@ +age-encryption.org/v1 +-> ssh-ed25519 cvV2sw 8XNRgmCnrZX7Gug5WDA9uBUPjXW+hz+NxGbpAfI5nBY +5DvhP919xM/ccBaRHjd+JnsiWNSbz3118p5iHUoDf8E +-> piv-p256 u9NeZg AlR0PR5A+mSyaT8wStnNKuWnO28YwUEwV/UPXK2JvlEi +xBtUPfZehUkzTeNTVk6FBZt4R/XfvKwzrkWipVJbHMY +--- mdXzr4rVzRNBekbnenHAXzr8SFYhHRJIO0/HaeL7QVI +$GAC[bmtty$Oc:<'`V<'/ XJ*!@լ!|g8ņ4ǞE`CpmAK- \ No newline at end of file diff --git a/secrets/secrets.nix b/secrets/secrets.nix new file mode 100644 index 00000000..86d1062c --- /dev/null +++ b/secrets/secrets.nix @@ -0,0 +1,31 @@ +let + users = { + alan = [ + "age1se1qdx3wrvaxevk3g40ngqreqc9n4gl0rwcjdvnptz5vw96jjjuf2rv2wp8c5m" # mba age-plugin-se + ]; + }; + + machines = { + linde = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHdh3J7dEmh9G+CVmzFEC8/ont35ZXpCFcpLUO863vC"; + nanopi = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/KOwhb4pyuw4U8hnkPAbRNk6o41Fmvsa67cY6MHA9k"; + }; + + secrets = with machines; { + acme = [ linde nanopi ]; + + binarycache = [ linde ]; + paperless = [ linde ]; + powerdns = [ linde ]; + + dyndns = [ nanopi ]; + syncthing = [ nanopi ]; + }; +in +builtins.listToAttrs ( + map + (secretName: { + name = "${secretName}.age"; + value.publicKeys = secrets.${secretName} ++ users.alan; + }) + (builtins.attrNames secrets) +) diff --git a/secrets/syncthing.age b/secrets/syncthing.age new file mode 100644 index 00000000..680dd1ce --- /dev/null +++ b/secrets/syncthing.age Binary files differdiff --git a/setup/hetzner.sh b/setup/hetzner.sh new file mode 100755 index 00000000..250a9211 --- /dev/null +++ b/setup/hetzner.sh @@ -0,0 +1,81 @@ +#! /usr/bin/env bash + +# Script to install NixOS from the Hetzner Cloud NixOS bootable ISO image. +# (tested with Hetzner's `NixOS 20.03 (amd64/minimal)` ISO image). +# +# This script wipes the disk of the server! +# +# Instructions: +# +# 1. Mount the above mentioned ISO image from the Hetzner Cloud GUI +# and reboot the server into it; do not run the default system (e.g. Ubuntu). +# 2. To be able to SSH straight in (recommended), you must replace hardcoded pubkey +# further down in the section labelled "Replace this by your SSH pubkey" by you own, +# and host the modified script way under a URL of your choosing +# (e.g. gist.github.com with git.io as URL shortener service). +# 3. Run on the server: +# +# # Replace this URL by your own that has your pubkey in +# curl -L https://home.alanpearce.eu/public/hetzner.sh | sudo bash +# +# This will install NixOS and power off the server. +# 4. Unmount the ISO image from the Hetzner Cloud GUI. +# 5. Turn the server back on from the Hetzner Cloud GUI. +# +# To run it from the Hetzner Cloud web terminal without typing it down, +# you can either select it and then middle-click onto the web terminal, (that pastes +# to it), or use `xdotool` (you have e.g. 3 seconds to focus the window): +# +# sleep 3 && xdotool type --delay 50 'curl YOUR_URL_HERE | sudo bash' +# +# (In the xdotool invocation you may have to replace chars so that +# the right chars appear on the US-English keyboard.) +# +# If you do not replace the pubkey, you'll be running with my pubkey, but you can +# change it afterwards by logging in via the Hetzner Cloud web terminal as `root` +# with empty password. + +set -e + +# Hetzner Cloud OS images grow the root partition to the size of the local +# disk on first boot. In case the NixOS live ISO is booted immediately on +# first powerup, that does not happen. Thus we need to grow the partition +# by deleting and re-creating it. +sgdisk -d 1 /dev/sda +sgdisk -N 1 /dev/sda +partprobe /dev/sda + +mkfs.ext4 -F /dev/sda1 # wipes all data! + +mount /dev/sda1 /mnt + +nixos-generate-config --root /mnt + +# Delete trailing `}` from `configuration.nix` so that we can append more to it. +sed -i -E 's:^\}\s*$::g' /mnt/etc/nixos/configuration.nix + +# Extend/override default `configuration.nix`: +echo ' + boot.loader.grub.devices = [ "/dev/sda" ]; + + # Initial empty root password for easy login: + users.users.root.initialHashedPassword = ""; + services.openssh = { + permitRootLogin = "prohibit-password"; + enable = true; + }; + + programs.fish.enable = true; + users.users.root = { + initialHashedPassword = ""; + shell = "${pkgs.fish}/bin/fish"; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner" + ]; + }; +} +' >> /mnt/etc/nixos/configuration.nix + +nixos-install --no-root-passwd + +poweroff diff --git a/system/linde-hardware.nix b/system/linde-hardware.nix new file mode 100644 index 00000000..ba48156f --- /dev/null +++ b/system/linde-hardware.nix @@ -0,0 +1,38 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "virtio_pci" "virtio_scsi" "usbhid" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/09c12218-c189-439a-9ef1-846b87538841"; + fsType = "ext4"; + }; + + fileSystems."/boot/efi" = + { device = "/dev/disk/by-uuid/1C43-4EC4"; + fsType = "vfat"; + }; + + swapDevices = + [ { device = "/dev/disk/by-uuid/29793e8e-5c0d-4e5b-80e0-11252d786294"; } + ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; +} diff --git a/system/linde.nix b/system/linde.nix new file mode 100644 index 00000000..11818395 --- /dev/null +++ b/system/linde.nix @@ -0,0 +1,787 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running `nixos-help`). + +{ config, lib, pkgs, ... }: + +with lib; +let + netif = "enp1s0"; + hostname = "linde"; + net-ip4 = "116.203.248.56"; + net-mask4 = "32"; + net-gw = "172.31.1.1"; + net-ip6 = "2a01:4f8:c012:23a4::1"; + net-rdnsip = "2a01:4f8:c012:23a4::53"; + net-mask6 = "64"; + net-gw6 = "fe80::1"; +in +{ + imports = + [ + # Include the results of the hardware scan. + ./linde-hardware.nix + ]; + age.secrets = { + paperless = + let + cfg = config.services.paperless; + in + { + file = ../secrets/paperless.age; + path = "${cfg.dataDir}/nixos-paperless-secret-key"; + owner = cfg.user; + mode = "400"; + symlink = false; + }; + acme.file = ../secrets/acme.age; + binarycache.file = ../secrets/binarycache.age; + powerdns.file = ../secrets/powerdns.age; + }; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + boot.loader.efi.efiSysMountPoint = "/boot/efi"; + + time.timeZone = "Europe/Berlin"; + + i18n.defaultLocale = "en_GB.UTF-8"; + + environment.homeBinInPath = true; + environment.localBinInPath = true; + environment.systemPackages = with pkgs; [ + kitty.terminfo + htop + lsof + gitMinimal + powerdns + sqlite-interactive + knot-dns + + nixpkgs-review + nix-output-monitor + ]; + + programs.ssh = with pkgs; { + knownHostsFiles = [ + (writeText "github.keys" '' + # github.com:22 SSH-2.0-babeld-05989c77 + # github.com:22 SSH-2.0-babeld-05989c77 + # github.com:22 SSH-2.0-babeld-05989c77 + # github.com:22 SSH-2.0-babeld-05989c77 + # github.com:22 SSH-2.0-babeld-05989c77 + github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= + github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + '') + (writeText "gitlab.keys" '' + # gitlab.com:22 SSH-2.0-GitLab-SSHD + # gitlab.com:22 SSH-2.0-GitLab-SSHD + # gitlab.com:22 SSH-2.0-GitLab-SSHD + # gitlab.com:22 SSH-2.0-GitLab-SSHD + # gitlab.com:22 SSH-2.0-GitLab-SSHD + gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 + gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= + gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf + '') + (writeText "codeberg.keys" '' + # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 + # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 + # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 + # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 + # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2 + codeberg.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8hZi7K1/2E2uBX8gwPRJAHvRAob+3Sn+y2hxiEhN0buv1igjYFTgFO2qQD8vLfU/HT/P/rqvEeTvaDfY1y/vcvQ8+YuUYyTwE2UaVU5aJv89y6PEZBYycaJCPdGIfZlLMmjilh/Sk8IWSEK6dQr+g686lu5cSWrFW60ixWpHpEVB26eRWin3lKYWSQGMwwKv4LwmW3ouqqs4Z4vsqRFqXJ/eCi3yhpT+nOjljXvZKiYTpYajqUC48IHAxTWugrKe1vXWOPxVXXMQEPsaIRc2hpK+v1LmfB7GnEGvF1UAKnEZbUuiD9PBEeD5a1MZQIzcoPWCrTxipEpuXQ5Tni4mN + codeberg.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL2pDxWr18SoiDJCGZ5LmxPygTlPu+cCKSkpqkvCyQzl5xmIMeKNdfdBpfbCGDPoZQghePzFZkKJNR/v9Win3Sc= + codeberg.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIVIC02vnjFyL+I4RHfvIGNtOgJMe769VTF1VR4EB3ZB + '') + ]; + }; + + # Initial empty root password for easy login: + users.users.root.initialHashedPassword = ""; + services.openssh = { + enable = true; + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + }; + }; + services.sshguard = { + enable = true; + services = [ "sshd" ]; + }; + programs.mosh.enable = true; + + system.autoUpgrade = { + enable = true; + dates = "05:10"; + allowReboot = true; + flake = "git+file://${config.services.gitolite.dataDir}/repositories/nixfiles.git"; + flags = [ + "--no-write-lock-file" + "--update-input" + "nixpkgs" + ]; + }; + + nix = { + daemonCPUSchedPolicy = "batch"; + daemonIOSchedPriority = 6; + settings = { + max-jobs = 2; + auto-optimise-store = true; + trusted-users = [ "root" "nixremote" ]; + experimental-features = [ "nix-command" "flakes" ]; + }; + gc = { + automatic = true; + dates = "08:15"; + options = "--delete-older-than 14d"; + }; + optimise = { + automatic = true; + dates = [ "02:30" ]; + }; + }; + + services.nix-serve = { + enable = true; + secretKeyFile = config.age.secrets.binarycache.path; + }; + + programs.neovim = { + enable = true; + defaultEditor = true; + viAlias = true; + vimAlias = true; + }; + + networking = { + hostName = hostname; + useDHCP = false; + dhcpcd.enable = false; + nameservers = [ + "2606:4700:4700::1111" + "2606:4700:4700::1001" + "1.1.1.1" + "1.0.0.1" + ]; + hosts = lib.mkForce { + ${net-ip4} = [ "${hostname}.alanpearce.eu" hostname ]; + ${net-ip6} = [ "${hostname}.alanpearce.eu" hostname ]; + ${net-rdnsip} = [ "dns" ]; + }; + firewall = { + enable = true; + allowPing = true; + pingLimit = "--limit 60/minute --limit-burst 30"; + logRefusedConnections = false; + allowedTCPPorts = [ + 22 + 80 + 443 + 53 + 853 + 9418 + 6922 + ]; + allowedUDPPorts = [ + 53 + 3478 + 6885 # DHT + 6922 + ]; + }; + resolvconf = { + enable = true; + useLocalResolver = false; + }; + }; + services.resolved.enable = false; + systemd.network = { + enable = true; + networks.${netif} = + { + name = netif; + gateway = [ net-gw ]; + routes = [{ + routeConfig = { + Gateway = net-gw6; + PreferredSource = net-ip6; + }; + }]; + address = [ + "${net-ip6}/${net-mask6}" + "${net-rdnsip}/${net-mask6}" + ]; + addresses = [{ + addressConfig = { + Address = "${net-ip4}/${net-mask4}"; + Peer = "${net-gw}/32"; + }; + }]; + }; + }; + + services.journald.extraConfig = '' + MaxRetentionSec=1 month + ''; + + boot.kernel.sysctl = { + "net.ipv4.tcp_allowed_congestion_control" = "bbr illinois reno"; + "net.ipv4.tcp_congestion_control" = "bbr"; + "net.core.default_qdisc" = "fq"; + }; + + security.sudo.execWheelOnly = true; + security.sudo.extraConfig = '' + Defaults:root,%wheel env_keep+=EDITOR + ''; + + nixpkgs = { + config.allowUnfree = true; + overlays = [ + (self: super: { + cgit-pink = super.cgit-pink.overrideAttrs (old: { + patches = [ ../patches/cgit-pink.patch ]; + }); + }) + ]; + }; + + programs.fish = { + enable = true; + interactiveShellInit = '' + set --universal fish_greeting "" + ''; + }; + programs.zsh.enable = true; + users.users.root.shell = "${pkgs.fish}/bin/fish"; + users.users.alan = { + shell = "${pkgs.fish}/bin/fish"; + extraGroups = [ "wheel" "caddy" "docker" ]; + isNormalUser = true; + home = "/home/alan"; + createHome = true; + + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner.strongbox" + ]; + }; + + users.users.nixremote = { + shell = "/bin/sh"; + isNormalUser = true; + home = "/var/lib/nixremote/"; + createHome = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBxa7lxDu0M4chats/VvpFzjT3ruexKa3J9UC6ASo3bN root@NanoPi.lan" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9of82WBHK8nr8L9RGeieLMfcAWaFCeCkmvYHM9LCuT nanopi" + ]; + }; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? + + services.powerdns = { + enable = true; + secretFile = config.age.secrets.powerdns.path; + extraConfig = '' + launch=gsqlite3 + dnsupdate=yes + allow-dnsupdate-from=0.0.0.0/0,::/0 + only-notify= + also-notify=216.218.130.2 + allow-axfr-ips=216.218.133.2,2001:470:600::2 + outgoing-axfr-expand-alias=yes + expand-alias=yes + resolver=1.1.1.1 + local-address=${net-ip4} ${net-ip6} + reuseport=yes + log-dns-details=no + log-dns-queries=no + loglevel=5 + primary=yes + secondary=yes + send-signed-notify=no + prevent-self-notification=no + + default-soa-edit=inception-increment + + api=yes + # replaced by secretFile/envsubst + api-key=$API_KEY + + gsqlite3-database=/var/db/pdns/zones.db + gsqlite3-pragma-foreign-keys=yes + gsqlite3-dnssec=yes + ''; + }; + + systemd.services.hagezi-blocklist-update = { + enable = true; + startAt = "daily"; + serviceConfig = { + CacheDirectory = "blocklist"; + UMask = "0077"; + DynamicUser = "yes"; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = "AF_INET AF_INET6"; + RestrictNamespaces = true; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = "true"; + SystemCallFilter = [ + "~@clock" + "~@cpu-emulation" + "~@debug" + "~@module" + "~@mount" + "~@obsolete" + "~@privileged" + "~@raw-io" + "~@reboot" + "~@resources" + "~@swap" + ]; + SystemCallArchitectures = "native"; + CapabilityBoundingSet = ""; + DevicePolicy = "closed"; + ProcSubset = "pid"; + NoNewPrivileges = true; + ExecStart = "${pkgs.curl}/bin/curl --no-progress-meter --output %C/blocklist/hagezi.rpz https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/pro.plus.txt"; + # https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/pro.plus.txt" + ExecStartPost = [ + "+/bin/sh -c 'exec install --compare --mode=644 %C/blocklist/hagezi.rpz /etc/knot-resolver/blocklist.rpz'" + "-/bin/sh -c 'exec rm -f %C/blocklist/hagezi.rpz'" + ]; + Environment = [ + "HOME=%C/blocklist" + ]; + }; + }; + + services.kresd = { + enable = true; + # package = pkgs.knot-resolver.override { extraFeatures = true; }; + listenPlain = [ + "[${net-rdnsip}]:53" + ]; + listenTLS = [ + "127.0.0.1:853" + "[::1]:853" + "${net-ip4}:853" + "[${net-ip6}]:853" + ]; + listenDoH = [ + "[::1]:443" + "127.0.0.1:443" + ]; + instances = 2; + extraConfig = '' + modules = { + 'rebinding < iterate', + 'hints > iterate', + 'serve_stale < cache', + 'stats', + predict = { + window = 30, + period = 24 * (60/30), + }, + 'nsid', + } + + local systemd_instance = os.getenv("SYSTEMD_INSTANCE") + nsid.name(systemd_instance) + + log_groups({ 'policy' }) + + cache.size = 500 * MB + + net.tls( + '/var/lib/acme/dns.alanpearce.eu/cert.pem', + '/var/lib/acme/dns.alanpearce.eu/key.pem' + ) + + -- override blocklist + policy.add(policy.suffix(policy.PASS, policy.todnames({ + }))) + + policy.add(policy.rpz( + policy.DENY_MSG('domain blocked by hagezi'), + '/etc/knot-resolver/blocklist.rpz', + false -- needs wrapped kresd + -- true -- will watch the file for updates + )) + + -- disable DNSSEC when using Quad9 since they do it + -- trust_anchors.remove('.') + -- policy.add(policy.all(policy.TLS_FORWARD({ + -- {'2620:fe::fe', hostname='dns.quad9.net'}, + -- {'2620:fe::9', hostname='dns.quad9.net'}, + -- {'9.9.9.9', hostname='dns.quad9.net'}, + -- {'149.112.122.122', hostname='dns.quad9.net'}, + -- }))) + ''; + }; + + users.groups.ntfy = { }; + users.users.ntfy = { + isSystemUser = true; + group = "ntfy"; + }; + services.ntfy-sh = { + enable = true; + user = "ntfy"; + group = "ntfy"; + settings = { + base-url = "https://ntfy.alanpearce.eu"; + listen-http = ":2586"; + behind-proxy = true; + manager-interval = "1h"; + cache-startup-queries = '' + PRAGMA journal_mode = WAL; + PRAGMA busy_timeout = 5000; + PRAGMA synchronous = NORMAL; + ''; + cache-file = "/var/cache/ntfy/cache.db"; + attachment-cache-dir = "/var/cache/ntfy/attachments"; + auth-default-access = "deny-all"; + auth-file = "/var/lib/ntfy/user.db"; + upstream-base-url = "https://ntfy.sh"; + }; + }; + systemd.services.ntfy-sh = { + serviceConfig = { + DynamicUser = lib.mkForce false; + StateDirectory = lib.mkForce "ntfy"; + RuntimeDirectory = "ntfy"; + CacheDirectory = "ntfy"; + }; + }; + + systemd.services.backup-etc-nixos = { + startAt = "04:30"; + path = with pkgs; [ + rdiff-backup + openssh + ]; + script = '' + rdiff-backup --api-version 201 backup /etc/nixos ${hostname}@home.alanpearce.eu::nixos + rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::nixos + ''; + serviceConfig = { + Type = "oneshot"; + }; + }; + + systemd.services.backup-gitolite = { + startAt = "daily"; + path = with pkgs; [ + rdiff-backup + openssh + ]; + script = '' + rdiff-backup --api-version 201 backup ${config.services.gitolite.dataDir} ${hostname}@home.alanpearce.eu::gitolite + rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::gitolite + ''; + serviceConfig.Type = "oneshot"; + }; + + systemd.services.backup-paperless = { + startAt = "daily"; + path = with pkgs; [ + sudo + rdiff-backup + openssh + ]; + script = '' + sudo -u paperless ./paperless-manage document_exporter --delete --use-filename-format --no-archive --no-thumbnail --no-progress-bar ./export + rdiff-backup --api-version 201 backup ./export ${hostname}@home.alanpearce.eu::paperless + rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::paperless + ''; + serviceConfig = { + Type = "oneshot"; + WorkingDirectory = config.services.paperless.dataDir; + }; + }; + + security.acme = { + defaults = { + email = "alan@alanpearce.eu"; + dnsProvider = "pdns"; + dnsResolver = "1.1.1.1:53"; + credentialsFile = config.age.secrets.acme.path; + reloadServices = [ "caddy" ]; + validMinDays = 32; + }; + acceptTerms = true; + certs."alanpearce.eu" = { + extraDomainNames = [ "*.alanpearce.eu" ]; + }; + certs."dns.alanpearce.eu" = { + reloadServices = map (x: "kresd@${toString x}") (range 1 config.services.kresd.instances); + group = "knot-resolver"; + }; + }; + users.groups.acme.members = [ + "caddy" + ]; + + services.caddy = { + enable = true; + group = "caddy"; + globalConfig = '' + auto_https disable_certs + default_bind ${net-ip6} ${net-ip4} + ''; + virtualHosts = { + "http://" = { + # Needed for HTTP->HTTPS servers + }; + "${hostname}.alanpearce.eu" = { + serverAliases = [ "https://" ]; + useACMEHost = "alanpearce.eu"; + extraConfig = '' + respond * 204 + ''; + }; + "pdns.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + log { + output discard + } + reverse_proxy 127.0.0.1:8081 + ''; + }; + "dns.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + log { + output discard + } + reverse_proxy localhost:443 { + transport http { + tls_server_name dns.alanpearce.eu + } + } + ''; + }; + "files.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + root * /srv/http/files + encode gzip zstd + file_server browse + ''; + }; + "git.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + root * ${pkgs.cgit-pink}/cgit/ + encode gzip zstd + handle_path /custom/* { + file_server { + root /srv/http/cgit/ + } + } + rewrite /robots.txt /assets/robots.txt + handle_path /assets/* { + file_server { + hide cgit.cgi + } + } + @git_http_backend path_regexp "^/.+/(info/refs|git-upload-pack)$" + handle @git_http_backend { + reverse_proxy unix/run/fcgiwrap.sock { + transport fastcgi { + env SCRIPT_FILENAME ${pkgs.git}/libexec/git-core/git-http-backend + env GIT_PROJECT_ROOT ${config.services.gitolite.dataDir}/repositories + } + } + } + handle { + reverse_proxy unix/run/fcgiwrap.sock { + transport fastcgi { + env SCRIPT_FILENAME {http.vars.root}/cgit.cgi + env CGIT_CONFIG ${pkgs.writeText "cgitrc" '' + head-include=/srv/http/cgit/responsive-cgit-css-master/head.html + css=/custom/custom.css + virtual-root=/ + logo= + readme=:README.md + source-filter=${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py + about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh + enable-git-config=1 + enable-index-owner=0 + enable-index-links=1 + enable-follow-links=0 + enable-log-linecount=1 + max-stats=year + snapshots=tar.lz tar.zst zip + cache-size=10240 + enable-http-clone=1 + enable-commit-graph=1 + mimetype-file=${pkgs.nginx}/conf/mime.types + section-from-path=1 + side-by-side-diffs=1 + noplainemail=1 + repository-sort=age + root-title=my personal projects + clone-url=git://git.alanpearce.eu/$CGIT_REPO_URL https://git.alanpearce.eu/$CGIT_REPO_URL + remove-suffix=1 + strict-export=git-daemon-export-ok + scan-path=${config.services.gitolite.dataDir}/repositories/ + ''} + } + } + } + ''; + }; + "ntfy.alanpearce.eu" = { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + reverse_proxy localhost${config.services.ntfy-sh.settings.listen-http} + ''; + }; + "legit.alanpearce.eu" = + let + server = config.services.legit.settings.server; + in + { + useACMEHost = "alanpearce.eu"; + extraConfig = '' + handle_path /static/* { + root * /srv/http/legit/src/static + file_server + } + reverse_proxy ${server.host}:${toString server.port} + ''; + }; + "papers.alanpearce.eu" = { + extraConfig = '' + handle_path /static/* { + root * ${config.services.paperless.package}/lib/paperless-ngx/static + file_server + } + reverse_proxy localhost:${toString config.services.paperless.port} + + ''; + }; + "binarycache.alanpearce.eu" = + let + ns = config.services.nix-serve; + in + { + extraConfig = '' + reverse_proxy ${ns.bindAddress}:${toString ns.port} + ''; + }; + }; + }; + systemd.services.caddy.serviceConfig = { + UMask = "007"; + }; + + services.fcgiwrap = { + enable = true; + group = "gitolite"; + }; + services.gitolite = { + enable = true; + adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner.strongbox"; + extraGitoliteRc = '' + $RC{UMASK} = 0027; + $RC{LOG_EXTRA} = 0; + $RC{HOSTNAME} = "${config.networking.hostName}"; + $RC{LOCAL_CODE} = "$rc{GL_ADMIN_BASE}/local"; + push( @{$RC{ENABLE}}, 'D' ); + push( @{$RC{ENABLE}}, 'Shell alan' ); + push( @{$RC{ENABLE}}, 'cgit' ); + push( @{$RC{ENABLE}}, 'repo-specific-hooks' ); + ''; + }; + services.legit = { + enable = true; + group = "gitolite"; + settings = { + server.name = "legit.alanpearce.eu"; + dirs = { + templates = "/srv/http/legit/src/templates"; + }; + repo = { + scanPath = "/srv/http/legit/repos"; + readme = [ + "readme" + "readme.md" + "README.md" + ]; + }; + }; + }; + + users.groups.git.gid = config.ids.gids.git; + services.gitDaemon = { + enable = true; + user = "git"; + group = "gitolite"; + basePath = "${config.services.gitolite.dataDir}/repositories/"; + }; + + users.groups.paperless.members = [ "alan" "syncthing" ]; + services.paperless = { + enable = true; + package = pkgs.paperless-ngx; + dataDir = "/srv/paperless"; + settings = { + PAPERLESS_DBENGINE = "sqlite"; + PAPERLESS_TIME_ZONE = "Europe/Berlin"; + + PAPERLESS_URL = "https://papers.alanpearce.eu"; + PAPERLESS_TRUSTED_PROXIES = "127.0.0.1"; + PAPERLESS_USE_X_FORWARD_HOST = true; + PAPERLESS_USE_X_FORWARD_PORT = true; + PAPERLESS_PROXY_SSL_HEADER = [ "HTTP_X_FORWARDED_PROTO" "https" ]; + PAPERLESS_ENABLE_COMPRESSION = false; # let caddy do it + + PAPERLESS_OCR_SKIP_ARCHIVE_FILE = "with_text"; + PAPERLESS_OCR_LANGUAGE = "deu+eng"; + PAPERLESS_IGNORE_DATES = "09.08.90"; + + PAPERLESS_TASK_WORKERS = 2; + PAPERLESS_THREADS_PER_WORKER = 1; + PAPERLESS_NUMBER_OF_SUGGESTED_DATES = 4; + + PAPERLESS_CONSUMER_IGNORE_PATTERN = [ ".DS_STORE/*" "desktop.ini" ".stfolder/*" ".stversions/*" ]; + + PAPERLESS_FILENAME_FORMAT = "{correspondent}/{created} {title} {asn}"; + PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = true; + }; + }; + + services.syncthing = { + enable = true; + dataDir = "/srv/syncthing"; + configDir = "/var/lib/syncthing"; + overrideDevices = false; + overrideFolders = false; + }; +} diff --git a/system/nanopi-hardware.nix b/system/nanopi-hardware.nix new file mode 100644 index 00000000..7f7d03ca --- /dev/null +++ b/system/nanopi-hardware.nix @@ -0,0 +1,35 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "nvme" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/6adfb32b-ebe9-4116-86e8-829d2c9dc79d"; + fsType = "ext4"; + }; + + fileSystems."/mnt/sd" = + { device = "/dev/disk/by-uuid/79d9c190-1728-42ae-8cfd-b03d4a10bdb3"; + fsType = "ext4"; + }; + + fileSystems."/boot" = + { device = "/mnt/sd/boot"; + fsType = "none"; + options = [ "bind" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; +} diff --git a/system/nanopi.nix b/system/nanopi.nix new file mode 100755 index 00000000..3b10e606 --- /dev/null +++ b/system/nanopi.nix @@ -0,0 +1,859 @@ +{ config +, pkgs +, lib +, inputs +, ... +}: +let + fsTypes = [ "f2fs" "ext" "exfat" "vfat" ]; +in +{ + imports = [ + ./nanopi-hardware.nix + (inputs.nixos-hardware + "/friendlyarm/nanopi-r5s") + ]; + + age.secrets = { + dyndns.file = ../secrets/dyndns.age; + acme.file = ../secrets/acme.age; + syncthing.file = ../secrets/syncthing.age; + }; + + boot = { + supportedFilesystems = fsTypes; + initrd.supportedFilesystems = fsTypes; + + loader.timeout = 1; + kernelPatches = lib.mkForce [ ]; + }; + + systemd.services."irqbalance-oneshot" = { + enable = true; + description = "Distribute interrupts after boot using \"irqbalance --oneshot\""; + documentation = [ "man:irqbalance" ]; + wantedBy = [ "sysinit.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.irqbalance.out}/bin/irqbalance --foreground --oneshot"; + }; + }; + systemd.tmpfiles.settings."leds-off" = { + "/sys/class/leds/green:*/brightness" = { + w = { + argument = "0"; + }; + }; + }; + + services.udev.extraRules = '' + # set scheduler for NVMe + ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="kyber" + # set scheduler for SSD and eMMC + ACTION=="add|change", KERNEL=="sd[a-z]|mmcblk[0-9]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline" + # set scheduler for rotating disks + ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="kyber" + ''; + + systemd.services.dynamic-dns-update = { + enable = true; + startAt = [ "hourly" ]; + description = "Update IP addresses"; + path = with pkgs; [ curl iproute2 dig.dnsutils miller ]; + after = [ "sys-devices-platform-fe2a0000.ethernet-net-wan0.device" ]; + bindsTo = [ "sys-devices-platform-fe2a0000.ethernet-net-wan0.device" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "/bin/sh /etc/nixos/update-ip ${config.age.secrets.dyndns.path}"; + }; + }; + + services.journald.extraConfig = '' + MaxRetentionSec=1 month + ''; + + environment.systemPackages = with pkgs; [ + kitty.terminfo + htop + lsof + usbutils + lzop + zstd + sqlite + ]; + + systemd.network.config.networkConfig = { + SpeedMeter = true; + }; + networking = { + hostName = "nanopi"; + domain = "lan"; + useDHCP = false; + useNetworkd = true; + nameservers = [ + "176.9.93.198" + "176.9.1.117" + "2a01:4f8:151:34aa::198" + "2a01:4f8:141:316d::117" + ]; + firewall = { + enable = true; + rejectPackets = true; + logRefusedConnections = false; + pingLimit = "5/second"; + filterForward = true; # we are a router + allowedUDPPorts = [ + 53 + 123 + ]; + allowedTCPPorts = [ + 53 + 123 + 80 + 443 + ]; + interfaces.bridge0 = { + allowedTCPPorts = [ + 53 + 67 + 139 + 445 + 1883 + 3689 + 5357 + 5533 # SmartDNS + 8096 + 9091 # Transmission + 8096 # Jellyfin + ]; + allowedUDPPorts = [ + 53 + 67 + 69 + 137 + 4011 # PXE + 5533 # SmartDNS + 5353 + 5355 # LLMNR + 1900 # DLNA Jellyfin + 3702 # Samba WSDD + 21027 # Syncthing LNDP + 41641 + 51827 + ]; + }; + interfaces.wan0 = { + allowedTCPPorts = [ + 6980 # aria2c + ]; + allowedUDPPorts = [ + 6976 + 6980 + 41641 + ]; + }; + extraForwardRules = '' + iifname { "wan0", "wlan0", "wwan0" } oifname { "lan1", "lan2", "bridge0" } icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept + iifname { "lan1", "lan2", "bridge0" } oifname { "wan0", "wlan0", "wwan0" } accept + ''; + }; + nftables = { + enable = true; + tables = { + firewall = { + family = "inet"; + content = '' + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oifname { "wan0", "wlan0", "wwan0" } masquerade + } + chain prerouting { + type nat hook prerouting priority dstnat; + iifname "wan0" tcp dport { 6922, 51413 } dnat ip to 10.0.0.42 + } + ''; + }; + }; + }; + wireless = { + enable = true; + # iwd = { + # enable = true; + # settings = { + # Network = { + # RoutePriorityOffset = 300; + # }; + # }; + # }; + }; + }; + services.resolved.enable = false; + + programs.command-not-found.enable = false; + + services.openssh = { + enable = true; + openFirewall = true; + startWhenNeeded = false; + }; + programs.mosh.enable = true; + services.sshguard = { + enable = true; + services = [ "sshd" ]; + }; + + systemd.network = { + enable = true; + wait-online = { + ignoredInterfaces = [ "wan0" "wlan0" "wwan0" ]; + }; + links = { + "10-name-lan1" = { + matchConfig.Path = "platform-3c0000000.pcie-pci-0000:01:00.0"; + linkConfig = { + Name = "lan1"; + MACAddress = "a8:95:85:0d:67:38"; + }; + }; + "10-name-lan2" = { + matchConfig.Path = "platform-3c0400000.pcie-pci-0001:01:00.0"; + linkConfig = { + Name = "lan2"; + MACAddress = "a8:95:85:0d:67:39"; + }; + }; + "10-name-wan0" = { + matchConfig.Path = "platform-fe2a0000.ethernet"; + linkConfig = { + Name = "wan0"; + MACAddress = "a8:95:85:0d:67:3a"; + }; + }; + "10-name-wlan0" = { + matchConfig.MACAddress = "9c:53:22:33:bf:e9"; + linkConfig = { + Name = "wlan0"; + }; + }; + "10-name-wwan0" = { + matchConfig.MACAddress = "34:4b:50:00:00:00"; + linkConfig = { + Name = "wwan0"; + }; + }; + }; + netdevs = { + "20-bridge" = { + netdevConfig = { + Kind = "bridge"; + Name = "bridge0"; + }; + }; + }; + networks = { + "30-lan-ports" = { + matchConfig.Name = "lan*"; + bridge = [ "bridge0" ]; + linkConfig = { + MACAddress = "82:E0:06:9C:8E:7C"; + RequiredForOnline = "no"; + }; + networkConfig.LinkLocalAddressing = "no"; + }; + "40-bridge0" = { + matchConfig.Name = "bridge0"; + linkConfig.RequiredForOnline = "routable"; + address = [ + "10.0.0.1/20" + "fd12:d04f:65d:42::1/56" + ]; + networkConfig = { + IPv6AcceptRA = false; + IPv6SendRA = true; + DHCPPrefixDelegation = true; + ConfigureWithoutCarrier = true; + }; + dhcpPrefixDelegationConfig = { + UplinkInterface = "wan0"; + SubnetId = "42"; + Assign = true; + Token = "::1"; + }; + ipv6SendRAConfig = { + RouterLifetimeSec = 1800; + EmitDNS = true; + DNS = "fd12:d04f:65d:42::1"; + EmitDomains = true; + Domains = [ config.networking.domain ]; + }; + }; + "50-wwan0" = { + matchConfig.Name = "wwan0"; + linkConfig.RequiredForOnline = false; + networkConfig = { + DHCP = "yes"; + IPv6AcceptRA = true; + IPForward = "yes"; + }; + dhcpV4Config = { + UseDNS = false; + SendHostname = false; + RouteMetric = 2048; + }; + ipv6AcceptRAConfig.UseDNS = false; + routes = [ + { + routeConfig = { + Gateway = "_dhcp4"; + QuickAck = true; + InitialCongestionWindow = 30; + InitialAdvertisedReceiveWindow = 30; + }; + } + ]; + cakeConfig = { + Bandwidth = "1M"; + OverheadBytes = 18; + MPUBytes = 64; + CompensationMode = "none"; + NAT = true; + PriorityQueueingPreset = "diffserv8"; + }; + }; + "50-wan" = { + matchConfig.Name = "wan0"; + linkConfig.RequiredForOnline = "no"; + networkConfig = { + DHCP = "yes"; + IPv6AcceptRA = true; + IPForward = "yes"; + }; + dhcpV4Config = { + UseDNS = false; + SendHostname = false; + SendRelease = false; + UseHostname = false; + # Label = "wan0:1"; + }; + dhcpV6Config = { + UseDNS = false; + RapidCommit = true; + PrefixDelegationHint = "::/56"; + }; + dhcpPrefixDelegationConfig = { + UplinkInterface = ":self"; + }; + ipv6AcceptRAConfig = { + UseDNS = false; + }; + addresses = [ + { + addressConfig = { + Address = "192.168.100.10/24"; + # Peer = "192.168.100.1/32"; + Label = "wan0:0"; + # Scope = "link"; + }; + } + ]; + cakeConfig = { + Bandwidth = "24M"; + OverheadBytes = 18; + MPUBytes = 64; + CompensationMode = "none"; + NAT = true; + PriorityQueueingPreset = "diffserv8"; + }; + }; + "60-wlan" = { + matchConfig.MACAddress = "9c:53:22:33:bf:e9"; + linkConfig.RequiredForOnline = "no"; + networkConfig = { + DHCP = "yes"; + IPForward = "yes"; + IgnoreCarrierLoss = "3s"; + }; + dhcpV4Config = { + UseDNS = false; + SendHostname = false; + SendRelease = true; + UseHostname = false; + RouteMetric = 2048; + }; + routes = [ + { + routeConfig = { + Metric = 2048; + Gateway = "_dhcp4"; + QuickAck = true; + InitialCongestionWindow = 30; + InitialAdvertisedReceiveWindow = 30; + }; + } + ]; + cakeConfig = { + Bandwidth = "1M"; + OverheadBytes = 18; + MPUBytes = 64; + CompensationMode = "none"; + NAT = true; + PriorityQueueingPreset = "diffserv8"; + }; + }; + }; + }; + boot.kernelModules = [ + "tcp_lp" + ]; + boot.kernel.sysctl = { + "net.ipv4.conf.bridge0.send_redirects" = 1; + "net.ipv4.conf.bridge0.accept_source_route" = 1; + "net.ipv4.tcp_slow_start_after_idle" = 0; + "net.ipv4.tcp_ecn" = 1; + "net.ipv4.tcp_fastopen" = "0x3"; + "net.ipv4.tcp_allowed_congestion_control" = "reno cubic lp"; + "net.core.default_qdisc" = "fq"; + }; + + services.dnsmasq = { + enable = true; + resolveLocalQueries = true; + alwaysKeepRunning = true; + settings = { + local-ttl = 60; + domain = "lan"; + dhcp-fqdn = false; + domain-needed = true; + bogus-priv = true; + no-resolv = true; + no-negcache = true; + strict-order = true; + log-queries = false; + server = [ + "9.9.9.9" + "149.112.112.112" + "2620::fe:fe" + "2620::fe:9" + "116.203.248.56" + "2a01:4f8:c012:23a4::1" + # "127.0.0.1#5553" + # "::1#5553" + "127.0.0.1#5533" + "::1#5533" + ]; + localise-queries = true; + cname = [ + "homeassistant,ha" + ]; + interface-name = [ + "home.alanpearce.eu,wan0" + "nanopi.alanpearce.eu,wan0" + "nanopi.lan.alanpearce.eu,bridge0" + "syncthing.lan.alanpearce.eu,bridge0" + "wan,wan0" + "wlan,wlan0" + "wwan,wwan0" + ]; + interface = [ + "bridge0" + ]; + # auth-zone = "lan,wan0"; + # auth-server = [ + # "nanopi.alanpearce.eu,wan0" + # ]; + bind-interfaces = false; + + no-hosts = true; + + enable-ra = true; + dhcp-lease-max = 240; + dhcp-authoritative = true; + dhcp-rapid-commit = true; + dhcp-range = [ + "10.0.1.0,10.0.1.250,12h" + "::, constructor:bridge0, ra-stateless, 48h" + "fd12:d04f:65d::, ra-stateless, ra-names, 48h" + ]; + dhcp-host = [ + "00:a0:de:b3:0c:01,10.0.0.50,wxa-50" + "10:f0:68:12:b1:e0,10.0.0.11,Ruckus" + "9c:93:4e:ad:05:c8,10.0.0.210,xerox-b210" + "00:08:9b:f5:b8:25,10.0.0.42,dontpanic" + "d8:3a:dd:34:85:cc,d8:3a:dd:34:85:cd,10.0.0.81,ha" + ]; + dhcp-option = [ + "option:ntp-server,0.0.0.0" + "option:dns-server,0.0.0.0,10.0.0.81" + "option:tftp-server,0.0.0.0" + "option:ip-forward-enable,0" # ip-forwarding + "252,\"\\n\"" + ]; + dhcp-name-match = "set:wpad-ignore,wpad"; + dhcp-ignore-names = "tag:wpad-ignore"; + + tftp-root = "/srv/tftp/"; + dhcp-boot = [ + "tag:bios,netboot.xyz.kpxe" + "tag:efi32,netboot.xyz.efi" + "tag:efi32-1,netboot.xyz.efi" + "tag:efi64,netboot.xyz.efi" + "tag:efi64-1,netboot.xyz.efi" + "tag:efi64-2,netboot.xyz.efi" + ]; + dhcp-match = [ + "set:bios,60,PXEClient:Arch:00000" + "set:efi32,60,PXEClient:Arch:00002" + "set:efi32-1,60,PXEClient:Arch:00006" + "set:efi64,60,PXEClient:Arch:00007" + "set:efi64-1,60,PXEClient:Arch:00008" + "set:efi64-2,60,PXEClient:Arch:00009" + ]; + }; + }; + systemd.services.dnsmasq.wants = [ "network-online.target" ]; + + services.networkd-dispatcher = { + enable = true; + rules = { + update-home-address = { + onState = [ "configured" "configuring" ]; + script = '' + #!${pkgs.runtimeShell} + set -eu + + if [[ $IFACE == "wan0" && $OperationalState == "routable" ]] + then + systemctl start dynamic-dns-update.service + fi + exit 0 + ''; + }; + }; + }; + + system.stateVersion = "23.05"; + + programs.fish = { + enable = true; + }; + programs.neovim = { + enable = true; + defaultEditor = true; + vimAlias = true; + viAlias = true; + }; + + users.users.root.shell = "${pkgs.fish}/bin/fish"; + + users.users.alan = { + description = "Alan Pearce"; + isNormalUser = true; + extraGroups = [ "wheel" "lp" "scanner" "dialout" ]; + shell = "${pkgs.fish}/bin/fish"; + home = "/home/alan"; + uid = 1000; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMvcW4Z9VxOQgEJjsRC1uSMwEJ4vru9BwjT+Z50nawp4 lan" + ]; + }; + + users.groups = { + linde.members = [ ]; + }; + users.users = { + linde = { + group = "linde"; + description = "Backup user for system 'linde'"; + isSystemUser = true; + shell = "/bin/sh"; + home = "/srv/backup/linde"; + createHome = true; + packages = with pkgs; [ rdiff-backup ]; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ74cPdIX9OlDkzHb6Y1E5sWqtIqMaf0z/SN3Tfy1Fjl root@linde" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINNXwIdGcP1vKyjmgeLw/sJntn7lajaZivepgdzaXvOt rdiff-backup" + ]; + }; + }; + + nix = { + distributedBuilds = true; + buildMachines = [ + { + protocol = "ssh-ng"; + sshUser = "nixremote"; + hostName = "linde.alanpearce.eu"; + system = "aarch64-linux"; + sshKey = "/root/.ssh/id_buche.alanpearce.eu_nixremote"; + maxJobs = 2; + speedFactor = 4; + supportedFeatures = [ ]; + } + ]; + settings = { + builders-use-substitutes = true; + max-jobs = 2; + auto-optimise-store = true; + experimental-features = [ "nix-command" "flakes" ]; + substituters = [ "https://binarycache.alanpearce.eu" ]; + trusted-public-keys = [ + "mba-1:CxokFjx7YAQWPWMJJKcP50ZpcPUCAFEOrtWdNUMTVjw=" + "binarycache.alanpearce.eu:ZwqO3XMuajPictjwih8OY2+RXnOKpjZEZFHJjGSxAI4=" + ]; + }; + daemonCPUSchedPolicy = "batch"; + daemonIOSchedPriority = 6; + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + optimise = { + automatic = true; + dates = [ "04:00" ]; + }; + }; + nixpkgs.config.allowUnfree = true; + nixpkgs.overlays = [ ]; + system.autoUpgrade = { + enable = false; + dates = "01:00"; + randomizedDelaySec = "59 min"; + channel = "https://nixos.org/channels/nixos-unstable-small"; + allowReboot = true; + rebootWindow = { + lower = "01:00"; + upper = "05:00"; + }; + }; + + services.miniupnpd = { + enable = false; + natpmp = true; + internalIPs = [ "bridge0" ]; + externalInterface = "wan0"; + }; + + users.groups.videos = { + members = [ "alan" "jellyfin" ]; + }; + services.jellyfin = { + enable = true; + }; + + users.users.syncthing = { + isSystemUser = true; + group = "syncthing"; + homeMode = "0755"; + }; + users.groups.syncthing.members = [ "alan" ]; + services.syncthing = { + enable = true; + openDefaultPorts = true; + dataDir = "/srv/syncthing"; + user = "syncthing"; + group = "syncthing"; + key = config.age.secrets.syncthing.path; + cert = toString (pkgs.writeText "syncthing.crt" '' + -----BEGIN CERTIFICATE----- + MIIBmjCCASCgAwIBAgIIUOEmXGFrrX0wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ + c3luY3RoaW5nMB4XDTIyMDcxMzEwMzIxOVoXDTQ5MTIzMTIzNTk1OVowFDESMBAG + A1UEAxMJc3luY3RoaW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPiJT41NqucQf + UXiBwt+yPYnMg9G8oTt9XNA72V99K46D7mIs1F/5oESlDiCSAngXPsajxRY7wyZV + VoiWegfiaBOGZmq+TyaLlQ5bq/hm/Mp/jVED/rUA+BggohoZZMa2oz8wPTAOBgNV + HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud + EwEB/wQCMAAwCgYIKoZIzj0EAwIDaAAwZQIwLp4Gv5EEmjRO9EphbYJ4jxEJks7E + oblgnTmhfWmVWmf9avJyeGB212VYu4X8cCKDAjEAn7tTB9Y6LZvYPaLSwUKY3EzF + hKTYCb7VA/P1dU3tTR1vSQxnu1DsiliD/XcKe2IK + -----END CERTIFICATE----- + ''); + settings = { + options = { + maxRecvKbps = 10240; + maxSendKbps = 1024; + globalAnnounceEnabled = false; + relaysEnabled = false; + natEnabled = false; + urAccepted = 4; + trafficClass = 1; + }; + overrideFolders = false; + overrideDevices = false; + }; + }; + + time.timeZone = "Europe/Berlin"; + + services.chrony = { + enable = true; + extraConfig = '' + rtcdevice /dev/rtc0 + rtcfile /var/lib/chrony/rtc + rtcautotrim 30 + + allow 10.0.0.0/8 + allow fd12:d04f:65d:42::0/56 + ''; + }; + + services.avahi = { + enable = true; + nssmdns4 = true; + denyInterfaces = [ "wan0" "wwan0" "wlan0" ]; + browseDomains = [ + "alanpearce.eu" + ]; + publish = { + enable = true; + hinfo = true; + addresses = true; + userServices = true; + workstation = true; + }; + }; + + services.samba = { + enable = true; + enableNmbd = false; + extraConfig = '' + log level = 1 + + interfaces = bridge0 + + min protocol = SMB2 + disable netbios = yes + smb ports = 445 + + socket options = IPTOS_LOWDELAY TCP_NODELAY SO_KEEPALIVE SO_RCVBUF=65536 SO_SNDBUF=65536 + max xmit = 131072 + min receivefile size = 131072 + + aio read size = 1 + aio write size = 1 + + load printers = no + disable spoolss = yes + + mdns name = mdns + + follow symlinks = yes + + veto files = /Thumbs.db/.DS_Store/._.DS_Store/.apdisk/ + delete veto files = yes + ''; + shares = { + public = { + path = "/srv/public"; + browseable = "yes"; + "guest ok" = "yes"; + "create mask" = "0666"; + "directory mask" = "0777"; + "read only" = "no"; + }; + Homes = { + "read only" = "no"; + "valid users" = "%S"; + "inherit acls" = "yes"; + }; + Videos = { + path = "/srv/videos"; + "valid users" = "alan"; + "create mask" = "0664"; + "directory mask" = "0775"; + "writeable" = "yes"; + }; + }; + }; + services.samba-wsdd = { + enable = true; + interface = "bridge0"; + }; + + security.acme = { + acceptTerms = true; + defaults.email = "tls@alanpearce.eu"; + certs."dns.alanpearce.eu" = { + reloadServices = map (x: "kresd@${toString x}") (lib.range 1 config.services.kresd.instances); + dnsProvider = "pdns"; + dnsResolver = "1.1.1.1:53"; + credentialsFile = config.age.secrets.acme.path; + group = "knot-resolver"; + }; + }; + + services.smartdns = { + enable = false; + bindPort = "5533"; + settings = { + bind = "[::]:5533"; + address = [ + "/use-application-dns.net/#" + ]; + server = [ + "[::1]:5553" + "10.0.0.1:53 -group lan -exclude-default-group" + ]; + nameserver = [ + "/lan/lan" + ]; + dualstack-ip-selection = true; + dualstack-ip-selection-threshold = 10; + dualstack-ip-allow-force-AAAA = false; + dnsmasq-lease-file = "/var/lib/dnsmasq/dnsmasq.leases"; + mdns-lookup = true; + }; + }; + + services.kresd = { + enable = true; + instances = 4; + listenPlain = [ "[::1]:5553" ]; + # listenTLS = [ "853" ]; + listenDoH = [ "[::1]:5443" ]; + extraConfig = '' + net.tls( + '/var/lib/acme/dns.alanpearce.eu/cert.pem', + '/var/lib/acme/dns.alanpearce.eu/key.pem' + ) + + -- Load useful modules + modules = { + 'serve_stale < cache', + 'workarounds < iterate', + 'hints > iterate', + 'nsid', + } + + local systemd_instance = os.getenv("SYSTEMD_INSTANCE") + nsid.name(systemd_instance) + + -- Cache size + cache.size = 500 * MB + + local internalDomains = policy.todnames({'lan.alanpearce.eu.', '10.in-addr.arpa.', '.172.in-addr.arpa.', '.168.192.in-addr.arpa.'}) + policy.add(policy.suffix(policy.FLAGS({'NO_CACHE'}), internalDomains)) + policy.add(policy.suffix(policy.STUB({'10.0.0.1'}), internalDomains)) + + -- disable duplicate DNSSEC validation when using Quad9 or private + trust_anchors.remove('.') + + -- policy.add(policy.all(policy.TLS_FORWARD({ + -- { "23.88.111.219", hostname="dns.alanpearce.eu" }, + -- { "2a01:4f8:c0c:d9ce::1", hostname="dns.alanpearce.eu" }, + -- }))) + + policy.add(policy.all(policy.TLS_FORWARD({ + {'9.9.9.11', hostname='dns11.quad9.net'}, + {'149.112.122.11', hostname='dns11.quad9.net'}, + {'2620:fe::11', hostname='dns11.quad9.net'}, + {'2620:fe::fe:11', hostname='dns11.quad9.net'} + }))) + + -- policy.add(policy.rpz( + -- policy.DENY_MSG('domain blocked by hblock'), + -- '/etc/knot-resolver/blocklist.rpz', + -- true + -- )) + ''; + }; +} |