From ce06f43476863da90dc60dcee606d2b6c5a89a8e Mon Sep 17 00:00:00 2001 From: sefidel Date: Wed, 29 Mar 2023 20:54:19 +0900 Subject: project: initial commit --- .gitignore | 3 + LICENSE | 17 ++ README.md | 11 + TODO.md | 16 ++ default.nix | 10 + flake.lock | 253 ++++++++++++++++++++ flake.nix | 44 ++++ lib/README.md | 13 + lib/attrs.nix | 26 ++ lib/default.nix | 18 ++ lib/misc.nix | 9 + lib/modules.nix | 54 +++++ lib/system.nix | 22 ++ modules/README.md | 9 + modules/cachix/caches/nix-community.nix | 12 + modules/cachix/default.nix | 13 + modules/flakes.nix | 36 +++ modules/nix.nix | 34 +++ modules/security.nix | 59 +++++ modules/services/acme.nix | 52 ++++ modules/services/akkoma/blocklist.toml | 163 +++++++++++++ modules/services/akkoma/default.nix | 95 ++++++++ modules/services/akkoma/favicon-withbg.png | Bin 0 -> 17246 bytes modules/services/akkoma/favicon.png | Bin 0 -> 16693 bytes modules/services/akkoma/logo.png | Bin 0 -> 1304 bytes modules/services/akkoma/logo.svg | 71 ++++++ modules/services/akkoma/robots.txt | 2 + modules/services/akkoma/terms-of-service.html | 26 ++ modules/services/cgit.nix | 121 ++++++++++ modules/services/coredns/_corefile.nix | 3 + modules/services/coredns/default.nix | 18 ++ modules/services/coturn.nix | 64 +++++ modules/services/dendrite.nix | 230 ++++++++++++++++++ modules/services/dovecot.nix | 18 ++ modules/services/element-web.nix | 47 ++++ modules/services/fail2ban.nix | 17 ++ modules/services/git-daemon.nix | 29 +++ modules/services/gitolite/default.nix | 108 +++++++++ modules/services/gitolite/fix-refs | 9 + modules/services/gitolite/rename | 63 +++++ modules/services/jitsi.nix | 38 +++ modules/services/ldap.nix | 76 ++++++ modules/services/matrix-bridge.nix | 200 ++++++++++++++++ modules/services/matrix-moderation.nix | 52 ++++ modules/services/metrics.nix | 165 +++++++++++++ modules/services/misskey/config/default.yml | 156 ++++++++++++ modules/services/misskey/default.nix | 88 +++++++ modules/services/nginx.nix | 37 +++ modules/services/nixos-mailserver.nix | 106 +++++++++ modules/services/postgresql.nix | 34 +++ modules/services/pubnix.nix | 20 ++ modules/services/sefidel-web.nix | 26 ++ modules/services/soju.nix | 48 ++++ modules/services/userweb.nix | 36 +++ modules/services/vikunja.nix | 50 ++++ modules/sops.nix | 21 ++ overlays/README.md | 4 + overlays/default.nix | 4 + overlays/git-daemon-module.nix | 137 +++++++++++ overlays/mautrix-signal-module.nix | 196 +++++++++++++++ overlays/mautrix-whatsapp-module.nix | 192 +++++++++++++++ overlays/mjolnir-module/default.nix | 242 +++++++++++++++++++ overlays/mjolnir-module/mjolnir.md | 110 +++++++++ overlays/mjolnir-module/pantalaimon-options.nix | 70 ++++++ overlays/mjolnir-package/default.nix | 80 +++++++ overlays/mjolnir-package/package.json | 69 ++++++ overlays/mjolnir-package/pin.json | 5 + overlays/mjolnir-package/update.sh | 36 +++ overlays/sliding-sync-module.nix | 87 +++++++ overlays/sliding-sync.nix | 22 ++ overlays/soju-module.nix | 132 +++++++++++ scripts/README.md | 5 + scripts/manage-user | 79 ++++++ systems/.sops.yaml | 10 + systems/cobalt/default.nix | 303 ++++++++++++++++++++++++ systems/cobalt/hardware-configuration.nix | 65 +++++ systems/cobalt/secrets/secrets.yaml | 39 +++ 77 files changed, 4835 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 TODO.md create mode 100644 default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 lib/README.md create mode 100644 lib/attrs.nix create mode 100644 lib/default.nix create mode 100644 lib/misc.nix create mode 100644 lib/modules.nix create mode 100644 lib/system.nix create mode 100644 modules/README.md create mode 100644 modules/cachix/caches/nix-community.nix create mode 100644 modules/cachix/default.nix create mode 100644 modules/flakes.nix create mode 100644 modules/nix.nix create mode 100644 modules/security.nix create mode 100644 modules/services/acme.nix create mode 100644 modules/services/akkoma/blocklist.toml create mode 100644 modules/services/akkoma/default.nix create mode 100644 modules/services/akkoma/favicon-withbg.png create mode 100644 modules/services/akkoma/favicon.png create mode 100644 modules/services/akkoma/logo.png create mode 100644 modules/services/akkoma/logo.svg create mode 100644 modules/services/akkoma/robots.txt create mode 100644 modules/services/akkoma/terms-of-service.html create mode 100644 modules/services/cgit.nix create mode 100644 modules/services/coredns/_corefile.nix create mode 100644 modules/services/coredns/default.nix create mode 100644 modules/services/coturn.nix create mode 100644 modules/services/dendrite.nix create mode 100644 modules/services/dovecot.nix create mode 100644 modules/services/element-web.nix create mode 100644 modules/services/fail2ban.nix create mode 100644 modules/services/git-daemon.nix create mode 100644 modules/services/gitolite/default.nix create mode 100644 modules/services/gitolite/fix-refs create mode 100644 modules/services/gitolite/rename create mode 100644 modules/services/jitsi.nix create mode 100644 modules/services/ldap.nix create mode 100644 modules/services/matrix-bridge.nix create mode 100644 modules/services/matrix-moderation.nix create mode 100644 modules/services/metrics.nix create mode 100644 modules/services/misskey/config/default.yml create mode 100644 modules/services/misskey/default.nix create mode 100644 modules/services/nginx.nix create mode 100644 modules/services/nixos-mailserver.nix create mode 100644 modules/services/postgresql.nix create mode 100644 modules/services/pubnix.nix create mode 100644 modules/services/sefidel-web.nix create mode 100644 modules/services/soju.nix create mode 100644 modules/services/userweb.nix create mode 100644 modules/services/vikunja.nix create mode 100644 modules/sops.nix create mode 100644 overlays/README.md create mode 100644 overlays/default.nix create mode 100644 overlays/git-daemon-module.nix create mode 100644 overlays/mautrix-signal-module.nix create mode 100644 overlays/mautrix-whatsapp-module.nix create mode 100644 overlays/mjolnir-module/default.nix create mode 100644 overlays/mjolnir-module/mjolnir.md create mode 100644 overlays/mjolnir-module/pantalaimon-options.nix create mode 100644 overlays/mjolnir-package/default.nix create mode 100644 overlays/mjolnir-package/package.json create mode 100644 overlays/mjolnir-package/pin.json create mode 100755 overlays/mjolnir-package/update.sh create mode 100644 overlays/sliding-sync-module.nix create mode 100644 overlays/sliding-sync.nix create mode 100644 overlays/soju-module.nix create mode 100644 scripts/README.md create mode 100755 scripts/manage-user create mode 100644 systems/.sops.yaml create mode 100644 systems/cobalt/default.nix create mode 100644 systems/cobalt/hardware-configuration.nix create mode 100644 systems/cobalt/secrets/secrets.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f3790b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +result +*.qcow2 +**/**/.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6a528e8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +ISC License +=================================== + +Copyright (c) 2023, sefidel + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae1411b --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +exotic->infra +============= + +Infrastructure configurations for exotic.sh + +This is still WIP: [TODO](TODO.md) + +Systems +------- + +`cobalt` - Main server diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..b08a964 --- /dev/null +++ b/TODO.md @@ -0,0 +1,16 @@ +# infra->todo + +* BEFORE-RELEASE: send mjolnir patch upstream + +* move dendrite and matrix-\* modules to modules/matrix/[..] +* Ensure functionality on polylith setup + 1. Mirror userdb (mirror ldap, or migrate to pgsql+libnss-pgsql) +* Add a 'ctl' command to let users control their info +* move scripts to ./script +* use same option name for 'hostName', 'domain', etc (perhaps use `fqdn`?) +* don't blindly enable TLS, make `tls.enable, tls.acmeHost` for all modules +* set `hostName` default to `config.networking.hostName` +* DNS server should also be hosted here +* ^ subdomains should be replaced to `*` + +* prometheus dashboard diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..afcdc08 --- /dev/null +++ b/default.nix @@ -0,0 +1,10 @@ +{ inputs, lib, ... }: +with lib; +with lib.my; +{ + imports = [ + inputs.impermanence.nixosModules.impermanence + ] ++ mapModulesRec' (toString ./modules) import; + + networking.useDHCP = mkDefault false; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6b86b8b --- /dev/null +++ b/flake.lock @@ -0,0 +1,253 @@ +{ + "nodes": { + "blobs": { + "flake": false, + "locked": { + "lastModified": 1604995301, + "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=", + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "type": "gitlab" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1668681692, + "narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "009399224d5e398d03b22badca40a37ac85412a1", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "impermanence": { + "locked": { + "lastModified": 1675359654, + "narHash": "sha256-FPxzuvJkcO49g4zkWLSeuZkln54bLoTtrggZDJBH90I=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "6138eb8e737bffabd4c8fc78ae015d4fd6a7e2fd", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, + "nixos-mailserver": { + "inputs": { + "blobs": "blobs", + "flake-compat": "flake-compat", + "nixpkgs": [ + "unstable" + ], + "nixpkgs-22_11": "nixpkgs-22_11", + "utils": "utils" + }, + "locked": { + "lastModified": 1671738303, + "narHash": "sha256-PRgqtaWf2kMSYqVmcnmhTh+UsC0RmvXRTr+EOw5VZUA=", + "owner": "simple-nixos-mailserver", + "repo": "nixos-mailserver", + "rev": "6d0d9fb966cc565a3df74d3b686f924c7615118c", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "repo": "nixos-mailserver", + "type": "gitlab" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1673606088, + "narHash": "sha256-wdYD41UwNwPhTdMaG0AIe7fE1bAdyHe6bB4HLUqUvck=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "37b97ae3dd714de9a17923d004a2c5b5543dfa6d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2111": { + "locked": { + "lastModified": 1659446231, + "narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-21.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-22_11": { + "locked": { + "lastModified": 1669558522, + "narHash": "sha256-yqxn+wOiPqe6cxzOo4leeJOp1bXE/fjPEi/3F/bBHv8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ce5fe99df1f15a09a91a86be9738d68fadfbad82", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-22.11", + "type": "indirect" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1679748960, + "narHash": "sha256-BP8XcYHyj1NxQi04RpyNW8e7KiXSoI+Fy1tXIK2GfdA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "da26ae9f6ce2c9ab380c0f394488892616fc5a6a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "impermanence": "impermanence", + "nixos-mailserver": "nixos-mailserver", + "nixpkgs-2111": "nixpkgs-2111", + "sefidel-web": "sefidel-web", + "sops-nix": "sops-nix", + "unstable": "unstable", + "unstable-small": "unstable-small" + } + }, + "sefidel-web": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1680013210, + "narHash": "sha256-892xzeELa85wJxOro40fVK4zmqbj7ZWJW54cECW7IYg=", + "ref": "refs/heads/main", + "rev": "ac0028999700416577f9a353530a89bd896cbc0d", + "revCount": 3, + "type": "git", + "url": "https://git.exotic.sh/pub/sefidel/sefidel-web" + }, + "original": { + "type": "git", + "url": "https://git.exotic.sh/pub/sefidel/sefidel-web" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "unstable" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1679993313, + "narHash": "sha256-pfZ/BxJDTifnQBMXg60OhwpJvg96LHvEXGtpHeGcWLM=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "5b26523e28989a7f56953b695184070c06335814", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, + "unstable": { + "locked": { + "lastModified": 1679944645, + "narHash": "sha256-e5Qyoe11UZjVfgRfwNoSU57ZeKuEmjYb77B9IVW7L/M=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "4bb072f0a8b267613c127684e099a70e1f6ff106", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "unstable-small": { + "locked": { + "lastModified": 1680024716, + "narHash": "sha256-f9824KWmxVBI4WLI7o6tDFfj+dW+qj6uQKo0ZRsbaZQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "49079a134fd3d3ac25d5ae1f5516f37770f19138", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "utils": { + "locked": { + "lastModified": 1605370193, + "narHash": "sha256-YyMTf3URDL/otKdKgtoMChu4vfVL3vCMkRqpGifhUn0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5021eac20303a61fafe17224c087f5519baed54d", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..40bd39d --- /dev/null +++ b/flake.nix @@ -0,0 +1,44 @@ +{ + description = "exotic.sh system flake"; + + inputs = { + unstable.url = "github:nixos/nixpkgs/nixos-unstable"; + unstable-small.url = "github:nixos/nixpkgs/nixos-unstable-small"; + nixpkgs-2111.url = "github:nixos/nixpkgs/nixos-21.11"; + + impermanence.url = "github:nix-community/impermanence"; + + sops-nix.url = "github:Mic92/sops-nix"; + sops-nix.inputs.nixpkgs.follows = "unstable"; + + nixos-mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver"; + nixos-mailserver.inputs.nixpkgs.follows = "unstable"; + + sefidel-web.url = "git+https://git.exotic.sh/pub/sefidel/sefidel-web"; + }; + + outputs = { self, unstable, ... } @ inputs: + let + inherit (lib.my) mapModulesRec mapSystems; + + system = "x86_64-linux"; + + pkgs = import unstable { inherit system; }; + + lib = unstable.lib.extend + (self: super: { my = import ./lib { inherit pkgs inputs; lib = self; }; }); + in + { + lib = lib.my; + + nixosModules = mapModulesRec ./modules import; + + colmena = { + meta = { + nixpkgs = import unstable { inherit system; overlays = [ (import ./overlays) ]; }; + specialArgs = { inherit lib inputs system; }; + }; + } + // mapSystems ./systems { }; + }; +} diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000..21ca023 --- /dev/null +++ b/lib/README.md @@ -0,0 +1,13 @@ +infra->lib +========== + +Attribution +----------- + +Most of the 'moving parts' of this is largely copied/modified from: + +- [hlissner/dotfiles][hlissner]: 2023-Feb, MIT (C) Henrik Lissner +- [NobbZ/nixos-config][nobbz]: 2022, MIT (C) Nobert Melzer + +[hlissner]: https://github.com/hlissner/dotfiles +[nobbz]: https://github.com/NobbZ/nixos-config diff --git a/lib/attrs.nix b/lib/attrs.nix new file mode 100644 index 0000000..0f8ebd1 --- /dev/null +++ b/lib/attrs.nix @@ -0,0 +1,26 @@ +{ lib, ... }: + +with builtins; +with lib; +rec { + # attrsToList + attrsToList = attrs: + mapAttrsToList (name: value: { inherit name value; }) attrs; + + # mapFilterAttrs :: + # (name -> value -> bool) + # (name -> value -> { name = any; value = any; }) + # attrs + mapFilterAttrs = pred: f: attrs: filterAttrs pred (mapAttrs' f attrs); + + # Generate an attribute set by mapping a function over a list of values. + genAttrs' = values: f: listToAttrs (map f values); + + # anyAttrs :: (name -> value -> bool) attrs + anyAttrs = pred: attrs: + any (attr: pred attr.name attr.value) (attrsToList attrs); + + # countAttrs :: (name -> value -> bool) attrs + countAttrs = pred: attrs: + count (attr: pred attr.name attr.value) (attrsToList attrs); +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..a275c09 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,18 @@ +{ inputs, lib, pkgs, ... }: + +let + inherit (lib) makeExtensible attrValues foldr; + inherit (modules) mapModules; + + modules = import ./modules.nix { + inherit lib; + self.attrs = import ./attrs.nix { inherit lib; self = { }; }; + }; + + mylib = makeExtensible (self: + with self; mapModules ./. + (file: import file { inherit self lib pkgs inputs; })); +in +mylib.extend + (self: super: + foldr (a: b: a // b) { } (attrValues super)) diff --git a/lib/misc.nix b/lib/misc.nix new file mode 100644 index 0000000..484d0d5 --- /dev/null +++ b/lib/misc.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +rec { + # ifd3f/infra + wrapFile = name: path: + (pkgs.runCommand name { inherit path; } '' + cp -r "$path" "$out" + ''); +} diff --git a/lib/modules.nix b/lib/modules.nix new file mode 100644 index 0000000..ef7c289 --- /dev/null +++ b/lib/modules.nix @@ -0,0 +1,54 @@ +{ self, lib, ... }: + +let + inherit (builtins) attrValues readDir pathExists concatLists; + inherit (lib) id mapAttrsToList filterAttrs hasPrefix hasSuffix nameValuePair removeSuffix; + inherit (self.attrs) mapFilterAttrs; +in +rec { + mapModules = dir: fn: + mapFilterAttrs + (n: v: + v != null && + !(hasPrefix "_" n)) + (n: v: + let path = "${toString dir}/${n}"; in + if v == "directory" && pathExists "${path}/default.nix" + then nameValuePair n (fn path) + else if v == "regular" && + n != "default.nix" && + hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) + (readDir dir); + + mapModules' = dir: fn: + attrValues (mapModules dir fn); + + mapModulesRec = dir: fn: + mapFilterAttrs + (n: v: + v != null && + !(hasPrefix "_" n)) + (n: v: + let path = "${toString dir}/${n}"; in + if v == "directory" + then nameValuePair n (mapModulesRec path fn) + else if v == "regular" && n != "default.nix" && hasSuffix ".nix" n + then nameValuePair (removeSuffix ".nix" n) (fn path) + else nameValuePair "" null) + (readDir dir); + + mapModulesRec' = dir: fn: + let + dirs = + mapAttrsToList + (k: _: "${dir}/${k}") + (filterAttrs + (n: v: v == "directory" && !(hasPrefix "_" n)) + (readDir dir)); + files = attrValues (mapModules dir id); + paths = files ++ concatLists (map (d: mapModulesRec' d id) dirs); + in + map fn paths; +} diff --git a/lib/system.nix b/lib/system.nix new file mode 100644 index 0000000..8fc4dce --- /dev/null +++ b/lib/system.nix @@ -0,0 +1,22 @@ +{ self, inputs, lib, pkgs, ... }: + +with lib; +with lib.my; +{ + mkSystem = path: attrs @ { ... }: { + imports = [ + { + networking.hostName = mkDefault + (removeSuffix ".nix" (baseNameOf path)); + system.configurationRevision = self.rev or "dirty"; + } + ../. # /default.nix + (import path) + ]; + }; + + mapSystems = dir: attrs @ { system ? system, ... }: + mapModules dir + (hostPath: mkSystem hostPath attrs); +} + diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000..25031dc --- /dev/null +++ b/modules/README.md @@ -0,0 +1,9 @@ +infra->modules +============== + +This is all the modules used to configure our systems based on its purposes. + +As of now, it doesn't have any clear "naming convention", but generally it's +`` for a module configuring one thing, `` otherwise. + +e.g) `dendrite` -> `dendrite`, `prometheus`, `grafana` -> `metrics` diff --git a/modules/cachix/caches/nix-community.nix b/modules/cachix/caches/nix-community.nix new file mode 100644 index 0000000..d323939 --- /dev/null +++ b/modules/cachix/caches/nix-community.nix @@ -0,0 +1,12 @@ +{ config, lib, ... }: + +{ + nix.settings = { + substituters = [ + "https://nix-community.cachix.org" + ]; + trusted-public-keys = [ + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + ]; + }; +} diff --git a/modules/cachix/default.nix b/modules/cachix/default.nix new file mode 100644 index 0000000..9dd55b5 --- /dev/null +++ b/modules/cachix/default.nix @@ -0,0 +1,13 @@ +{ config, pkgs, lib, ... }: +let + folder = ./caches; + toImport = name: value: folder + ("/" + name); + filterCaches = key: value: value == "regular" && lib.hasSuffix ".nix" key; + imports = lib.mapAttrsToList toImport (lib.filterAttrs filterCaches (builtins.readDir folder)); +in +{ + inherit imports; + nix.settings.substituters = [ "https://cache.nixos.org/" ]; + + environment.systemPackages = [ pkgs.cachix ]; +} diff --git a/modules/flakes.nix b/modules/flakes.nix new file mode 100644 index 0000000..df86369 --- /dev/null +++ b/modules/flakes.nix @@ -0,0 +1,36 @@ +{ config, pkgs, unstable, unstable-small, nixpkgs-2111, lib, ... }: + +with lib; +let + base = "/etc/nixpkgs/channels"; + nixpkgsPath = "${base}/nixpkgs"; + nixpkgsSmallPath = "${base}/nixpkgsSmall"; + nixpkgs2111Path = "${base}/nixpkgs2111"; +in +{ + options.nix.flakes.enable = mkEnableOption "nix flakes"; + + config = lib.mkIf config.nix.flakes.enable { + nix = { + package = pkgs.nixUnstable; + experimentalFeatures = "nix-command flakes"; + + registry.nixpkgs.flake = unstable; + registry.nixpkgsSmall.flake = unstable-small; + registry.nixpkgs2111.flake = nixpkgs-2111; + + nixPath = [ + "nixpkgs=${nixpkgsPath}" + "nixpkgsSmall=${nixpkgsSmallPath}" + "nixpkgs2111=${nixpkgs2111Path}" + "/nix/var/nix/profiles/per-user/root/channels" + ]; + }; + + systemd.tmpfiles.rules = [ + "L+ ${nixpkgsPath} - - - - ${unstable}" + "L+ ${nixpkgsSmallPath} - - - - ${unstable-small}" + "L+ ${nixpkgs2111Path} - - - - ${nixpkgs-2111}" + ]; + }; +} diff --git a/modules/nix.nix b/modules/nix.nix new file mode 100644 index 0000000..1f61d45 --- /dev/null +++ b/modules/nix.nix @@ -0,0 +1,34 @@ +{ config, lib, ... }: + +let + allowed = config.nix.allowedUnfree; +in +{ + options.nix = { + experimentalFeatures = lib.mkOption { + type = lib.types.separatedString " "; + default = ""; + description = '' + Enables experimental features + ''; + }; + + allowedUnfree = lib.mkOption { + type = lib.types.listOf lib.types.string; + default = [ ]; + description = '' + Allows for unfree packages by their name. + ''; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf (config.nix.experimentalFeatures != "") { nix.extraOptions = "experimental-features = ${config.nix.experimentalFeatures}"; }) + (lib.mkIf (allowed != [ ]) { nixpkgs.config.allowUnfreePredicate = (pkg: __elem (lib.getName pkg) allowed); }) + { nix.settings.auto-optimise-store = lib.mkDefault true; } + { + nix.gc.automatic = lib.mkDefault true; + nix.gc.options = lib.mkDefault "--delete-older-than 10d"; + } + ]; +} diff --git a/modules/security.nix b/modules/security.nix new file mode 100644 index 0000000..d845393 --- /dev/null +++ b/modules/security.nix @@ -0,0 +1,59 @@ +{ config, lib, ... }: + +{ + # Security-related system tweaks + + # Prevent replacing the running kernel without reboot. + security.protectKernelImage = true; + + # mount /tmp in ram. This makes temp file management faster + # on ssd systems, and volatile! Because it's wiped on reboot. + boot.tmpOnTmpfs = false; + boot.tmpOnTmpfsSize = "80%"; + + # Purge /tmp on boot. (fallback option) + boot.cleanTmpDir = lib.mkDefault (!config.boot.tmpOnTmpfs); + + boot.kernel.sysctl = { + # The Magic SysRq key is a key combo that allows users connected to the + # system console of a Linux kernel to perform some low-level commands. + # Disable it, since we don't need it, and is a potential security concern. + "kernel.sysrq" = 0; + + ## TCP hardening + # Prevent bogus ICMP errors from filling up logs. + "net.ipv4.icmp_ignore_bogus_error_responses" = 1; + # Reverse path filtering causes the kernel to do source validation of + # packets received from all interfaces. This can mitigate IP spoofing. + "net.ipv4.conf.default.rp_filter" = 1; + "net.ipv4.conf.all.rp_filter" = 1; + # Do not accept IP source route packets (we're not a router) + "net.ipv4.conf.all.accept_source_route" = 0; + "net.ipv6.conf.all.accept_source_route" = 0; + # Don't send ICMP redirects (again, we're on a router) + "net.ipv4.conf.all.send_redirects" = 0; + "net.ipv4.conf.default.send_redirects" = 0; + # Refuse ICMP redirects (MITM mitigations) + "net.ipv4.conf.all.accept_redirects" = 0; + "net.ipv4.conf.default.accept_redirects" = 0; + "net.ipv4.conf.all.secure_redirects" = 0; + "net.ipv4.conf.default.secure_redirects" = 0; + "net.ipv6.conf.all.accept_redirects" = 0; + "net.ipv6.conf.default.accept_redirects" = 0; + # Protects against SYN flood attacks + "net.ipv4.tcp_syncookies" = 1; + # Incomplete protection again TIME-WAIT assassination + "net.ipv4.tcp_rfc1337" = 1; + + ## TCP optimization + # TCP Fast Open is a TCP extension that reduces network latency by packing + # data in the sender’s initial TCP SYN. Setting 3 = enable TCP Fast Open for + # both incoming and outgoing connections: + "net.ipv4.tcp_fastopen" = 3; + # Bufferbloat mitigations + slight improvement in throughput & latency + "net.ipv4.tcp_congestion_control" = "bbr"; + "net.core.default_qdisc" = "cake"; + }; + + boot.kernelModules = [ "tcp_bbr" ]; +} diff --git a/modules/services/acme.nix b/modules/services/acme.nix new file mode 100644 index 0000000..6f6e33e --- /dev/null +++ b/modules/services/acme.nix @@ -0,0 +1,52 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.modules.services.acme; +in +{ + options.modules.services.acme = { + enable = mkEnableOption "ACME certificate manager"; + email = mkOption { + type = types.str; + description = mdDoc '' + The postmaster email address to use. + ''; + }; + certs = mkOption { + type = types.attrsOf + (types.submodule { + options = { + domain = mkOption { + type = types.nullOr types.str; + default = null; + }; + subDomains = mkOption { type = types.listOf types.str; }; + }; + }); + }; + secrets.acme-credentials = mkOption { type = types.str; description = "path to the acme environment file"; }; + }; + + config = mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults.email = cfg.email; + certs = mapAttrs + (name: { domain, subDomains }: { + extraDomainNames = lists.forEach subDomains (elem: elem + ".${name}"); + } // { + dnsProvider = "hetzner"; + dnsPropagationCheck = true; + credentialsFile = cfg.secrets.acme-credentials; + } // optionalAttrs (domain != null) { + domain = domain; + }) + cfg.certs; + }; + + environment.persistence."/persist".directories = [ + "/var/lib/acme" + ]; + }; +} diff --git a/modules/services/akkoma/blocklist.toml b/modules/services/akkoma/blocklist.toml new file mode 100644 index 0000000..e5eac7a --- /dev/null +++ b/modules/services/akkoma/blocklist.toml @@ -0,0 +1,163 @@ +[followers_only] + +[media_nsfw] + +[reject] +"*.tk" = "Free TLD" +"*.ml" = "Free TLD" +"*.ga" = "Free TLD" +"*.cf" = "Free TLD" +"*.gq" = "Free TLD" +# Reject list from chaos.social at 2023-02-06 +"activitypub-proxy.cf" = "Only exists to evade instance blocks, details" +"activitypub-troll.cf" = "Spam" +"aethy.com" = "Lolicon" +"bae.st" = "Discrimination, racism, “free speech zone”" +"baraag.net" = "Lolicon" +"banepo.st" = "Homophobia" +"beefyboys.club" = "Discrimination, racism, “free speech zone”" +"beefyboys.win" = "Discrimination, racism, “free speech zone”" +"beta.birdsite.live" = "Twitter crossposter" +"birb.elfenban.de" = "Twitter crossposter" +"bird.evilcyberhacker.net" = "Twitter crossposter" +"bird.froth.zone" = "Twitter crossposter" +"bird.geiger.ee" = "Twitter crossposter" +"bird.im-in.space" = "Twitter crossposter" +"bird.istheguy.com" = "Twitter crossposter" +"bird.karatek.net" = "Twitter crossposter" +"bird.makeup" = "Twitter crossposter" +"bird.nzbr.de" = "Twitter crossposter" +"bird.r669.live" = "Twitter crossposter" +"bird.seafoam.space" = "Twitter crossposter" +"birdbots.leptonics.com" = "Twitter crossposter" +"birdsite.b93.dece.space" = "Twitter crossposter" +"birdsite.blazelights.dev" = "Twitter crossposter" +"birdsite.frog.fashion" = "Twitter crossposter" +"birdsite.gabeappleton.me" = "Twitter crossposter" +"birdsite.james.moody.name" = "Twitter crossposter" +"birdsite.koyu.space" = "Twitter crossposter" +"birdsite.lakedrops.com" = "Twitter crossposter" +"birdsite.link" = "Twitter crossposter" +"birdsite.monster" = "Twitter crossposter" +"birdsite.oliviaappleton.com" = "Twitter crossposter" +"birdsite.platypush.tech" = "Twitter crossposter" +"birdsite.slashdev.space" = "Twitter crossposter" +"birdsite.tcjc.uk" = "Twitter crossposter" +"birdsite.thorlaksson.com" = "Twitter crossposter" +"birdsite.toot.si" = "Twitter crossposter" +"birdsite.wilde.cloud" = "Twitter crossposter" +"birdsitelive.ffvo.dev" = "Twitter crossposter" +"birdsitelive.kevinyank.com" = "Twitter crossposter" +"birdsitelive.peanutlasko.com" = "Twitter crossposter" +"birdsitelive.treffler.cloud" = "Twitter crossposter" +"bridge.birb.space" = "Twitter crossposter" +"brighteon.social" = "“free speech zone”" +"cawfee.club" = "Discrimination, racism, “free speech zone”" +"childpawn.shop" = "Pedophilia" +"chudbuds.lol" = "Discrimination, racism, “free speech zone”" +"club.darknight-coffee.eu" = "“free speech zone”" +"clubcyberia.co" = "Homophobia" +"clube.social" = "Harassment" +"comfyboy.club" = "Discrimination, racism" +"cum.camp" = "Harassment" +"cum.salon" = "Misogynic, pedophilia" +"daishouri.moe" = "Fascism, openly advertises with swastika" +"detroitriotcity.com" = "Discrimination, racism, “free speech zone”" +"eientei.org" = "Racism, antisemitism" +"eveningzoo.club" = "Discrimination, racism, “free speech zone”" +"f.haeder.net" = "Discrimination" +"freak.university" = "Pedophilia" +"freeatlantis.com" = "Conspiracy theory instance" +"freecumextremist.com" = "Discrimination, racism, “free speech zone”" +"freefedifollowers.ga" = "Follower spam" +"freespeechextremist.com" = "Discrimination, racism, “free speech zone”" +"frennet.link" = "Discrimination, racism, “free speech zone”" +"froth.zone" = "Calls freespeechextremist their local bubble" +"gab.com/.ai, develop.gab.com" = "Discrimination, racism, “free speech zone”" +"gameliberty.club" = "“free speech zone”" +"gegenstimme.tv" = "“free speech zone”" +"genderheretics.xyz" = "Tagline “Now With 41% More Misgendering!”" +"gitmo.life" = "“free speech zone”" +"gleasonator.com" = "Transphobia, TERFs" +"glindr.org" = "Discrimination" +"glowers.club" = "Discrimination, racism, “free speech zone”" +"honkwerx.tech" = "Racism" +"iamterminally.online" = "Discrimination, racism, “free speech zone”" +"iddqd.social" = "Discrimination, racism, “free speech zone”" +"itmslaves.com" = "“free speech zone”, noagenda affiliated" +"jaeger.website" = "Discrimination, racism, “free speech zone”" +"kenfm.quadplay.tv" = "Conspiracy videos" +"kiwifarms.cc" = "Discrimination" +"lgbtfree.zone" = "Racism, transphobia, all that" +"liberdon.com" = "Conspiracy theories, transphobia, racism" +"libre.tube" = "Promotion of violence and murder, multiple other violations of our rules" +"lolicon.rocks" = "Lolicon" +"lolison.top" = "Lolicon, paedophilia" +"mastinator.com" = "Block evasion, unwanted profile mirroring, and more" +"mastodon.network" = "Instance went down, now porn spam" +"mastodon.popps.org" = "Homophobia" +"mastodong.lol" = "Admin maintains and runs activitypub-proxy.cf" +"meta-tube.de" = "Conspiracy, CoVid19 denier videos https://fediblock.org/blocklist/#meta-tube.de" +"midnightride.rs" = "Discrimination" +"misskey-forkbomb.cf" = "Spam" +"morale.ch" = "Antisemitism and more" +"mstdn.foxfam.club" = "Right wing twitter mirror" +"natehiggers.online" = "Racism" +"newjack.city" = "Exclusive to unwanted follow bots" +"nicecrew.digital" = "Discrimination, racism, “free speech zone”" +"noagendasocial.com" = "“free speech zone”, harassment" +"noagendasocial.nl" = "“free speech zone”, harassment" +"noagendatube.com" = "“free speech zone”, harassment" +"ns.auction" = "Racism etc" +"ohai.su" = "Offline" +"pawoo.net" = "Untagged nfsw content, unwanted follow bots, lolicon" +"paypig.org" = "Racism" +"pieville.net" = "Racism, antisemitism" +"pl.serialmay.link" = "Racism, transphobia" +"pl.tkammer.de" = "Transphobia" +"play.xmr.101010.pl" = "Cryptomining" +"pleroma.kitsunemimi.club" = "Discrimination" +"pleroma.narrativerry.xyz" = "Discrimination, racism, “free speech zone”" +"pleroma.nobodyhasthe.biz" = "Doxxing and discrimination" +"pleroma.rareome.ga" = "Doesn’t respect blocks or status privacy, lolicons" +"poa.st" = "Discrimination" +"podcastindex.social" = "noagenda affiliated" +"poster.place" = "Discrimination, racism, “free speech zone”, harassment in response to blocks" +"qoto.org" = "“free speech zone”, harassment" +"rapemeat.solutions" = "Lolicon and also, like, the domain name" +"rdrama.cc" = "Discrimination, “free speech zone”, racism" +"repl.co" = "Spam" +"rojogato.com" = "Harassment, “free speech zone”" +"ryona.agency" = "Alt-right trolls, harassment" +"seal.cafe" = "Discrimination, racism, “free speech zone”" +"shitpost.cloud" = "“Free speech zone”, antisemitism" +"shitposter.club" = "“Free speech zone”" +"shortstackran.ch" = "Racism, homophobia, “free speech zone”" +"shota.house" = "Lolicon" +"skippers-bin.com" = "Same admin as neckbeard.xyz, same behaviour" +"sleepy.cafe" = "Racism, harassment" +"sneak.berlin" = "privacy violation" +"sneed.social" = "Discrimination, racism, “free speech zone”, nationalism, hate speech, completely unmoderated" +"soc.ua-fediland.de" = "Spam" +"social.ancreport.com" = "Discrimination, racism, “free speech zone”" +"social.lovingexpressions.net" = "Transphobia" +"social.teci.world" = "Discrimination, racism, “free speech zone”" +"social.urspringer.de" = "Conspiracy, CoVid19 denier" +"socnet.supes.com" = "Right wing “free speech zone”" +"solagg.com" = "Scammers" +"spinster.xyz" = "Discrimination, TERFs" +"tastingtraffic.net" = "Homophobia" +"truthsocial.co.in" = "Alt-right trolls" +"tube.kenfm.de" = "Right-wing conspiracy videos" +"tube.querdenken-711.de" = "Right-wing onspiracy videos" +"tweet.pasture.moe" = "Twitter crossposter" +"tweetbridge.kogasa.de" = "Twitter crossposter" +"tweets.icu" = "Twitter crossposter" +"twitter.activitypub.actor" = "Twitter crossposter" +"twitter.doesnotexist.club" = "Twitter crossposter" +"twitterbridge.jannis.rocks" = "Twitter crossposter" +"twtr.plus" = "Twitter crossposter" +"varishangout.net" = "Transphobia and racism go unmoderated, aggressive trolling, lolicon permitted in rules" +"wiki-tube.de" = "Right-wing conspiracy videos (initial video welcomes Querdenken and KenFM)" +"wolfgirl.bar" = "Discrimination, homophobia, unmoderated trolling" +"yggdrasil.social" = "Instance rules: “No LGBTQ. Period. No homosexuality. No men who think they’re women or women who think they’re men. No made up genders.”" diff --git a/modules/services/akkoma/default.nix b/modules/services/akkoma/default.nix new file mode 100644 index 0000000..a0cd42c --- /dev/null +++ b/modules/services/akkoma/default.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.modules.services.akkoma; + + poorObfuscation = y: x: "${x}@${y}"; + federation-blocklist = lib.importTOML ./blocklist.toml; + + inherit (lib.my) wrapFile; +in +{ + options.modules.services.akkoma = { + enable = mkEnableOption "Akkoma instance"; + domain = mkOption { type = types.str; }; + realHost = mkOption { type = types.str; }; + instanceName = mkOption { type = types.str; default = "Akkoma on ${cfg.domain}"; }; + }; + + config = mkIf cfg.enable { + modules.services.postgresql.enable = true; + + services.akkoma = { + enable = true; + initDb.enable = true; + + extraStatic = { + "static/terms-of-service.html" = wrapFile "terms-of-service.html" ./terms-of-service.html; + "static/logo.svg" = wrapFile "logo.svg" ./logo.svg; + "static/logo.png" = wrapFile "logo.png" ./logo.png; + "static/logo-512.png" = wrapFile "logo-512.png" ./favicon-withbg.png; # Intentional, for PWA favicon. + "static/icon.png" = wrapFile "icon.png" ./favicon.png; + "favicon.png" = wrapFile "favicon.png" ./favicon.png; + }; + config = + let inherit ((pkgs.formats.elixirConf { }).lib) mkRaw mkMap; + in { + ":pleroma"."Pleroma.Web.Endpoint".url.host = cfg.realHost; + ":pleroma"."Pleroma.Web.WebFinger".domain = cfg.domain; + ":pleroma".":media_proxy".enabled = false; + ":pleroma".":instance" = { + name = cfg.instanceName; + + description = "Private akkoma instance"; + email = poorObfuscation cfg.domain "postmaster"; + notify_email = poorObfuscation cfg.domain "postmaster"; + + registrations_open = false; + invites_enabled = true; + + limit = 5000; + }; + ":pleroma".":frontend_configurations" = { + pleroma_fe = mkMap { + logo = "/static/logo.png"; + }; + }; + ":pleroma".":mrf" = { + policies = map mkRaw [ "Pleroma.Web.ActivityPub.MRF.SimplePolicy" ]; + }; + ":pleroma".":mrf_simple" = { + followers_only = mkMap federation-blocklist.followers_only; + media_nsfw = mkMap federation-blocklist.media_nsfw; + reject = mkMap federation-blocklist.reject; + }; + }; + + nginx = { + forceSSL = true; + useACMEHost = cfg.domain; + + locations."~ \\.(js|css|woff|woff2?|png|jpe?g|svg)$" = { + extraConfig = '' + add_header Cache-Control "public, max-age=14400, must-revalidate"; + ''; + + proxyPass = "http://unix:${config.services.akkoma.config.":pleroma"."Pleroma.Web.Endpoint".http.ip}"; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + }; + }; + + services.nginx.virtualHosts.${cfg.domain} = { + forceSSL = true; + useACMEHost = cfg.domain; + + locations."/.well-known/host-meta" = { + extraConfig = '' + return 301 https://${cfg.realHost}$request_uri; + ''; + }; + }; + }; + } diff --git a/modules/services/akkoma/favicon-withbg.png b/modules/services/akkoma/favicon-withbg.png new file mode 100644 index 0000000..7d15954 Binary files /dev/null and b/modules/services/akkoma/favicon-withbg.png differ diff --git a/modules/services/akkoma/favicon.png b/modules/services/akkoma/favicon.png new file mode 100644 index 0000000..d8cbce3 Binary files /dev/null and b/modules/services/akkoma/favicon.png differ diff --git a/modules/services/akkoma/logo.png b/modules/services/akkoma/logo.png new file mode 100644 index 0000000..7744b1a Binary files /dev/null and b/modules/services/akkoma/logo.png differ diff --git a/modules/services/akkoma/logo.svg b/modules/services/akkoma/logo.svg new file mode 100644 index 0000000..68e647e --- /dev/null +++ b/modules/services/akkoma/logo.svg @@ -0,0 +1,71 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/modules/services/akkoma/robots.txt b/modules/services/akkoma/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/modules/services/akkoma/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/modules/services/akkoma/terms-of-service.html b/modules/services/akkoma/terms-of-service.html new file mode 100644 index 0000000..b954760 --- /dev/null +++ b/modules/services/akkoma/terms-of-service.html @@ -0,0 +1,26 @@ + + + + +

Terms of Service

+

This is a personal instance with only one user. Therefore, I'll write rules that I'll abide:

+
    +
  1. +

    No discrimination based on race, gender, sexual orientation, disabilities, or any other characteristic.

    +
  2. +
  3. +

    No harassment or doxxing towards others.

    +
  4. +
  5. +

    No promotion of violence.

    +
  6. +
  7. +

    No content that is illegal in United Kingdom, Japan, Finland, Germany, and South Korea.

    +
  8. +
  9. +

    Use content warnings for explicit or controversial content.

    +
  10. +
+

Since I'm the only user here, I try to moderate myself best as I can. But I might sometimes fail to do so. If that ever happens, please do let me know. I'll make sure it never happens again!

+ + diff --git a/modules/services/cgit.nix b/modules/services/cgit.nix new file mode 100644 index 0000000..418312b --- /dev/null +++ b/modules/services/cgit.nix @@ -0,0 +1,121 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.modules.services.cgit; +in +{ + options.modules.services.cgit = { + enable = mkEnableOption "cgit with uwsgi"; + + domain = mkOption { type = types.str; }; + realHost = mkOption { type = types.str; }; + # TODO: use generators & submodules + settings = { + title = mkOption { type = types.str; default = "${cfg.domain} git"; }; + description = mkOption { type = types.str; default = "cgit, hyperfast web frontend for Git"; }; + }; + }; + config = mkIf cfg.enable { + + modules.services.nginx.enable = true; + + services.uwsgi = { + enable = true; + user = "nginx"; + group = "nginx"; + plugins = [ "cgi" ]; + + instance = { + type = "emperor"; + vassals = { + cgit = { + type = "normal"; + master = true; + socket = "/run/uwsgi/cgit.sock"; + procname-master = "uwsgi cgit"; + plugins = [ "cgi" ]; + cgi = "${pkgs.cgit-pink}/cgit/cgit.cgi"; + }; + }; + }; + }; + + users.extraUsers.nginx.extraGroups = [ "git" ]; + + services.nginx.virtualHosts.${cfg.realHost} = { + forceSSL = true; + useACMEHost = cfg.domain; + root = "${pkgs.cgit-pink}/cgit"; + locations = { + "/" = { + extraConfig = '' + try_files $uri @cgit; + ''; + }; + "@cgit" = { + extraConfig = '' + uwsgi_pass unix:/run/uwsgi/cgit.sock; + include ${pkgs.nginx}/conf/uwsgi_params; + uwsgi_modifier1 9; + ''; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + systemd.services.create-cgit-cache = { + description = "Create cache directory for cgit"; + enable = true; + + script = '' + mkdir -p /run/cgit + chown -R nginx:nginx /run/cgit + ''; + + wantedBy = [ "uwsgi.service" ]; + serviceConfig = { + Type = "oneshot"; + }; + }; + + environment.etc."cgitrc".text = '' + virtual-root=/ + + cache-size=1000 + cache-root=/run/cgit + + root-title=${cfg.domain} git + root-desc=Exotic place. + + snapshots=tar.gz zip + + enable-git-config=1 + remove-suffix=1 + + enable-git-clone=1 + enable-index-links=1 + enable-commit-graph=1 + enable-log-filecount=1 + enable-log-linecount=1 + + branch-sort=age + + readme=:README + readme=:readme + readme=:README.md + readme=:readme.md + readme=:README.org + readme=:readme.org + + source-filter=${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py + about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh + + section-from-path=2 + + project-list=${config.services.gitolite.dataDir}/projects.list + scan-path=${config.services.gitolite.dataDir}/repositories + ''; + }; +} diff --git a/modules/services/coredns/_corefile.nix b/modules/services/coredns/_corefile.nix new file mode 100644 index 0000000..8d0ec66 --- /dev/null +++ b/modules/services/coredns/_corefile.nix @@ -0,0 +1,3 @@ +'' +Add content here +'' diff --git a/modules/services/coredns/default.nix b/modules/services/coredns/default.nix new file mode 100644 index 0000000..52d8570 --- /dev/null +++ b/modules/services/coredns/default.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.modules.services.coredns; +in +{ + options.modules.services.coredns = { + enable = mkEnableOption "coredns"; + }; + + config = mkIf cfg.enable { + services.coredns = { + enable = true; + config = import ./_corefile.nix; + }; + }; +} diff --git a/modules/services/coturn.nix b/modules/services/coturn.nix new file mode 100644 index 0000000..967ba60 --- /dev/null +++ b/modules/services/coturn.nix @@ -0,0 +1,64 @@ +{ config, lib, ... }: + +with lib; +let + turnRange = with config.services.coturn; [{ + from = min-port; + to = max-port; + }]; + + cfg = config.modules.services.coturn; +in +{ + options.modules.services.coturn = { + enable = mkEnableOption "coturn"; + domain = mkOption { type = types.str; default = config.networking.hostName; }; + shared_secret = mkOption { type = types.str; }; + tls.acmeHost = mkOption { type = types.str; default = cfg.domain; }; + }; + + config = mkIf cfg.enable { + services.coturn = { + enable = true; + use-auth-secret = true; + static-auth-secret = cfg.shared_secret; + realm = cfg.domain; + cert = "${config.security.acme.certs.${cfg.tls.acmeHost}.directory}/fullchain.pem"; + pkey = "${config.security.acme.certs.${cfg.tls.acmeHost}.directory}/key.pem"; + + no-tcp-relay = true; + no-cli = true; + + extraConfig = '' + user-quota=12 + total-quota=1200 + + no-loopback-peers + no-multicast-peers + denied-peer-ip=0.0.0.0-0.255.255.255 + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=100.64.0.0-100.127.255.255 + denied-peer-ip=127.0.0.0-127.255.255.255 + denied-peer-ip=169.254.0.0-169.254.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + denied-peer-ip=192.0.0.0-192.0.0.255 + denied-peer-ip=192.0.2.0-192.0.2.255 + denied-peer-ip=192.88.99.0-192.88.99.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=198.18.0.0-198.19.255.255 + denied-peer-ip=198.51.100.0-198.51.100.255 + denied-peer-ip=203.0.113.0-203.0.113.255 + denied-peer-ip=240.0.0.0-255.255.255.255 + ''; + }; + + systemd.services.coturn = { + serviceConfig.SupplementaryGroups = [ "acme" ]; + }; + + networking.firewall.allowedUDPPortRanges = turnRange; + networking.firewall.allowedTCPPortRanges = turnRange; + networking.firewall.allowedTCPPorts = [ 3478 3479 5349 5350 ]; + networking.firewall.allowedUDPPorts = [ 3478 3479 5349 5350 ]; + }; +} diff --git a/modules/services/dendrite.nix b/modules/services/dendrite.nix new file mode 100644 index 0000000..70f9db8 --- /dev/null +++ b/modules/services/dendrite.nix @@ -0,0 +1,230 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.modules.services.dendrite; + + database = { + connection_string = "postgres:///dendrite?host=/run/postgresql"; + max_open_conns = 100; + max_idle_conns = 5; + conn_max_lifetime = -1; + }; +in +{ + imports = [ + ../../overlays/sliding-sync-module.nix + ]; + + options.modules.services.dendrite = { + enable = mkEnableOption "dendrite instance"; + domain = mkOption { type = types.str; }; + realHost = mkOption { type = types.str; default = "matrix.${cfg.domain}"; }; + slidingSyncHost = mkOption { type = types.str; default = "slidingsync.${cfg.domain}"; }; + turn = { + enable = mkEnableOption "VOIP suing TURN"; + domain = mkOption { type = types.str; default = "turn.${cfg.domain}"; }; + shared_secret = mkOption { type = types.str; }; + }; + secrets = { + matrix-server-key = mkOption { type = types.str; description = "path to the server key"; }; + dendrite-envs = mkOption { type = types.nullOr types.str; description = "path for the environment file to source"; }; + sliding-sync-secret = mkOption { type = types.nullOr types.str; description = "path to the sliding sync secret"; }; + }; + }; + + config = mkIf cfg.enable { + # Adapted from Mic92/dotfiles, (C) 2021 Jörg Thalheim (MIT) + services.dendrite = { + enable = true; + settings = { + global = { + server_name = cfg.domain; + # `private_key` has the type `path` + # prefix a `/` to make `path` happy + private_key = "/$CREDENTIALS_DIRECTORY/matrix-server-key"; + jetstream.storage_path = "/var/lib/dendrite/jetstream"; + trusted_third_party_id_servers = [ + "matrix.org" + "vector.im" + ]; + metrics.enabled = true; + }; + logging = [ + { + type = "std"; + level = "info"; # "warn" on public release + } + ]; + app_service_api = { + inherit database; + config_files = [ ]; + }; + client_api = { + registration_disabled = true; + rate_limiting.enabled = false; + rate_limiting.exempt_user_ids = [ + "@abuse:${cfg.domain}" + ]; + # registration_shared_secret = ""; # Initially set this option to configure the admin user. + } // optionalAttrs cfg.turn.enable { + turn = { + turn_user_lifetime = "24h"; + turn_uris = [ + "turns:${cfg.turn.domain}?transport=udp" + "turns:${cfg.turn.domain}?transport=tcp" + "turn:${cfg.turn.domain}?transport=udp" + "turn:${cfg.turn.domain}?transport=tcp" + ]; + turn_shared_secret = cfg.turn.shared_secret; + }; + }; + media_api = { + inherit database; + dynamic_thumbnails = true; + }; + room_server = { + inherit database; + }; + push_server = { + inherit database; + }; + mscs = { + inherit database; + mscs = [ "msc2836" "msc2946" ]; + }; + sync_api = { + inherit database; + real_ip_header = "X-Real-IP"; + # The NixOS option is 'enable', which doesn't exist in Dendrite. + search.enabled = true; + }; + key_server = { + inherit database; + }; + federation_api = { + inherit database; + key_perspectives = [ + { + server_name = "matrix.org"; + keys = [ + { + key_id = "ed25519:auto"; + public_key = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; + } + { + key_id = "ed25519:a_RXGa"; + public_key = "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ"; + } + ]; + } + ]; + prefer_direct_fetch = false; + }; + user_api = { + account_database = database; + device_database = database; + }; + }; + loadCredential = [ "matrix-server-key:${cfg.secrets.matrix-server-key}" ]; + } // optionalAttrs (cfg.secrets.dendrite-envs != null) { + environmentFile = cfg.secrets.dendrite-envs; + }; + + services.prometheus.scrapeConfigs = [ + { + job_name = "dendrite"; + static_configs = [{ + targets = [ "127.0.0.1:${toString config.services.dendrite.httpPort}" ]; + }]; + } + ]; + + systemd.services.dendrite = { + after = [ "postgresql.service" ]; + }; + + environment.persistence."/persist".directories = [ + "/var/lib/private/dendrite" + ]; + + services.sliding-sync = { + enable = true; + server = "https://${cfg.realHost}"; + bindAddr = "[::1]:8009"; + db = "postgres:///syncv3?host=/run/postgresql"; + secret = cfg.secrets.sliding-sync-secret; + }; + + services.postgresql.enable = true; + services.postgresql.ensureDatabases = [ "dendrite" "syncv3" ]; + services.postgresql.ensureUsers = [ + { + name = "dendrite"; + ensurePermissions."DATABASE dendrite" = "ALL PRIVILEGES"; + } + { + name = "sliding-sync"; + ensurePermissions."DATABASE syncv3" = "ALL PRIVILEGES"; + } + ]; + + services.nginx.virtualHosts.${cfg.realHost} = { + forceSSL = true; + useACMEHost = cfg.domain; + listen = [ + { addr = "0.0.0.0"; port = 443; ssl = true; } + { addr = "[::]"; port = 443; ssl = true; } + { addr = "0.0.0.0"; port = 8448; ssl = true; } + { addr = "[::]"; port = 8448; ssl = true; } + + ]; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 600; + client_max_body_size 50M; + ''; + locations."/_matrix".proxyPass = "http://[::1]:${toString config.services.dendrite.httpPort}"; + locations."/_dendrite".proxyPass = "http://[::1]:${toString config.services.dendrite.httpPort}"; + locations."/_synapse".proxyPass = "http://[::1]:${toString config.services.dendrite.httpPort}"; + }; + + services.nginx.virtualHosts.${cfg.domain} = + let + server-hello = { "m.server" = "${cfg.realHost}:443"; }; + client-hello = { + "m.homeserver"."base_url" = "https://${cfg.realHost}"; + "m.identity_server"."base_url" = "https://vector.im"; + "org.matrix.msc3575.proxy"."url" = "https://${cfg.slidingSyncHost}"; + }; + in + { + forceSSL = true; + useACMEHost = cfg.domain; + locations = { + "/.well-known/matrix/server" = { + extraConfig = '' + add_header Content-Type application/json; + return 200 '${builtins.toJSON server-hello}'; + ''; + }; + "/.well-known/matrix/client" = { + extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON client-hello}'; + ''; + }; + }; + }; + + services.nginx.virtualHosts.${cfg.slidingSyncHost} = { + forceSSL = true; + useACMEHost = cfg.domain; + locations."/".proxyPass = "http://${config.services.sliding-sync.bindAddr}"; + }; + + networking.firewall.allowedTCPPorts = [ 443 8448 ]; + }; +} diff --git a/modules/services/dovecot.nix b/modules/services/dovecot.nix new file mode 100644 index 0000000..a33b0d1 --- /dev/null +++ b/modules/services/dovecot.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: + +with lib; +let + cfg = config.modules.services.dovecot; +in +{ + options.modules.services.dovecot = { + enable = mkEnableOption "dovecot"; + }; + + config = mkIf cfg.enable { + services.dovecot2 = { + enable = true; + }; + networking.firewall.allowedTCPPorts = [ 587 465 ]; + }; +} diff --git a/modules/services/element-web.nix b/modules/services/element-web.nix new file mode 100644 index 0000000..2b200bd --- /dev/null +++ b/modules/services/element-web.nix @@ -0,0 +1,47 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.modules.services.element-web; +in +{ + options.modules.services.element-web = { + enable = mkEnableOption "element-web"; + package = mkOption { type = types.package; default = pkgs.element-web; }; + hostName = mkOption { type = types.str; default = config.networking.hostName; }; + matrix = { + baseUrl = mkOption { type = types.str; default = "https://matrix.${config.networking.hostName}"; }; + serverName = mkOption { type = types.str; default = config.networking.hostName; }; + }; + tls.acmeHost = mkOption { type = types.str; default = cfg.hostName; }; + jitsi.domain = mkOption { type = types.str; default = "jitsi.${cfg.hostName}"; }; + }; + + config = mkIf cfg.enable { + services.nginx.virtualHosts.${cfg.hostName} = { + useACMEHost = cfg.tls.acmeHost; + forceSSL = true; + + root = cfg.package.override { + conf = { + default_server_config = { + "m.homeserver" = { + "base_url" = cfg.matrix.baseUrl; + "server_name" = cfg.matrix.serverName; + }; +