From 33639143ea50404a04bc4c454435aff1bd79dd4b Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Fri, 20 Dec 2024 13:58:21 +0100 Subject: refactor({modules,test}): Migrate to a `by-name` structure --- flake.lock | 17 ++ flake.nix | 12 +- modules/by-name/et/etesync/module.nix | 72 ++++++ modules/by-name/et/etesync/secret_file.age | 17 ++ modules/by-name/gi/git-server/css.nix | 116 +++++++++ modules/by-name/gi/git-server/module.nix | 178 ++++++++++++++ modules/by-name/ng/nginx/module.nix | 68 ++++++ modules/by-name/ng/nginx/redirects.nix | 6 + modules/by-name/ni/nix-sync/hosts.nix | 48 ++++ modules/by-name/ni/nix-sync/internal_module.nix | 299 +++++++++++++++++++++++ modules/by-name/ni/nix-sync/module.nix | 61 +++++ modules/by-name/op/openssh/module.nix | 31 +++ modules/by-name/pe/peertube/module.nix | 113 +++++++++ modules/by-name/pe/peertube/secrets/general.age | 15 ++ modules/by-name/pe/peertube/secrets/smtp.age | 16 ++ modules/default.nix | 21 ++ modules/nixos/default.nix | 5 - modules/nixos/vhack/default.nix | 10 - modules/nixos/vhack/etesync/default.nix | 72 ------ modules/nixos/vhack/etesync/secret_file.age | 17 -- modules/nixos/vhack/git-server/css.nix | 116 --------- modules/nixos/vhack/git-server/default.nix | 178 -------------- modules/nixos/vhack/nginx/default.nix | 68 ------ modules/nixos/vhack/nginx/redirects.nix | 6 - modules/nixos/vhack/nix-sync/default.nix | 61 ----- modules/nixos/vhack/nix-sync/hosts.nix | 48 ---- modules/nixos/vhack/nix-sync/module.nix | 299 ----------------------- modules/nixos/vhack/openssh/default.nix | 31 --- modules/nixos/vhack/peertube/default.nix | 113 --------- modules/nixos/vhack/peertube/secrets/general.age | 15 -- modules/nixos/vhack/peertube/secrets/smtp.age | 16 -- secrets.nix | 7 +- tests.nix | 28 --- tests/README.md | 2 +- tests/by-name/gi/git-server/ssh_keys.nix | 49 ++++ tests/by-name/gi/git-server/test.nix | 245 +++++++++++++++++++ tests/default.nix | 13 + tests/nixos/vhack/git-server/ssh_keys.nix | 49 ---- tests/nixos/vhack/git-server/test.nix | 244 ------------------ 39 files changed, 1398 insertions(+), 1384 deletions(-) create mode 100644 modules/by-name/et/etesync/module.nix create mode 100644 modules/by-name/et/etesync/secret_file.age create mode 100644 modules/by-name/gi/git-server/css.nix create mode 100644 modules/by-name/gi/git-server/module.nix create mode 100644 modules/by-name/ng/nginx/module.nix create mode 100644 modules/by-name/ng/nginx/redirects.nix create mode 100644 modules/by-name/ni/nix-sync/hosts.nix create mode 100644 modules/by-name/ni/nix-sync/internal_module.nix create mode 100644 modules/by-name/ni/nix-sync/module.nix create mode 100644 modules/by-name/op/openssh/module.nix create mode 100644 modules/by-name/pe/peertube/module.nix create mode 100644 modules/by-name/pe/peertube/secrets/general.age create mode 100644 modules/by-name/pe/peertube/secrets/smtp.age create mode 100644 modules/default.nix delete mode 100644 modules/nixos/default.nix delete mode 100644 modules/nixos/vhack/default.nix delete mode 100644 modules/nixos/vhack/etesync/default.nix delete mode 100644 modules/nixos/vhack/etesync/secret_file.age delete mode 100644 modules/nixos/vhack/git-server/css.nix delete mode 100644 modules/nixos/vhack/git-server/default.nix delete mode 100644 modules/nixos/vhack/nginx/default.nix delete mode 100644 modules/nixos/vhack/nginx/redirects.nix delete mode 100644 modules/nixos/vhack/nix-sync/default.nix delete mode 100644 modules/nixos/vhack/nix-sync/hosts.nix delete mode 100644 modules/nixos/vhack/nix-sync/module.nix delete mode 100644 modules/nixos/vhack/openssh/default.nix delete mode 100644 modules/nixos/vhack/peertube/default.nix delete mode 100644 modules/nixos/vhack/peertube/secrets/general.age delete mode 100644 modules/nixos/vhack/peertube/secrets/smtp.age delete mode 100644 tests.nix create mode 100644 tests/by-name/gi/git-server/ssh_keys.nix create mode 100644 tests/by-name/gi/git-server/test.nix create mode 100644 tests/default.nix delete mode 100644 tests/nixos/vhack/git-server/ssh_keys.nix delete mode 100644 tests/nixos/vhack/git-server/test.nix diff --git a/flake.lock b/flake.lock index a901d68..c85da85 100644 --- a/flake.lock +++ b/flake.lock @@ -170,6 +170,22 @@ "type": "github" } }, + "library": { + "locked": { + "lastModified": 1734626644, + "narHash": "sha256-p/RVC4Rp5AGN3qwlVoQJHkbEkvcilSr2lWfRgnlRXlQ=", + "ref": "prime", + "rev": "1021c1ffe1dd8dd75380dac618b93ff2cefd81f4", + "revCount": 1, + "type": "git", + "url": "https://git.vhack.eu/vhack.eu/nix-library" + }, + "original": { + "ref": "prime", + "type": "git", + "url": "https://git.vhack.eu/vhack.eu/nix-library" + } + }, "nixpkgs": { "locked": { "lastModified": 1734522913, @@ -257,6 +273,7 @@ "flake-compat": "flake-compat", "flake-utils": "flake-utils", "impermanence": "impermanence", + "library": "library", "nixpkgs": "nixpkgs", "nixpkgs-unstable": "nixpkgs-unstable", "ragenix": "ragenix", diff --git a/flake.nix b/flake.nix index d9f02c2..ff3014f 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,8 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11-small"; nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable-small"; + library.url = "git+https://git.vhack.eu/vhack.eu/nix-library?ref=prime"; + # inputs for following systems = { url = "github:nix-systems/x86_64-linux"; # only evaluate for this system @@ -73,6 +75,7 @@ self, nixpkgs, nixpkgs-unstable, + library, # modules simple-nixos-mailserver, impermanence, @@ -90,7 +93,7 @@ specialArgs = attrs // { - inherit pkgsUnstable nixpkgs-unstable nixos-lib extraModules; + inherit pkgsUnstable nixpkgs-unstable nixos-lib extraModules nixLib; }; extraModules = [ @@ -100,8 +103,9 @@ agenix.nixosModules.default ]; - inherit (pkgs) lib; - tests = import ./tests.nix {inherit lib pkgs specialArgs;}; + tests = import ./tests {inherit pkgs specialArgs nixLib;}; + + inherit (library) nixLib; in { nixosConfigurations."server1" = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; @@ -109,7 +113,7 @@ modules = extraModules ++ [ - ./modules/nixos + ./modules ./hosts/server1/configuration.nix ]; }; diff --git a/modules/by-name/et/etesync/module.nix b/modules/by-name/et/etesync/module.nix new file mode 100644 index 0000000..0f6c565 --- /dev/null +++ b/modules/by-name/et/etesync/module.nix @@ -0,0 +1,72 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.etesync; +in { + options.vhack.etesync = { + enable = lib.mkEnableOption '' + a secure, end-to-end encrypted, and privacy respecting sync for your contacts, calendars, tasks and notes. + ''; + }; + + config = lib.mkIf cfg.enable { + services.etebase-server = { + enable = true; + port = 8001; + settings = { + global.secret_file = "${config.age.secrets.etebase-server.path}"; + allowed_hosts = { + allowed_host1 = "etebase.vhack.eu"; + allowed_host2 = "dav.vhack.eu"; + }; + }; + }; + + age.secrets.etebase-server = { + file = ./secret_file.age; + mode = "700"; + owner = "etebase-server"; + group = "etebase-server"; + }; + + environment.persistence."/srv".directories = [ + { + directory = "/var/lib/etebase-server"; + user = "etebase-server"; + group = "etebase-server"; + mode = "0700"; + } + ]; + + services.nginx = { + enable = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + virtualHosts = { + "etebase.vhack.eu" = { + enableACME = true; + forceSSL = true; + + locations = { + # TODO: Maybe fix permissions to use pregenerated static files which would + # improve performance. + #"/static" = { + # root = config.services.etebase-server.settings.global.static_root; + #}; + "/" = { + proxyPass = "http://127.0.0.1:${builtins.toString config.services.etebase-server.port}"; + }; + }; + serverAliases = [ + "dav.vhack.eu" + ]; + }; + }; + }; + }; +} diff --git a/modules/by-name/et/etesync/secret_file.age b/modules/by-name/et/etesync/secret_file.age new file mode 100644 index 0000000..8d8e3c2 --- /dev/null +++ b/modules/by-name/et/etesync/secret_file.age @@ -0,0 +1,17 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0UiswNDhQNWpsaFZUQTdY +U3F2TFlrSzhMbmRBWEIyTGQ2VGVramdPTDI4CjRGSnlqUm5rWWJ2Vk5neE56azdt +WitpbXlPWngxSGtEalBKWkRZdHF5QjQKLT4gWDI1NTE5IDRSSW1jcHhocjBIM0tM +ZjRxNUhZWkhkd1c5aVlucTMxTTVhSHRIMHMyU0EKbWlQZ0xKRXUvOWluSkZQRWdp +UjNMQWR3MHNwbUVYbm4vSGJQOGtrb2ZxVQotPiBzc2gtZWQyNTUxOSBPRDhUNGcg +SEpCY1JWZm5yMG1lL3QwUERPVUFqRWo5ZVJEb1JqNGVLS3pXVkhaYk1SYwpjb3dW +UWcrMkdmYTlvckFOYmsvcGwvY1dvc1oxY1FaY2p4eURCK3BIR044Ci0+ICgreWhl +KG9RLWdyZWFzZSAobEpLXVEgNVA3IGQKekx5YVFkeFRBUlJiUis2cFVyWlBPNncK +LS0tIFJxa0hDZUIyYm5uYlhiZjRnNHRLNTRrRW01d1hCL2dCZnByL1M2SkFyQXMK +gsR7erKGQrBhXlcnR73PbnC+PzOQlsBOg6a6DosGyixbnEgZ4DfyeK5Ep1oPB81Q +zcS9AV7h+8NlpmVM4G+0JCIC8I3TTCEQyOPwiu+GVXr4GYy/3stg+pK1htkt2V2M +WraPl//K3kvFln1KRt5lbsVXLX8SYZS4UJDzK25oJElwdNuqXHqwMkTmXjEgnbvS +pjgaNak5ooxHiZfCtzismLx5iL+P/+oohegUPvW16fQTq/eKp3mIjeBZmrWNnTuL +/xlhk0vp0+jS3+TqgGWSwAAqoCp/+TewUZ9f+GhU0/pkU3HP4+tx35rKN2wxerQj +nMbQ8SphigUeMpc501oDRw6X5ZAasoww +-----END AGE ENCRYPTED FILE----- diff --git a/modules/by-name/gi/git-server/css.nix b/modules/by-name/gi/git-server/css.nix new file mode 100644 index 0000000..3d73ea0 --- /dev/null +++ b/modules/by-name/gi/git-server/css.nix @@ -0,0 +1,116 @@ +{cgitPkg, pkgs}: let + /* + Adapted from `https://git.qyliss.net/nixlib/sys/atuin.nix`, originally distributed under + the MIT license. + */ + cgitCss = + pkgs.runCommand "cgit-extra.css" { + licenseHeader = '' + /* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See . + */ + + ''; + + # Adapted from + # , + # distributed as a Larger Work under a Secondary License, + # as permitted by the terms of the + # Mozilla Public License Version 2.0. + extraCss = '' + * { line-height: 1.25em; } + + article { + font-family: sans-serif; + max-width: 70ch; + margin-left: auto; + margin-right: auto; + } + + div#cgit { + margin: auto; + font-family: monospace; + -moz-tab-size: 4; + tab-size: 4; + display: table; + } + + div#cgit table#header { + margin-left: auto; + margin-right: auto; + } + div#cgit table#header td.logo { + display: none; + } + div#cgit table#header td.main { + font-size: 1em; + font-weight: bold; + } + div#cgit table#header td.sub { + border-top: none; + } + div#cgit table.tabs { + margin-left: auto; + margin-right: auto; + border-bottom: none; + } + div#cgit div.content { + border-bottom: none; + min-width: 108ch; + } + div#cgit div.content div#summary { + display: table; + margin-left: auto; + margin-right: auto; + } + div#cgit div.notes { + border: none; + background: transparent; + padding: 0; + } + div#cgit table.list { + margin-left: auto; + margin-right: auto; + } + div#cgit table.list th a { + color: inherit; + } + div#cgit table.list tr:nth-child(even) { + background: inherit; + } + div#cgit table.list tr:hover { + background: inherit; + } + div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { + background: inherit; + } + div#cgit div.footer { + font-size: 1em; + margin-top: 0; + } + + div#cgit table.blob td.linenumbers:nth-last-child(3) { + display: none; + } + + div#cgit table.blob td.linenumbers a:target { + color: goldenrod; + text-decoration: underline; + outline: none; + } + ''; + passAsFile = ["licenseHeader" "extraCss"]; + } '' + cat $licenseHeaderPath ${cgitPkg}/cgit/cgit.css $extraCssPath > $out + ''; +in + cgitCss diff --git a/modules/by-name/gi/git-server/module.nix b/modules/by-name/gi/git-server/module.nix new file mode 100644 index 0000000..a374f4c --- /dev/null +++ b/modules/by-name/gi/git-server/module.nix @@ -0,0 +1,178 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.vhack.git-server; + + cgitCss = import ./css.nix { + inherit pkgs; + cgitPkg = + config.services.cgit."${cfg.domain}".package; + }; +in { + options.vhack.git-server = { + enable = lib.mkEnableOption '' + a lightweight git-server, realised with cgit and gitolite. + ''; + + domain = lib.mkOption { + type = lib.types.str; + default = "git.vhack.eu"; + description = '' + The domain this git instance will run under. + ''; + }; + + gitolite = { + adminPubkey = lib.mkOption { + description = '' + The initial key to use for gitolite. This will only be used for the initial + clone of the `gitolite-admin` repository. + ''; + type = lib.types.str; + default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAe4o1PM6VasT3KZNl5NYvgkkBrPOg36dqsywd10FztS openpgp:0x21D20D6A"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + programs.git = { + enable = true; + config = { + init = { + defaultBranch = "main"; + }; + }; + }; + + # Needed for the nginx proxy and the virtual host + vhack.nginx.enable = true; + + services = { + gitolite = { + inherit (cfg.gitolite) adminPubkey; + enable = true; + dataDir = "/srv/gitolite"; + user = "git"; + group = "git"; + extraGitoliteRc = '' + $RC{UMASK} = 0027; # Enable group access, important for cgit. + + # Enable modifing git variables (for cgit.owner and such things) + # These must be enable in the gitolite-admin repo (option user-configs = ...) + push( @{$RC{ENABLE}}, 'config' ); + push( @{$RC{ENABLE}}, 'git-config' ); + + push( @{$RC{ENABLE}}, 'expand-deny-messages' ); + push( @{$RC{ENABLE}}, 'Motd' ); + + push( @{$RC{ENABLE}}, 'cgit' ); + ''; + }; + + cgit."${cfg.domain}" = { + enable = true; + package = pkgs.cgit-pink; + scanPath = "${config.services.gitolite.dataDir}/repositories"; + user = "git"; + group = "git"; + settings = { + branch-sort = "age"; + + # Allow users to download a repo checkout with these compression formats + snapshots = ["tar.gz" "zip"]; + # The template used to generate the clone url for https clone. + clone-url = [ + "https://${cfg.domain}/$CGIT_REPO_URL" + "ssh://git@${cfg.domain}/$CGIT_REPO_URL" + ]; + enable-http-clone = true; + + # TODO: We might want to add an logo and readme here <2024-07-31> + # logo = ""; + # root-readme = "/some/readme/file" + root-desc = "The cgit instance of ${cfg.domain}!"; + root-title = "${ + lib.strings.toUpper (builtins.substring 0 1 cfg.domain) + builtins.substring 1 (builtins.stringLength cfg.domain) cfg.domain + } cgit instace"; + + # Set the default maximum statistics period. Valid values are "week", + # "month", "quarter" and "year". + max-stats = "week"; + + readme = [ + ":README.md" + ":readme.md" + ":README.mkd" + ":readme.mkd" + ":README.rst" + ":readme.rst" + ":README.html" + ":readme.html" + ":README.htm" + ":readme.htm" + ":README.txt" + ":readme.txt" + ":README" + ":readme" + ":INSTALL.md" + ":install.md" + ":INSTALL.mkd" + ":install.mkd" + ":INSTALL.rst" + ":install.rst" + ":INSTALL.html" + ":install.html" + ":INSTALL.htm" + ":install.htm" + ":INSTALL.txt" + ":install.txt" + ":INSTALL" + ":install" + ]; + + enable-blame = true; + enable-commit-graph = true; + enable-subject-links = true; + enable-follow-links = true; + enable-index-links = true; + enable-index-owner = true; + + # NOTE: This allows cgit to take configuration from the bare git repositories: + # All `repo.` can be set by setting `cgit.` in the git config. E.g.: + # setting the owner (i.e. `repo.owner`) would be done by setting the + # `cgit.owner` config. All repo options are outline in the cgitrc (5) man page. + enable-git-config = true; + + # Remove the `.git` suffix from scanned repositories (this must be set _before_ `scan-path`) + remove-suffix = true; + + css = "/custom_cgit.css"; + + # This is a number of path elements to treat as section. + # `-1` means that we treat the last element as name, all others as sections + section-from-path = -1; + + project-list = "${config.services.gitolite.dataDir}/projects.list"; + + # TODO: We might want to use the kernel.org `libravatar.lua` email-filter <2024-07-31> + source-filter = "${config.services.cgit."${cfg.domain}".package}/lib/cgit/filters/syntax-highlighting.py"; + about-filter = "${config.services.cgit."${cfg.domain}".package}/lib/cgit/filters/about-formatting.sh"; + }; + }; + + nginx.virtualHosts."${cfg.domain}" = { + enableACME = true; + forceSSL = true; + + locations = { + "= /custom_cgit.css" = { + alias = cgitCss.outPath; + }; + }; + }; + }; + }; +} diff --git a/modules/by-name/ng/nginx/module.nix b/modules/by-name/ng/nginx/module.nix new file mode 100644 index 0000000..6a82147 --- /dev/null +++ b/modules/by-name/ng/nginx/module.nix @@ -0,0 +1,68 @@ +{ + lib, + config, + ... +}: let + importedRedirects = import ./redirects.nix {}; + mkRedirect = { + key, + value, + }: { + name = key; + value = { + forceSSL = true; + enableACME = true; + locations."/".return = "301 ${value}"; + }; + }; + + redirects = builtins.listToAttrs (builtins.map mkRedirect importedRedirects); + + cfg = config.vhack.nginx; +in { + options.vhack.nginx = { + enable = lib.mkEnableOption '' + a default nginx config. + ''; + + selfsign = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to selfsign the acme certificates. This should only + really be useful for tests. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults = { + email = "admin@vhack.eu"; + webroot = "/var/lib/acme/acme-challenge"; + + # Avoid spamming the acme server, if we run in a test, and only really want self-signed + # certificates + server = lib.mkIf cfg.selfsign "https://127.0.0.1"; + }; + }; + + networking.firewall = { + allowedTCPPorts = [80 443]; + }; + services.nginx = { + enable = true; + # The merge here is fine, as no domain should be specified twice + virtualHosts = + { + "gallery.s-schoeffel.de" = { + forceSSL = true; + enableACME = true; + root = "/srv/gallery.s-schoeffel.de"; + }; + } + // redirects; + }; + }; +} diff --git a/modules/by-name/ng/nginx/redirects.nix b/modules/by-name/ng/nginx/redirects.nix new file mode 100644 index 0000000..a021e72 --- /dev/null +++ b/modules/by-name/ng/nginx/redirects.nix @@ -0,0 +1,6 @@ +{...}: [ + { + key = "source.vhack.eu"; + value = "https://codeberg.org/vhack.eu/nixos-server"; + } +] diff --git a/modules/by-name/ni/nix-sync/hosts.nix b/modules/by-name/ni/nix-sync/hosts.nix new file mode 100644 index 0000000..98dbbf1 --- /dev/null +++ b/modules/by-name/ni/nix-sync/hosts.nix @@ -0,0 +1,48 @@ +{...}: let + extraWkdSettings = { + locations."/.well-known/openpgpkey/hu/".extraConfig = '' + default_type application/octet-stream; + + # Came from: https://www.uriports.com/blog/setting-up-openpgp-web-key-directory/ + # No idea if it is actually necessary + # add_header Access-Control-Allow-Origin * always; + ''; + }; +in [ + { + domain = "vhack.eu"; + url = "https://codeberg.org/vhack.eu/website.git"; + } + { + domain = "b-peetz.de"; + url = "https://codeberg.org/bpeetz/b-peetz.de.git"; + } + + # Trinitrix + { + domain = "trinitrix.vhack.eu"; + url = "https://codeberg.org/trinitrix/website.git"; + } + + # WKD + { + domain = "openpgpkey.b-peetz.de"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } + { + domain = "openpgpkey.s-schoeffel.de"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } + { + domain = "openpgpkey.sils.li"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } + { + domain = "openpgpkey.vhack.eu"; + url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; + extraSettings = extraWkdSettings; + } +] diff --git a/modules/by-name/ni/nix-sync/internal_module.nix b/modules/by-name/ni/nix-sync/internal_module.nix new file mode 100644 index 0000000..a3ab0af --- /dev/null +++ b/modules/by-name/ni/nix-sync/internal_module.nix @@ -0,0 +1,299 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.services.nix-sync; + esa = lib.strings.escapeShellArg; + + mkTimer = name: repo: { + description = "Nix sync ${name} timer"; + wantedBy = ["timers.target"]; + timerConfig = { + OnUnitActiveSec = repo.interval; + }; + wants = ["network-online.target"]; + after = ["network-online.target"]; + }; + + parents = path: let + split_path = builtins.split "/" path; + filename = builtins.elemAt split_path (builtins.length split_path - 1); + path_build = + lib.strings.removeSuffix "/" (builtins.replaceStrings [filename] [""] path); + final_path = + if filename == "" + then parents path_build + else path_build; + in + final_path; + + mkUnit = name: repo: let + optionalPathSeparator = + if lib.strings.hasPrefix "/" repo.path + then "" + else "/"; + /* + * `ln` tries to create a symlink in the directory, if the target ends with a '/', + * thus remove it. + */ + repoPath = lib.strings.removeSuffix "/" repo.path; + + repoCachePath = cfg.cachePath + optionalPathSeparator + repo.path; + execStartScript = pkgs.writeScript "nix-sync-exec" '' + #! /usr/bin/env dash + export XDG_CACHE_HOME="$CACHE_DIRECTORY"; + cd ${esa repoCachePath}; + + git fetch + origin="$(git rev-parse @{u})"; + branch="$(git rev-parse @)"; + + if ! [ "$origin" = "$branch" ]; then + git pull --rebase; + + out_paths=$(mktemp); + nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; + [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) + out_path="$(cat "$out_paths")"; + rm ${esa repoPath}; + ln -s "$out_path" ${esa repoPath}; + rm "$out_paths"; + fi + ''; + execStartPreScript = '' + export XDG_CACHE_HOME="$CACHE_DIRECTORY"; + + if ! [ -d ${esa repoCachePath}/.git ]; then + mkdir --parents ${esa repoCachePath}; + git clone ${esa repo.uri} ${esa repoCachePath}; + + out_paths=$(mktemp); + nix build ${esa repoCachePath} --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; + [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) + out_path="$(cat "$out_paths")"; + ln -s "$out_path" ${esa repoPath}; + rm "$out_paths"; + fi + + if ! [ -L ${esa repoPath} ]; then + cd ${esa repoCachePath}; + + git pull --rebase; + + out_paths=$(mktemp); + nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; + [ "$(wc -l < "$out_paths")" -gt 1 ] && { echo "To many out-paths"; exit 1; } + out_path="$(cat "$out_paths")"; + + if [ -d ${esa repoPath} ]; then + rm -d ${esa repoPath}; + else + mkdir --parents "$(dirname ${esa repoPath})"; + fi + [ -e ${esa repoPath} ] && rm ${esa repoPath}; + + ln -s "$out_path" ${esa repoPath}; + rm "$out_paths"; + fi + ''; + in { + description = "Nix Sync ${name}"; + wantedBy = ["default.target"]; + after = ["network.target"]; + path = with pkgs; [openssh git nix mktemp coreutils dash]; + preStart = execStartPreScript; + + serviceConfig = { + TimeoutSec = 0; + ExecStart = execStartScript; + Restart = "on-abort"; + # User and group + User = cfg.user; + Group = cfg.group; + # Runtime directory and mode + RuntimeDirectory = "nix-sync"; + RuntimeDirectoryMode = "0750"; + # Cache directory and mode + CacheDirectory = "nix-sync"; + CacheDirectoryMode = "0750"; + # Logs directory and mode + LogsDirectory = "nix-sync"; + LogsDirectoryMode = "0750"; + # Proc filesystem + ProcSubset = "all"; + ProtectProc = "invisible"; + # New file permissions + UMask = "0027"; # 0640 / 0750 + # Capabilities + AmbientCapabilities = ["CAP_CHOWN"]; + CapabilityBoundingSet = ["CAP_CHOWN"]; + # Security + NoNewPrivileges = true; + # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html) + ReadWritePaths = ["${esa (parents repo.path)}" "-${esa (parents repoCachePath)}" "-${esa cfg.cachePath}"]; + ReadOnlyPaths = ["/nix"]; # TODO: Should be irrelevant, as we have ProtectSystem=Strict <2024-06-01> + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + PrivateMounts = true; + # System Call Filtering + SystemCallArchitectures = "native"; + SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"]; + }; + }; + + services = + lib.mapAttrs' (name: repo: { + name = "nix-sync-${name}"; + value = mkUnit name repo; + }) + cfg.repositories; + timers = + lib.mapAttrs' (name: repo: { + name = "nix-sync-${name}"; + value = mkTimer name repo; + }) + cfg.repositories; + + # generate the websites directory, so systemd can mount it read write + generatedDirectories = + lib.mapAttrsToList ( + _: repo: "d ${esa (parents repo.path)} 0755 ${cfg.user} ${cfg.group}" + ) + cfg.repositories; + + repositoryType = lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + internal = true; + default = name; + type = lib.types.str; + description = "The name that should be given to this unit."; + }; + + path = lib.mkOption { + type = lib.types.str; + description = "The path at which to sync the repository"; + }; + + uri = lib.mkOption { + type = lib.types.str; + example = "ssh://user@example.com:/~[user]/path/to/repo.git"; + description = '' + The URI of the remote to be synchronized. This is only used in the + event that the directory does not already exist. See + + for the supported URIs. + ''; + }; + + extraSettings = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + example = lib.literalExpression '' + { + locations."/.well-known/openpgpkey/hu/" = { + extraConfig = \'\' + default_type application/octet-stream; + + add_header Access-Control-Allow-Origin * always; + \'\'; + }; + } + ''; + description = '' + Extra config to add the the nginx virtual host. + ''; + }; + + interval = lib.mkOption { + type = lib.types.int; + default = 500; + description = '' + The interval, specified in seconds, at which the synchronization will + be triggered. + ''; + }; + }; + }); +in { + options = { + services.nix-sync = { + enable = lib.mkEnableOption "nix-sync services"; + + user = lib.mkOption { + type = lib.types.str; + default = "nix-sync"; + description = lib.mdDoc "User account under which nix-sync units runs."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "nix-sync"; + description = lib.mdDoc "Group account under which nix-sync units runs."; + }; + + cachePath = lib.mkOption { + type = lib.types.str; + default = "/var/lib/nix-sync"; + description = lib.mdDoc '' + Where to cache git directories. Should not end with a slash ("/") + ''; + }; + + repositories = lib.mkOption { + type = with lib.types; attrsOf repositoryType; + description = '' + The repositories that should be synchronized. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = !lib.strings.hasSuffix "/" cfg.cachePath; + message = "Your cachePath ('${cfg.cachePath}') ends with a slash ('/'), please use: '${lib.strings.removeSuffix "/" cfg.cachePath}'."; + } + ]; + systemd = { + tmpfiles.rules = + generatedDirectories; + + inherit services timers; + }; + users.users = + if cfg.user == "nix-sync" + then { + nix-sync = { + group = "${cfg.group}"; + isSystemUser = true; + }; + } + else lib.warnIf (cfg.user != "nix-sync") "The user (${cfg.user}) is not \"nix-sync\", thus you are responible for generating it."; + users.groups = + if cfg.group == "nix-sync" + then { + nix-sync = { + members = ["${cfg.user}"]; + }; + } + else lib.warnIf (cfg.group != "nix-sync") "The group (${cfg.group}) is not \"nix-sync\", thus you are responible for generating it."; + }; +} diff --git a/modules/by-name/ni/nix-sync/module.nix b/modules/by-name/ni/nix-sync/module.nix new file mode 100644 index 0000000..0a92888 --- /dev/null +++ b/modules/by-name/ni/nix-sync/module.nix @@ -0,0 +1,61 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.nix-sync; + + mkNixSyncRepository = { + domain, + root ? "", + url, + extraSettings ? {}, + }: { + name = "${domain}"; + value = { + path = "/etc/nginx/websites/${domain}/${root}"; + uri = "${url}"; + inherit extraSettings; + }; + }; + nixSyncRepositories = builtins.listToAttrs (builtins.map mkNixSyncRepository domains); + + mkVirtHost = { + domain, + root ? "", + url, + extraSettings ? {}, + }: { + name = "${domain}"; + value = + lib.recursiveUpdate { + forceSSL = true; + enableACME = true; + root = "/etc/nginx/websites/${domain}/${root}"; + } + extraSettings; + }; + virtHosts = builtins.listToAttrs (builtins.map mkVirtHost domains); + + domains = import ./hosts.nix {}; +in { + imports = [ + ./internal_module.nix + ]; + + options.vhack.nix-sync = { + enable = lib.mkEnableOption '' + a website git ops solution. + ''; + }; + + config = lib.mkIf cfg.enable { + services.nix-sync = { + enable = true; + repositories = nixSyncRepositories; + }; + + vhack.nginx.enable = true; + services.nginx.virtualHosts = virtHosts; + }; +} diff --git a/modules/by-name/op/openssh/module.nix b/modules/by-name/op/openssh/module.nix new file mode 100644 index 0000000..30d16a6 --- /dev/null +++ b/modules/by-name/op/openssh/module.nix @@ -0,0 +1,31 @@ +{ + config, + lib, + ... +}: let + cfg = config.vhack.openssh; +in { + options.vhack.openssh = { + enable = lib.mkEnableOption '' + a sane openssh implementation. + ''; + }; + + config = lib.mkIf cfg.enable { + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + hostKeys = [ + { + # See the explanation for this in /system/impermanence/mods/openssh.nix + # path = "/var/lib/sshd/ssh_host_ed25519_key"; + + # FIXME: Remove this workaround + path = "/srv/var/lib/sshd/ssh_host_ed25519_key"; + rounds = 1000; + type = "ed25519"; + } + ]; + }; + }; +} diff --git a/modules/by-name/pe/peertube/module.nix b/modules/by-name/pe/peertube/module.nix new file mode 100644 index 0000000..29d1d07 --- /dev/null +++ b/modules/by-name/pe/peertube/module.nix @@ -0,0 +1,113 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.vhack.peertube; +in { + options.vhack.peertube = { + enable = lib.mkEnableOption '' + the peertube video platform. + ''; + }; + + config = lib.mkIf cfg.enable { + services.peertube = { + enable = true; + + configureNginx = true; + localDomain = "peertube.vhack.eu"; + enableWebHttps = true; + listenWeb = 443; + + smtp = { + createLocally = true; + passwordFile = "${config.age.secrets.peertubeSmtp.path}"; + }; + database = { + createLocally = true; + }; + redis = { + enableUnixSocket = true; + createLocally = true; + }; + + secrets.secretsFile = "${config.age.secrets.peertubeGeneral.path}"; + + settings = { + signup = { + enabled = true; + + limit = 10; # When the limit is reached, registrations are disabled. -1 == unlimited + + minimum_age = 18; # Used to configure the signup form + + # Users fill a form to register so moderators can accept/reject the registration + requires_approval = true; + requires_email_verification = true; + }; + user = { + video_quota = "10GB"; + video_quota_daily = "2GB"; + }; + auto_blacklist = { + videos = { + of_users = { + enabled = true; + }; + }; + }; + listen.hostname = "127.0.0.1"; + instance.name = "PeerTube at Vhack.eu"; + + admin.email = "admin@vhack.eu"; + + smtp = let + emailAddress = "peertube@vhack.eu"; + in { + sendmail = "${pkgs.postfix}/bin/sendmail"; + + transport = "sendmail"; + hostname = "server1.vhack.eu"; + port = 587; + username = emailAddress; + tls = true; + disable_starttls = true; + from_address = emailAddress; + }; + }; + }; + + # The `configureNginx` option does not do this for some reason + # TODO(@bpeetz): Find out why <2024-06-27> + services.nginx.virtualHosts."${config.services.peertube.localDomain}" = { + enableACME = true; + forceSSL = true; + }; + + age.secrets = { + peertubeGeneral = { + file = ./secrets/general.age; + mode = "700"; + owner = "peertube"; + group = "peertube"; + }; + peertubeSmtp = { + file = ./secrets/smtp.age; + mode = "700"; + owner = "peertube"; + group = "peertube"; + }; + }; + + environment.persistence."/srv".directories = [ + { + directory = "/var/lib/peertube"; + user = "peertube"; + group = "peertube"; + mode = "0700"; + } + ]; + }; +} diff --git a/modules/by-name/pe/peertube/secrets/general.age b/modules/by-name/pe/peertube/secrets/general.age new file mode 100644 index 0000000..854ab1a --- /dev/null +++ b/modules/by-name/pe/peertube/secrets/general.age @@ -0,0 +1,15 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlNjR4TDVUZmY2Y0hYT2hk +YmtPcFIxSXplNWF4M0V1Kzh2b2VoSTFCK0dzCmpwT2tDa3FpR082V2pyelBoS05o +RmlWRVdNdVhZbkRVUEVnaDlPdlN1bDAKLT4gWDI1NTE5IFlvaTFPc2JHcWczbEJy +eVZDS2NaUzBvbnpadk5ySVFxRTlNVXhrd2N0a3MKanJ0NEZWaTg3dE5Cbm9uNHNS +ZCs2dmU4RkFZOHNyNlJKa0cyd2VqSlFPQQotPiBzc2gtZWQyNTUxOSBPRDhUNGcg +NXhFSHdWUk1sbEUyb3FTdGpIaHlyTUJlMnlzNXBEY2lzTXpuM09WVDBrOApmM05W +d1VBSGlhMmlDYlhZS1hSdlJBUVkrVWs0bTJseS9BUmZGY1l5K0NBCi0+IEQkNi1l +LWdyZWFzZSAhIUlaOnNsZCAsUVRVKiBfRig2KGg+NSA6CmI0Q0N0cmlFbnNGSFZQ +WThEV0RHS0V2NTVaZnIyK2tUQXZTOHdsRkhyRlExdCtOeHRML2hFNDNxd08xQjlG +V3oKMThoQnF4Y3FDU3hMZjhwRUNvVWRRR3I4c1k5QnhJS1dRR2dod0EKLS0tIEZT +dHhnVXdHV3QzYThXWFJQL2szeTZ4SWM4czZYQWxJOFFIVjBZSnJ0K00KH8WdXv68 +rjAqo5RoWu91aVg5Bl2HKuiFbaGcnlkiMPZ9wGfpq4mpCc/yc4NTa6HhkaI5tA61 +PjKurnkiLXywcdyUTPuaykk+wANynLucbwfq/Mv3aLcG01soh+dFNKZV/g== +-----END AGE ENCRYPTED FILE----- diff --git a/modules/by-name/pe/peertube/secrets/smtp.age b/modules/by-name/pe/peertube/secrets/smtp.age new file mode 100644 index 0000000..1979ea7 --- /dev/null +++ b/modules/by-name/pe/peertube/secrets/smtp.age @@ -0,0 +1,16 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtU05NMDN0Q2MrVGEraHpH +Tkwyd0NuVEQwcjljd0NUNUpsSER0V0RldWxNCjlnRHZWNmprVDYxQm90Q3pFVHR5 +enJyUTZhSVdUL1I1aC9Ya2NkMElQaFUKLT4gWDI1NTE5IHprWjRDZVlMK3Rmd1A5 +K0pZRVBIYldsOW0wQXp4SmJzM1pXdzAvZVpiWEUKd1cwR2ZNZTh6WXhQNGZBVmdN +VWpxZGxPZXJBT1dqUFd1aU4xaHAxckZLcwotPiBzc2gtZWQyNTUxOSBPRDhUNGcg +akdaS3I3VHplOENIZDg5TSt2SmRCSGpjaUZoUHVYTFJRR2wzc1RHYWNnVQpFN1Ew +MTZDNGNyKzB1aEdTMHpKaWlFLzE0blJpZ3RhOTZReTNucUp6SEdZCi0+IHloQSUq +LWdyZWFzZQo5VitXYjNxck5FbnkwYlBvUyt6R2ROVG9JOWtQNGJma1ZYd29oVlFx +blFzSytWNDA4d3lqWE9JTUVreCs2Wi92ClZCdFgwYmRmc1VsU0NhTVR4b2dtZkpK +ZTU2M24zVjd0UTRrelFXYnFEZwotLS0gT2ZlRGJsZWNPcEwxK2drdDhVSndDV3Fj +SENsN2piWWEzSFI2OW8xbk12cwrFU4dzHxb5M3miGDpWLh3XbwzsrqWlFWLLu0Ht +SDvqJGrwAPsnVn4YLSG42q1BodYfcQVvVwqRCVbkubEUDcecDTdaYDvjaS3tmDZW +u5Nabp1ujYuIewOEZ8w41napS0C553qq0mL5sYZH1C23ViW81va4X1XOJTCnmbz6 +lbh+lK8ZbZz3cer49nR8OHTtpjA9hrf4Pf/W2nMR+0exy4zDYw== +-----END AGE ENCRYPTED FILE----- diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..61d259d --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,21 @@ +{nixLib, ...}: let + files = + builtins.attrValues + (nixLib.mkByName { + baseDirectory = ./by-name; + fileName = "module.nix"; + + # We only want the base paths. + finalizeFunction = name: value: value; + + # TODO: Re-activate, when/if most modules have tests. <2024-11-23> + # coImportsNameFunction = { + # shard, + # name, + # }: + # ../tests/by-name + "/${shard}" + "/${name}" + "/test.nix"; + # coImportsWarnMessageObject = "test"; + }); +in { + imports = files; +} diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix deleted file mode 100644 index fa21596..0000000 --- a/modules/nixos/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - imports = [ - ./vhack - ]; -} diff --git a/modules/nixos/vhack/default.nix b/modules/nixos/vhack/default.nix deleted file mode 100644 index bed22af..0000000 --- a/modules/nixos/vhack/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{...}: { - imports = [ - ./etesync - ./git-server - ./nginx - ./nix-sync - ./openssh - ./peertube - ]; -} diff --git a/modules/nixos/vhack/etesync/default.nix b/modules/nixos/vhack/etesync/default.nix deleted file mode 100644 index 0f6c565..0000000 --- a/modules/nixos/vhack/etesync/default.nix +++ /dev/null @@ -1,72 +0,0 @@ -{ - config, - lib, - ... -}: let - cfg = config.vhack.etesync; -in { - options.vhack.etesync = { - enable = lib.mkEnableOption '' - a secure, end-to-end encrypted, and privacy respecting sync for your contacts, calendars, tasks and notes. - ''; - }; - - config = lib.mkIf cfg.enable { - services.etebase-server = { - enable = true; - port = 8001; - settings = { - global.secret_file = "${config.age.secrets.etebase-server.path}"; - allowed_hosts = { - allowed_host1 = "etebase.vhack.eu"; - allowed_host2 = "dav.vhack.eu"; - }; - }; - }; - - age.secrets.etebase-server = { - file = ./secret_file.age; - mode = "700"; - owner = "etebase-server"; - group = "etebase-server"; - }; - - environment.persistence."/srv".directories = [ - { - directory = "/var/lib/etebase-server"; - user = "etebase-server"; - group = "etebase-server"; - mode = "0700"; - } - ]; - - services.nginx = { - enable = true; - recommendedTlsSettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; - recommendedProxySettings = true; - - virtualHosts = { - "etebase.vhack.eu" = { - enableACME = true; - forceSSL = true; - - locations = { - # TODO: Maybe fix permissions to use pregenerated static files which would - # improve performance. - #"/static" = { - # root = config.services.etebase-server.settings.global.static_root; - #}; - "/" = { - proxyPass = "http://127.0.0.1:${builtins.toString config.services.etebase-server.port}"; - }; - }; - serverAliases = [ - "dav.vhack.eu" - ]; - }; - }; - }; - }; -} diff --git a/modules/nixos/vhack/etesync/secret_file.age b/modules/nixos/vhack/etesync/secret_file.age deleted file mode 100644 index 8d8e3c2..0000000 --- a/modules/nixos/vhack/etesync/secret_file.age +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0UiswNDhQNWpsaFZUQTdY -U3F2TFlrSzhMbmRBWEIyTGQ2VGVramdPTDI4CjRGSnlqUm5rWWJ2Vk5neE56azdt -WitpbXlPWngxSGtEalBKWkRZdHF5QjQKLT4gWDI1NTE5IDRSSW1jcHhocjBIM0tM -ZjRxNUhZWkhkd1c5aVlucTMxTTVhSHRIMHMyU0EKbWlQZ0xKRXUvOWluSkZQRWdp -UjNMQWR3MHNwbUVYbm4vSGJQOGtrb2ZxVQotPiBzc2gtZWQyNTUxOSBPRDhUNGcg -SEpCY1JWZm5yMG1lL3QwUERPVUFqRWo5ZVJEb1JqNGVLS3pXVkhaYk1SYwpjb3dW -UWcrMkdmYTlvckFOYmsvcGwvY1dvc1oxY1FaY2p4eURCK3BIR044Ci0+ICgreWhl -KG9RLWdyZWFzZSAobEpLXVEgNVA3IGQKekx5YVFkeFRBUlJiUis2cFVyWlBPNncK -LS0tIFJxa0hDZUIyYm5uYlhiZjRnNHRLNTRrRW01d1hCL2dCZnByL1M2SkFyQXMK -gsR7erKGQrBhXlcnR73PbnC+PzOQlsBOg6a6DosGyixbnEgZ4DfyeK5Ep1oPB81Q -zcS9AV7h+8NlpmVM4G+0JCIC8I3TTCEQyOPwiu+GVXr4GYy/3stg+pK1htkt2V2M -WraPl//K3kvFln1KRt5lbsVXLX8SYZS4UJDzK25oJElwdNuqXHqwMkTmXjEgnbvS -pjgaNak5ooxHiZfCtzismLx5iL+P/+oohegUPvW16fQTq/eKp3mIjeBZmrWNnTuL -/xlhk0vp0+jS3+TqgGWSwAAqoCp/+TewUZ9f+GhU0/pkU3HP4+tx35rKN2wxerQj -nMbQ8SphigUeMpc501oDRw6X5ZAasoww ------END AGE ENCRYPTED FILE----- diff --git a/modules/nixos/vhack/git-server/css.nix b/modules/nixos/vhack/git-server/css.nix deleted file mode 100644 index 3d73ea0..0000000 --- a/modules/nixos/vhack/git-server/css.nix +++ /dev/null @@ -1,116 +0,0 @@ -{cgitPkg, pkgs}: let - /* - Adapted from `https://git.qyliss.net/nixlib/sys/atuin.nix`, originally distributed under - the MIT license. - */ - cgitCss = - pkgs.runCommand "cgit-extra.css" { - licenseHeader = '' - /* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * See . - */ - - ''; - - # Adapted from - # , - # distributed as a Larger Work under a Secondary License, - # as permitted by the terms of the - # Mozilla Public License Version 2.0. - extraCss = '' - * { line-height: 1.25em; } - - article { - font-family: sans-serif; - max-width: 70ch; - margin-left: auto; - margin-right: auto; - } - - div#cgit { - margin: auto; - font-family: monospace; - -moz-tab-size: 4; - tab-size: 4; - display: table; - } - - div#cgit table#header { - margin-left: auto; - margin-right: auto; - } - div#cgit table#header td.logo { - display: none; - } - div#cgit table#header td.main { - font-size: 1em; - font-weight: bold; - } - div#cgit table#header td.sub { - border-top: none; - } - div#cgit table.tabs { - margin-left: auto; - margin-right: auto; - border-bottom: none; - } - div#cgit div.content { - border-bottom: none; - min-width: 108ch; - } - div#cgit div.content div#summary { - display: table; - margin-left: auto; - margin-right: auto; - } - div#cgit div.notes { - border: none; - background: transparent; - padding: 0; - } - div#cgit table.list { - margin-left: auto; - margin-right: auto; - } - div#cgit table.list th a { - color: inherit; - } - div#cgit table.list tr:nth-child(even) { - background: inherit; - } - div#cgit table.list tr:hover { - background: inherit; - } - div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { - background: inherit; - } - div#cgit div.footer { - font-size: 1em; - margin-top: 0; - } - - div#cgit table.blob td.linenumbers:nth-last-child(3) { - display: none; - } - - div#cgit table.blob td.linenumbers a:target { - color: goldenrod; - text-decoration: underline; - outline: none; - } - ''; - passAsFile = ["licenseHeader" "extraCss"]; - } '' - cat $licenseHeaderPath ${cgitPkg}/cgit/cgit.css $extraCssPath > $out - ''; -in - cgitCss diff --git a/modules/nixos/vhack/git-server/default.nix b/modules/nixos/vhack/git-server/default.nix deleted file mode 100644 index a374f4c..0000000 --- a/modules/nixos/vhack/git-server/default.nix +++ /dev/null @@ -1,178 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - cfg = config.vhack.git-server; - - cgitCss = import ./css.nix { - inherit pkgs; - cgitPkg = - config.services.cgit."${cfg.domain}".package; - }; -in { - options.vhack.git-server = { - enable = lib.mkEnableOption '' - a lightweight git-server, realised with cgit and gitolite. - ''; - - domain = lib.mkOption { - type = lib.types.str; - default = "git.vhack.eu"; - description = '' - The domain this git instance will run under. - ''; - }; - - gitolite = { - adminPubkey = lib.mkOption { - description = '' - The initial key to use for gitolite. This will only be used for the initial - clone of the `gitolite-admin` repository. - ''; - type = lib.types.str; - default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAe4o1PM6VasT3KZNl5NYvgkkBrPOg36dqsywd10FztS openpgp:0x21D20D6A"; - }; - }; - }; - - config = lib.mkIf cfg.enable { - programs.git = { - enable = true; - config = { - init = { - defaultBranch = "main"; - }; - }; - }; - - # Needed for the nginx proxy and the virtual host - vhack.nginx.enable = true; - - services = { - gitolite = { - inherit (cfg.gitolite) adminPubkey; - enable = true; - dataDir = "/srv/gitolite"; - user = "git"; - group = "git"; - extraGitoliteRc = '' - $RC{UMASK} = 0027; # Enable group access, important for cgit. - - # Enable modifing git variables (for cgit.owner and such things) - # These must be enable in the gitolite-admin repo (option user-configs = ...) - push( @{$RC{ENABLE}}, 'config' ); - push( @{$RC{ENABLE}}, 'git-config' ); - - push( @{$RC{ENABLE}}, 'expand-deny-messages' ); - push( @{$RC{ENABLE}}, 'Motd' ); - - push( @{$RC{ENABLE}}, 'cgit' ); - ''; - }; - - cgit."${cfg.domain}" = { - enable = true; - package = pkgs.cgit-pink; - scanPath = "${config.services.gitolite.dataDir}/repositories"; - user = "git"; - group = "git"; - settings = { - branch-sort = "age"; - - # Allow users to download a repo checkout with these compression formats - snapshots = ["tar.gz" "zip"]; - # The template used to generate the clone url for https clone. - clone-url = [ - "https://${cfg.domain}/$CGIT_REPO_URL" - "ssh://git@${cfg.domain}/$CGIT_REPO_URL" - ]; - enable-http-clone = true; - - # TODO: We might want to add an logo and readme here <2024-07-31> - # logo = ""; - # root-readme = "/some/readme/file" - root-desc = "The cgit instance of ${cfg.domain}!"; - root-title = "${ - lib.strings.toUpper (builtins.substring 0 1 cfg.domain) + builtins.substring 1 (builtins.stringLength cfg.domain) cfg.domain - } cgit instace"; - - # Set the default maximum statistics period. Valid values are "week", - # "month", "quarter" and "year". - max-stats = "week"; - - readme = [ - ":README.md" - ":readme.md" - ":README.mkd" - ":readme.mkd" - ":README.rst" - ":readme.rst" - ":README.html" - ":readme.html" - ":README.htm" - ":readme.htm" - ":README.txt" - ":readme.txt" - ":README" - ":readme" - ":INSTALL.md" - ":install.md" - ":INSTALL.mkd" - ":install.mkd" - ":INSTALL.rst" - ":install.rst" - ":INSTALL.html" - ":install.html" - ":INSTALL.htm" - ":install.htm" - ":INSTALL.txt" - ":install.txt" - ":INSTALL" - ":install" - ]; - - enable-blame = true; - enable-commit-graph = true; - enable-subject-links = true; - enable-follow-links = true; - enable-index-links = true; - enable-index-owner = true; - - # NOTE: This allows cgit to take configuration from the bare git repositories: - # All `repo.` can be set by setting `cgit.` in the git config. E.g.: - # setting the owner (i.e. `repo.owner`) would be done by setting the - # `cgit.owner` config. All repo options are outline in the cgitrc (5) man page. - enable-git-config = true; - - # Remove the `.git` suffix from scanned repositories (this must be set _before_ `scan-path`) - remove-suffix = true; - - css = "/custom_cgit.css"; - - # This is a number of path elements to treat as section. - # `-1` means that we treat the last element as name, all others as sections - section-from-path = -1; - - project-list = "${config.services.gitolite.dataDir}/projects.list"; - - # TODO: We might want to use the kernel.org `libravatar.lua` email-filter <2024-07-31> - source-filter = "${config.services.cgit."${cfg.domain}".package}/lib/cgit/filters/syntax-highlighting.py"; - about-filter = "${config.services.cgit."${cfg.domain}".package}/lib/cgit/filters/about-formatting.sh"; - }; - }; - - nginx.virtualHosts."${cfg.domain}" = { - enableACME = true; - forceSSL = true; - - locations = { - "= /custom_cgit.css" = { - alias = cgitCss.outPath; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/vhack/nginx/default.nix b/modules/nixos/vhack/nginx/default.nix deleted file mode 100644 index 6a82147..0000000 --- a/modules/nixos/vhack/nginx/default.nix +++ /dev/null @@ -1,68 +0,0 @@ -{ - lib, - config, - ... -}: let - importedRedirects = import ./redirects.nix {}; - mkRedirect = { - key, - value, - }: { - name = key; - value = { - forceSSL = true; - enableACME = true; - locations."/".return = "301 ${value}"; - }; - }; - - redirects = builtins.listToAttrs (builtins.map mkRedirect importedRedirects); - - cfg = config.vhack.nginx; -in { - options.vhack.nginx = { - enable = lib.mkEnableOption '' - a default nginx config. - ''; - - selfsign = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Whether to selfsign the acme certificates. This should only - really be useful for tests. - ''; - }; - }; - - config = lib.mkIf cfg.enable { - security.acme = { - acceptTerms = true; - defaults = { - email = "admin@vhack.eu"; - webroot = "/var/lib/acme/acme-challenge"; - - # Avoid spamming the acme server, if we run in a test, and only really want self-signed - # certificates - server = lib.mkIf cfg.selfsign "https://127.0.0.1"; - }; - }; - - networking.firewall = { - allowedTCPPorts = [80 443]; - }; - services.nginx = { - enable = true; - # The merge here is fine, as no domain should be specified twice - virtualHosts = - { - "gallery.s-schoeffel.de" = { - forceSSL = true; - enableACME = true; - root = "/srv/gallery.s-schoeffel.de"; - }; - } - // redirects; - }; - }; -} diff --git a/modules/nixos/vhack/nginx/redirects.nix b/modules/nixos/vhack/nginx/redirects.nix deleted file mode 100644 index a021e72..0000000 --- a/modules/nixos/vhack/nginx/redirects.nix +++ /dev/null @@ -1,6 +0,0 @@ -{...}: [ - { - key = "source.vhack.eu"; - value = "https://codeberg.org/vhack.eu/nixos-server"; - } -] diff --git a/modules/nixos/vhack/nix-sync/default.nix b/modules/nixos/vhack/nix-sync/default.nix deleted file mode 100644 index a624e0e..0000000 --- a/modules/nixos/vhack/nix-sync/default.nix +++ /dev/null @@ -1,61 +0,0 @@ -{ - config, - lib, - ... -}: let - cfg = config.vhack.nix-sync; - - mkNixSyncRepository = { - domain, - root ? "", - url, - extraSettings ? {}, - }: { - name = "${domain}"; - value = { - path = "/etc/nginx/websites/${domain}/${root}"; - uri = "${url}"; - inherit extraSettings; - }; - }; - nixSyncRepositories = builtins.listToAttrs (builtins.map mkNixSyncRepository domains); - - mkVirtHost = { - domain, - root ? "", - url, - extraSettings ? {}, - }: { - name = "${domain}"; - value = - lib.recursiveUpdate { - forceSSL = true; - enableACME = true; - root = "/etc/nginx/websites/${domain}/${root}"; - } - extraSettings; - }; - virtHosts = builtins.listToAttrs (builtins.map mkVirtHost domains); - - domains = import ./hosts.nix {}; -in { - imports = [ - ./module.nix - ]; - - options.vhack.nix-sync = { - enable = lib.mkEnableOption '' - a website git ops solution. - ''; - }; - - config = lib.mkIf cfg.enable { - services.nix-sync = { - enable = true; - repositories = nixSyncRepositories; - }; - - vhack.nginx.enable = true; - services.nginx.virtualHosts = virtHosts; - }; -} diff --git a/modules/nixos/vhack/nix-sync/hosts.nix b/modules/nixos/vhack/nix-sync/hosts.nix deleted file mode 100644 index 98dbbf1..0000000 --- a/modules/nixos/vhack/nix-sync/hosts.nix +++ /dev/null @@ -1,48 +0,0 @@ -{...}: let - extraWkdSettings = { - locations."/.well-known/openpgpkey/hu/".extraConfig = '' - default_type application/octet-stream; - - # Came from: https://www.uriports.com/blog/setting-up-openpgp-web-key-directory/ - # No idea if it is actually necessary - # add_header Access-Control-Allow-Origin * always; - ''; - }; -in [ - { - domain = "vhack.eu"; - url = "https://codeberg.org/vhack.eu/website.git"; - } - { - domain = "b-peetz.de"; - url = "https://codeberg.org/bpeetz/b-peetz.de.git"; - } - - # Trinitrix - { - domain = "trinitrix.vhack.eu"; - url = "https://codeberg.org/trinitrix/website.git"; - } - - # WKD - { - domain = "openpgpkey.b-peetz.de"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } - { - domain = "openpgpkey.s-schoeffel.de"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } - { - domain = "openpgpkey.sils.li"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } - { - domain = "openpgpkey.vhack.eu"; - url = "https://codeberg.org/vhack.eu/gpg_wkd.git"; - extraSettings = extraWkdSettings; - } -] diff --git a/modules/nixos/vhack/nix-sync/module.nix b/modules/nixos/vhack/nix-sync/module.nix deleted file mode 100644 index a3ab0af..0000000 --- a/modules/nixos/vhack/nix-sync/module.nix +++ /dev/null @@ -1,299 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - cfg = config.services.nix-sync; - esa = lib.strings.escapeShellArg; - - mkTimer = name: repo: { - description = "Nix sync ${name} timer"; - wantedBy = ["timers.target"]; - timerConfig = { - OnUnitActiveSec = repo.interval; - }; - wants = ["network-online.target"]; - after = ["network-online.target"]; - }; - - parents = path: let - split_path = builtins.split "/" path; - filename = builtins.elemAt split_path (builtins.length split_path - 1); - path_build = - lib.strings.removeSuffix "/" (builtins.replaceStrings [filename] [""] path); - final_path = - if filename == "" - then parents path_build - else path_build; - in - final_path; - - mkUnit = name: repo: let - optionalPathSeparator = - if lib.strings.hasPrefix "/" repo.path - then "" - else "/"; - /* - * `ln` tries to create a symlink in the directory, if the target ends with a '/', - * thus remove it. - */ - repoPath = lib.strings.removeSuffix "/" repo.path; - - repoCachePath = cfg.cachePath + optionalPathSeparator + repo.path; - execStartScript = pkgs.writeScript "nix-sync-exec" '' - #! /usr/bin/env dash - export XDG_CACHE_HOME="$CACHE_DIRECTORY"; - cd ${esa repoCachePath}; - - git fetch - origin="$(git rev-parse @{u})"; - branch="$(git rev-parse @)"; - - if ! [ "$origin" = "$branch" ]; then - git pull --rebase; - - out_paths=$(mktemp); - nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; - [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) - out_path="$(cat "$out_paths")"; - rm ${esa repoPath}; - ln -s "$out_path" ${esa repoPath}; - rm "$out_paths"; - fi - ''; - execStartPreScript = '' - export XDG_CACHE_HOME="$CACHE_DIRECTORY"; - - if ! [ -d ${esa repoCachePath}/.git ]; then - mkdir --parents ${esa repoCachePath}; - git clone ${esa repo.uri} ${esa repoCachePath}; - - out_paths=$(mktemp); - nix build ${esa repoCachePath} --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; - [ "$(wc -l < "$out_paths")" -gt 1 ] && (echo "To many out-paths"; exit 1) - out_path="$(cat "$out_paths")"; - ln -s "$out_path" ${esa repoPath}; - rm "$out_paths"; - fi - - if ! [ -L ${esa repoPath} ]; then - cd ${esa repoCachePath}; - - git pull --rebase; - - out_paths=$(mktemp); - nix build . --print-out-paths --experimental-features 'nix-command flakes' > "$out_paths"; - [ "$(wc -l < "$out_paths")" -gt 1 ] && { echo "To many out-paths"; exit 1; } - out_path="$(cat "$out_paths")"; - - if [ -d ${esa repoPath} ]; then - rm -d ${esa repoPath}; - else - mkdir --parents "$(dirname ${esa repoPath})"; - fi - [ -e ${esa repoPath} ] && rm ${esa repoPath}; - - ln -s "$out_path" ${esa repoPath}; - rm "$out_paths"; - fi - ''; - in { - description = "Nix Sync ${name}"; - wantedBy = ["default.target"]; - after = ["network.target"]; - path = with pkgs; [openssh git nix mktemp coreutils dash]; - preStart = execStartPreScript; - - serviceConfig = { - TimeoutSec = 0; - ExecStart = execStartScript; - Restart = "on-abort"; - # User and group - User = cfg.user; - Group = cfg.group; - # Runtime directory and mode - RuntimeDirectory = "nix-sync"; - RuntimeDirectoryMode = "0750"; - # Cache directory and mode - CacheDirectory = "nix-sync"; - CacheDirectoryMode = "0750"; - # Logs directory and mode - LogsDirectory = "nix-sync"; - LogsDirectoryMode = "0750"; - # Proc filesystem - ProcSubset = "all"; - ProtectProc = "invisible"; - # New file permissions - UMask = "0027"; # 0640 / 0750 - # Capabilities - AmbientCapabilities = ["CAP_CHOWN"]; - CapabilityBoundingSet = ["CAP_CHOWN"]; - # Security - NoNewPrivileges = true; - # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html) - ReadWritePaths = ["${esa (parents repo.path)}" "-${esa (parents repoCachePath)}" "-${esa cfg.cachePath}"]; - ReadOnlyPaths = ["/nix"]; # TODO: Should be irrelevant, as we have ProtectSystem=Strict <2024-06-01> - ProtectSystem = "strict"; - ProtectHome = true; - PrivateTmp = true; - PrivateDevices = true; - ProtectHostname = true; - ProtectClock = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectKernelLogs = true; - ProtectControlGroups = true; - RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; - RestrictNamespaces = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - RemoveIPC = true; - PrivateMounts = true; - # System Call Filtering - SystemCallArchitectures = "native"; - SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"]; - }; - }; - - services = - lib.mapAttrs' (name: repo: { - name = "nix-sync-${name}"; - value = mkUnit name repo; - }) - cfg.repositories; - timers = - lib.mapAttrs' (name: repo: { - name = "nix-sync-${name}"; - value = mkTimer name repo; - }) - cfg.repositories; - - # generate the websites directory, so systemd can mount it read write - generatedDirectories = - lib.mapAttrsToList ( - _: repo: "d ${esa (parents repo.path)} 0755 ${cfg.user} ${cfg.group}" - ) - cfg.repositories; - - repositoryType = lib.types.submodule ({name, ...}: { - options = { - name = lib.mkOption { - internal = true; - default = name; - type = lib.types.str; - description = "The name that should be given to this unit."; - }; - - path = lib.mkOption { - type = lib.types.str; - description = "The path at which to sync the repository"; - }; - - uri = lib.mkOption { - type = lib.types.str; - example = "ssh://user@example.com:/~[user]/path/to/repo.git"; - description = '' - The URI of the remote to be synchronized. This is only used in the - event that the directory does not already exist. See - - for the supported URIs. - ''; - }; - - extraSettings = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - example = lib.literalExpression '' - { - locations."/.well-known/openpgpkey/hu/" = { - extraConfig = \'\' - default_type application/octet-stream; - - add_header Access-Control-Allow-Origin * always; - \'\'; - }; - } - ''; - description = '' - Extra config to add the the nginx virtual host. - ''; - }; - - interval = lib.mkOption { - type = lib.types.int; - default = 500; - description = '' - The interval, specified in seconds, at which the synchronization will - be triggered. - ''; - }; - }; - }); -in { - options = { - services.nix-sync = { - enable = lib.mkEnableOption "nix-sync services"; - - user = lib.mkOption { - type = lib.types.str; - default = "nix-sync"; - description = lib.mdDoc "User account under which nix-sync units runs."; - }; - - group = lib.mkOption { - type = lib.types.str; - default = "nix-sync"; - description = lib.mdDoc "Group account under which nix-sync units runs."; - }; - - cachePath = lib.mkOption { - type = lib.types.str; - default = "/var/lib/nix-sync"; - description = lib.mdDoc '' - Where to cache git directories. Should not end with a slash ("/") - ''; - }; - - repositories = lib.mkOption { - type = with lib.types; attrsOf repositoryType; - description = '' - The repositories that should be synchronized. - ''; - }; - }; - }; - - config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = !lib.strings.hasSuffix "/" cfg.cachePath; - message = "Your cachePath ('${cfg.cachePath}') ends with a slash ('/'), please use: '${lib.strings.removeSuffix "/" cfg.cachePath}'."; - } - ]; - systemd = { - tmpfiles.rules = - generatedDirectories; - - inherit services timers; - }; - users.users = - if cfg.user == "nix-sync" - then { - nix-sync = { - group = "${cfg.group}"; - isSystemUser = true; - }; - } - else lib.warnIf (cfg.user != "nix-sync") "The user (${cfg.user}) is not \"nix-sync\", thus you are responible for generating it."; - users.groups = - if cfg.group == "nix-sync" - then { - nix-sync = { - members = ["${cfg.user}"]; - }; - } - else lib.warnIf (cfg.group != "nix-sync") "The group (${cfg.group}) is not \"nix-sync\", thus you are responible for generating it."; - }; -} diff --git a/modules/nixos/vhack/openssh/default.nix b/modules/nixos/vhack/openssh/default.nix deleted file mode 100644 index 30d16a6..0000000 --- a/modules/nixos/vhack/openssh/default.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ - config, - lib, - ... -}: let - cfg = config.vhack.openssh; -in { - options.vhack.openssh = { - enable = lib.mkEnableOption '' - a sane openssh implementation. - ''; - }; - - config = lib.mkIf cfg.enable { - services.openssh = { - enable = true; - settings.PasswordAuthentication = false; - hostKeys = [ - { - # See the explanation for this in /system/impermanence/mods/openssh.nix - # path = "/var/lib/sshd/ssh_host_ed25519_key"; - - # FIXME: Remove this workaround - path = "/srv/var/lib/sshd/ssh_host_ed25519_key"; - rounds = 1000; - type = "ed25519"; - } - ]; - }; - }; -} diff --git a/modules/nixos/vhack/peertube/default.nix b/modules/nixos/vhack/peertube/default.nix deleted file mode 100644 index 29d1d07..0000000 --- a/modules/nixos/vhack/peertube/default.nix +++ /dev/null @@ -1,113 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - cfg = config.vhack.peertube; -in { - options.vhack.peertube = { - enable = lib.mkEnableOption '' - the peertube video platform. - ''; - }; - - config = lib.mkIf cfg.enable { - services.peertube = { - enable = true; - - configureNginx = true; - localDomain = "peertube.vhack.eu"; - enableWebHttps = true; - listenWeb = 443; - - smtp = { - createLocally = true; - passwordFile = "${config.age.secrets.peertubeSmtp.path}"; - }; - database = { - createLocally = true; - }; - redis = { - enableUnixSocket = true; - createLocally = true; - }; - - secrets.secretsFile = "${config.age.secrets.peertubeGeneral.path}"; - - settings = { - signup = { - enabled = true; - - limit = 10; # When the limit is reached, registrations are disabled. -1 == unlimited - - minimum_age = 18; # Used to configure the signup form - - # Users fill a form to register so moderators can accept/reject the registration - requires_approval = true; - requires_email_verification = true; - }; - user = { - video_quota = "10GB"; - video_quota_daily = "2GB"; - }; - auto_blacklist = { - videos = { - of_users = { - enabled = true; - }; - }; - }; - listen.hostname = "127.0.0.1"; - instance.name = "PeerTube at Vhack.eu"; - - admin.email = "admin@vhack.eu"; - - smtp = let - emailAddress = "peertube@vhack.eu"; - in { - sendmail = "${pkgs.postfix}/bin/sendmail"; - - transport = "sendmail"; - hostname = "server1.vhack.eu"; - port = 587; - username = emailAddress; - tls = true; - disable_starttls = true; - from_address = emailAddress; - }; - }; - }; - - # The `configureNginx` option does not do this for some reason - # TODO(@bpeetz): Find out why <2024-06-27> - services.nginx.virtualHosts."${config.services.peertube.localDomain}" = { - enableACME = true; - forceSSL = true; - }; - - age.secrets = { - peertubeGeneral = { - file = ./secrets/general.age; - mode = "700"; - owner = "peertube"; - group = "peertube"; - }; - peertubeSmtp = { - file = ./secrets/smtp.age; - mode = "700"; - owner = "peertube"; - group = "peertube"; - }; - }; - - environment.persistence."/srv".directories = [ - { - directory = "/var/lib/peertube"; - user = "peertube"; - group = "peertube"; - mode = "0700"; - } - ]; - }; -} diff --git a/modules/nixos/vhack/peertube/secrets/general.age b/modules/nixos/vhack/peertube/secrets/general.age deleted file mode 100644 index 854ab1a..0000000 --- a/modules/nixos/vhack/peertube/secrets/general.age +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlNjR4TDVUZmY2Y0hYT2hk -YmtPcFIxSXplNWF4M0V1Kzh2b2VoSTFCK0dzCmpwT2tDa3FpR082V2pyelBoS05o -RmlWRVdNdVhZbkRVUEVnaDlPdlN1bDAKLT4gWDI1NTE5IFlvaTFPc2JHcWczbEJy -eVZDS2NaUzBvbnpadk5ySVFxRTlNVXhrd2N0a3MKanJ0NEZWaTg3dE5Cbm9uNHNS -ZCs2dmU4RkFZOHNyNlJKa0cyd2VqSlFPQQotPiBzc2gtZWQyNTUxOSBPRDhUNGcg -NXhFSHdWUk1sbEUyb3FTdGpIaHlyTUJlMnlzNXBEY2lzTXpuM09WVDBrOApmM05W -d1VBSGlhMmlDYlhZS1hSdlJBUVkrVWs0bTJseS9BUmZGY1l5K0NBCi0+IEQkNi1l -LWdyZWFzZSAhIUlaOnNsZCAsUVRVKiBfRig2KGg+NSA6CmI0Q0N0cmlFbnNGSFZQ -WThEV0RHS0V2NTVaZnIyK2tUQXZTOHdsRkhyRlExdCtOeHRML2hFNDNxd08xQjlG -V3oKMThoQnF4Y3FDU3hMZjhwRUNvVWRRR3I4c1k5QnhJS1dRR2dod0EKLS0tIEZT -dHhnVXdHV3QzYThXWFJQL2szeTZ4SWM4czZYQWxJOFFIVjBZSnJ0K00KH8WdXv68 -rjAqo5RoWu91aVg5Bl2HKuiFbaGcnlkiMPZ9wGfpq4mpCc/yc4NTa6HhkaI5tA61 -PjKurnkiLXywcdyUTPuaykk+wANynLucbwfq/Mv3aLcG01soh+dFNKZV/g== ------END AGE ENCRYPTED FILE----- diff --git a/modules/nixos/vhack/peertube/secrets/smtp.age b/modules/nixos/vhack/peertube/secrets/smtp.age deleted file mode 100644 index 1979ea7..0000000 --- a/modules/nixos/vhack/peertube/secrets/smtp.age +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtU05NMDN0Q2MrVGEraHpH -Tkwyd0NuVEQwcjljd0NUNUpsSER0V0RldWxNCjlnRHZWNmprVDYxQm90Q3pFVHR5 -enJyUTZhSVdUL1I1aC9Ya2NkMElQaFUKLT4gWDI1NTE5IHprWjRDZVlMK3Rmd1A5 -K0pZRVBIYldsOW0wQXp4SmJzM1pXdzAvZVpiWEUKd1cwR2ZNZTh6WXhQNGZBVmdN -VWpxZGxPZXJBT1dqUFd1aU4xaHAxckZLcwotPiBzc2gtZWQyNTUxOSBPRDhUNGcg -akdaS3I3VHplOENIZDg5TSt2SmRCSGpjaUZoUHVYTFJRR2wzc1RHYWNnVQpFN1Ew -MTZDNGNyKzB1aEdTMHpKaWlFLzE0blJpZ3RhOTZReTNucUp6SEdZCi0+IHloQSUq -LWdyZWFzZQo5VitXYjNxck5FbnkwYlBvUyt6R2ROVG9JOWtQNGJma1ZYd29oVlFx -blFzSytWNDA4d3lqWE9JTUVreCs2Wi92ClZCdFgwYmRmc1VsU0NhTVR4b2dtZkpK -ZTU2M24zVjd0UTRrelFXYnFEZwotLS0gT2ZlRGJsZWNPcEwxK2drdDhVSndDV3Fj -SENsN2piWWEzSFI2OW8xbk12cwrFU4dzHxb5M3miGDpWLh3XbwzsrqWlFWLLu0Ht -SDvqJGrwAPsnVn4YLSG42q1BodYfcQVvVwqRCVbkubEUDcecDTdaYDvjaS3tmDZW -u5Nabp1ujYuIewOEZ8w41napS0C553qq0mL5sYZH1C23ViW81va4X1XOJTCnmbz6 -lbh+lK8ZbZz3cer49nR8OHTtpjA9hrf4Pf/W2nMR+0exy4zDYw== ------END AGE ENCRYPTED FILE----- diff --git a/secrets.nix b/secrets.nix index 3f22872..bd5630e 100644 --- a/secrets.nix +++ b/secrets.nix @@ -10,9 +10,10 @@ let server1 ]; in { - "./modules/nixos/vhack/etesync/secret_file.age".publicKeys = allSecrets; - "./modules/nixos/vhack/peertube/secrets/general.age".publicKeys = allSecrets; - "./modules/nixos/vhack/peertube/secrets/smtp.age".publicKeys = allSecrets; + "./modules/by-name/et/etesync/secret_file.age".publicKeys = allSecrets; + "./modules/by-name/pe/peertube/secrets/general.age".publicKeys = allSecrets; + "./modules/by-name/pe/peertube/secrets/smtp.age".publicKeys = allSecrets; + "./system/secrets/backup/backuppass.age".publicKeys = allSecrets; "./system/secrets/backup/backupssh.age".publicKeys = allSecrets; "./system/secrets/invidious/hmac.age".publicKeys = allSecrets; diff --git a/tests.nix b/tests.nix deleted file mode 100644 index d91a9c9..0000000 --- a/tests.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - pkgs, - lib, - specialArgs, -}: let - # for `nix eval --file` (as it does not support args) use: - # ``` - # specialArgs = {}; - # pkgs = (builtins.getFlake "nixpkgs").legacyPackages."x86_64-linux"; - # inherit (pkgs) lib; - # ``` - # instead of the function arguments above. - importTests' = test: let - basename = builtins.baseNameOf test; - testName = builtins.baseNameOf (lib.strings.removeSuffix "/${basename}" "${builtins.toString test}"); - in { - name = "${testName}"; - value = pkgs.callPackage test specialArgs; - }; - - importTests = dir: - builtins.listToAttrs (builtins.map importTests' ( - lib.fileset.toList (lib.fileset.fileFilter (file: file.name == "test.nix") dir) - )); - - tests = importTests ./tests; -in - tests diff --git a/tests/README.md b/tests/README.md index 2613f4a..0ff5738 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,4 +3,4 @@ This directory tree mirrors the modules defined in the [modules](`../modules/`) directory. Each module should have at least one test in the mirrored directory, effectively replacing the module's -`default.nix` file. +`module.nix` file. diff --git a/tests/by-name/gi/git-server/ssh_keys.nix b/tests/by-name/gi/git-server/ssh_keys.nix new file mode 100644 index 0000000..07f0b88 --- /dev/null +++ b/tests/by-name/gi/git-server/ssh_keys.nix @@ -0,0 +1,49 @@ +{pkgs}: { + admin = { + priv = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACDu7qxYQAPdAU6RrhB3llk2N1v4PTwcVzcX1oX265uC3gAAAJBJiYxDSYmM + QwAAAAtzc2gtZWQyNTUxOQAAACDu7qxYQAPdAU6RrhB3llk2N1v4PTwcVzcX1oX265uC3g + AAAEDE1W6vMwSEUcF1r7Hyypm/+sCOoDmKZgPxi3WOa1mD2u7urFhAA90BTpGuEHeWWTY3 + W/g9PBxXNxfWhfbrm4LeAAAACGJmb0BtaW5pAQIDBAU= + -----END OPENSSH PRIVATE KEY----- + ''; + + pub = '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7urFhAA90BTpGuEHeWWTY3W/g9PBxXNxfWhfbrm4Le root@client + ''; + }; + + alice = { + priv = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACBbeWvHh/AWGWI6EIc1xlSihyXtacNQ9KeztlW/VUy8wQAAAJAwVQ5VMFUO + VQAAAAtzc2gtZWQyNTUxOQAAACBbeWvHh/AWGWI6EIc1xlSihyXtacNQ9KeztlW/VUy8wQ + AAAEB7lbfkkdkJoE+4TKHPdPQWBKLSx+J54Eg8DaTr+3KoSlt5a8eH8BYZYjoQhzXGVKKH + Je1pw1D0p7O2Vb9VTLzBAAAACGJmb0BtaW5pAQIDBAU= + -----END OPENSSH PRIVATE KEY----- + ''; + + pub = pkgs.writeText "id_ed25519.pub" '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFt5a8eH8BYZYjoQhzXGVKKHJe1pw1D0p7O2Vb9VTLzB alice@client + ''; + }; + + bob = { + priv = pkgs.writeText "id_ed25519" '' + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW + QyNTUxOQAAACCWTaJ1D9Xjxy6759FvQ9oXTes1lmWBciXPkEeqTikBMAAAAJDQBmNV0AZj + VQAAAAtzc2gtZWQyNTUxOQAAACCWTaJ1D9Xjxy6759FvQ9oXTes1lmWBciXPkEeqTikBMA + AAAEDM1IYYFUwk/IVxauha9kuR6bbRtT3gZ6ZA0GLb9txb/pZNonUP1ePHLrvn0W9D2hdN + 6zWWZYFyJc+QR6pOKQEwAAAACGJmb0BtaW5pAQIDBAU= + -----END OPENSSH PRIVATE KEY----- + ''; + + pub = pkgs.writeText "id_ed25519.pub" '' + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJZNonUP1ePHLrvn0W9D2hdN6zWWZYFyJc+QR6pOKQEw bob@client + ''; + }; +} diff --git a/tests/by-name/gi/git-server/test.nix b/tests/by-name/gi/git-server/test.nix new file mode 100644 index 0000000..0cf3ee8 --- /dev/null +++ b/tests/by-name/gi/git-server/test.nix @@ -0,0 +1,245 @@ +{ + nixos-lib, + pkgsUnstable, + nixpkgs-unstable, + pkgs, + extraModules, + nixLib, + ... +}: let + sshKeys = + import ./ssh_keys.nix {inherit pkgs;}; + + gitServerDomain = "server"; + + gitoliteAdminConfSnippet = pkgs.writeText "gitolite-admin-conf-snippet" '' + repo CREATOR/[a-zA-Z0-9].* + C = @all + RW+ = CREATOR + RW = WRITERS + R = READERS + option user-configs = cgit\.owner cgit\.desc cgit\.section cgit\.homepage + ''; + + expectedGitoliteConf = pkgs.writeText "expected-gitolite-conf" '' + repo gitolite-admin + RW+ = gitolite-admin + + repo testing + RW+ = @all + repo CREATOR/[a-zA-Z0-9].* + C = @all + RW+ = CREATOR + RW = WRITERS + R = READERS + option user-configs = cgit\.owner cgit\.desc cgit\.section cgit\.homepage + ''; + + expectedHtmlReadme = pkgs.writeText "expectedHtmlReadme" '' +

Alice's Repo

+ ''; + expectedMdReadme = pkgs.writeText "expectedMdReadme" '' + # Alice's Repo + ''; +in + nixos-lib.runTest { + hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs + + name = "git-server"; + + node = { + specialArgs = {inherit pkgsUnstable nixpkgs-unstable nixLib;}; + + # Use the nixpkgs as constructed by the `nixpkgs.*` options + pkgs = null; + }; + + nodes = { + server = {config, ...}: { + imports = + extraModules + ++ [ + ../../../../modules + ]; + + system.activationScripts = { + gitolite = { + text = '' + if ! [ -d /srv/gitolite ]; then + mkdir --parents /srv/gitolite + chown -R git:git /srv/gitolite + fi + ''; + }; + }; + + vhack = { + openssh.enable = true; + nginx = { + enable = true; + selfsign = true; + }; + git-server = { + enable = true; + domain = gitServerDomain; + gitolite.adminPubkey = sshKeys.admin.pub; + }; + }; + }; + + client = {...}: { + environment.systemPackages = [pkgs.git]; + programs.ssh.extraConfig = '' + Host * + UserKnownHostsFile /dev/null + StrictHostKeyChecking no + # there's nobody around that can input password + PreferredAuthentications publickey + ''; + users.users.alice = {isNormalUser = true;}; + users.users.bob = {isNormalUser = true;}; + }; + }; + + testScript = {nodes, ...}: + /* + python + */ + '' + start_all() + + with subtest("can setup ssh keys on client"): + client.succeed( + "mkdir -p ~root/.ssh", + "cp ${sshKeys.admin.priv} ~root/.ssh/id_ed25519", + "chmod 600 ~root/.ssh/id_ed25519", + ) + client.succeed( + "sudo -u alice mkdir -p ~alice/.ssh", + "sudo -u alice cp ${sshKeys.alice.priv} ~alice/.ssh/id_ed25519", + "sudo -u alice chmod 600 ~alice/.ssh/id_ed25519", + ) + client.succeed( + "sudo -u bob mkdir -p ~bob/.ssh", + "sudo -u bob cp ${sshKeys.bob.priv} ~bob/.ssh/id_ed25519", + "sudo -u bob chmod 600 ~bob/.ssh/id_ed25519", + ) + + with subtest("gitolite server starts"): + server.wait_for_unit("gitolite-init.service") + server.wait_for_unit("sshd.service") + client.succeed("ssh -n git@server info") + + + with subtest("admin can clone and configure gitolite-admin.git"): + client.succeed("${pkgs.writeShellScript "setup-gitolite-admin.git" '' + set -xe + + git clone git@server:gitolite-admin.git + git config --global user.name 'System Administrator' + git config --global user.email root\@domain.example + + cp ${sshKeys.alice.pub} gitolite-admin/keydir/alice.pub + cp ${sshKeys.bob.pub} gitolite-admin/keydir/bob.pub + + (cd gitolite-admin && git switch -c master && git branch -D main) + + (cd gitolite-admin && git add . && git commit -m 'Add keys for alice, bob' && git push -u origin master) + cat ${gitoliteAdminConfSnippet} >> gitolite-admin/conf/gitolite.conf + (cd gitolite-admin && git add . && git commit -m 'Add support for wild repos' && git push) + (cd gitolite-admin && git push -d origin main) + ''}") + + server.succeed("${pkgs.writeShellScript "verify gitolite-admin.conf" '' + set -xe + + testFile=~git/.gitolite/conf/gitolite.conf.test + + cp ~git/.gitolite/conf/gitolite.conf "$testFile" + + # Normalize the white space + sed -i 's/\t/ /g' "$testFile" + sed -i 's/\s\+/ /g' "$testFile" + + diff "$testFile" ${expectedGitoliteConf} + ''}") + + + with subtest("non-admins cannot clone gitolite-admin.git"): + client.fail("sudo -i -u alice git clone git@server:gitolite-admin.git") + client.fail("sudo -i -u bob git clone git@server:gitolite-admin.git") + + with subtest("non-admins can clone testing.git"): + client.succeed("sudo -i -u alice git clone git@server:testing.git") + client.succeed("sudo -i -u bob git clone git@server:testing.git") + + + with subtest("alice can create a repo"): + client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-create-repo" '' + set -xe + + mkdir alice-repo && cd alice-repo; + + git init --initial-branch main + echo "# Alice's Repo" > README.md + git add README.md + git -c user.name=Alice -c user.email=alice@domain.example commit -m 'Add readme' + + git remote add origin git@server:alice/alice-project.git + git push --set-upstream origin main + ''}") + + with subtest("alice can clone alice-project.git"): + client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-clone-repo" '' + set -xe + + git clone git@server:alice/alice-project.git + diff --side-by-side ${expectedMdReadme} ./alice-project/README.md + ''}") + + with subtest("bob cannot clone alice-project.git"): + client.fail("sudo -i -u bob git clone git@server:alice/alice-project.git") + + with subtest("Alice can make her repo public"): + client.succeed( + "sudo -u alice ssh git@server perms alice/alice-project + READERS @all", + "sudo -u alice ssh git@server desc alice/alice-project 'My nice project.'" + ) + + with subtest("Bob can see alice config on cgit"): + client.succeed("sudo -u bob ${pkgs.writeShellScript "bob-clone-repo" '' + set -xe + + cd ~bob + # Disable ssl verification, as the certs are self-signed + git -c http.sslVerify=false clone https://server/alice/alice-project.git + ''}") + + with subtest("Alice can change settings in her repo"): + client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-change-settings" '' + set -xe + + echo 'Hi! You want to work with alice' | ssh git@server motd alice/alice-project set + ssh git@server config alice/alice-project --add 'cgit.owner' 'alice' + ssh git@server config alice/alice-project --add 'cgit.section' 'alice' + ssh git@server config alice/alice-project --add 'cgit.homepage' 'alice' + + owner="$(ssh git@server config alice/alice-project --get-all 'cgit.owner')" + [ "$owner" = "alice" ] || { + echo "owner should be alice but is '$owner'!" + exit 1 + } + ''}") + + + # He can't see the readme (FIXME: find out why this does not work. <2024-08-13> ) + # with subtest("Bob can see alice's README"): + # client.succeed("sudo -u bob ${pkgs.writeShellScript "bob-alice-readme" '' + # set -xe + # + # curl --insecure --silent --fail --show-error 'https://server/alice/alice-project/about' > readme.html + # cat readme.html + # diff --side-by-side ${expectedHtmlReadme} readme.html + # ''}") + ''; + } diff --git a/tests/default.nix b/tests/default.nix new file mode 100644 index 0000000..d9b354a --- /dev/null +++ b/tests/default.nix @@ -0,0 +1,13 @@ +{ + specialArgs, + nixLib, + pkgs, +}: let + tests = nixLib.mkByName { + baseDirectory = ./by-name; + fileName = "test.nix"; + finalizeFunction = name: value: + import value (nixLib.warnMerge specialArgs {inherit pkgs;} "the test args set"); + }; +in + tests diff --git a/tests/nixos/vhack/git-server/ssh_keys.nix b/tests/nixos/vhack/git-server/ssh_keys.nix deleted file mode 100644 index 07f0b88..0000000 --- a/tests/nixos/vhack/git-server/ssh_keys.nix +++ /dev/null @@ -1,49 +0,0 @@ -{pkgs}: { - admin = { - priv = pkgs.writeText "id_ed25519" '' - -----BEGIN OPENSSH PRIVATE KEY----- - b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW - QyNTUxOQAAACDu7qxYQAPdAU6RrhB3llk2N1v4PTwcVzcX1oX265uC3gAAAJBJiYxDSYmM - QwAAAAtzc2gtZWQyNTUxOQAAACDu7qxYQAPdAU6RrhB3llk2N1v4PTwcVzcX1oX265uC3g - AAAEDE1W6vMwSEUcF1r7Hyypm/+sCOoDmKZgPxi3WOa1mD2u7urFhAA90BTpGuEHeWWTY3 - W/g9PBxXNxfWhfbrm4LeAAAACGJmb0BtaW5pAQIDBAU= - -----END OPENSSH PRIVATE KEY----- - ''; - - pub = '' - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7urFhAA90BTpGuEHeWWTY3W/g9PBxXNxfWhfbrm4Le root@client - ''; - }; - - alice = { - priv = pkgs.writeText "id_ed25519" '' - -----BEGIN OPENSSH PRIVATE KEY----- - b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW - QyNTUxOQAAACBbeWvHh/AWGWI6EIc1xlSihyXtacNQ9KeztlW/VUy8wQAAAJAwVQ5VMFUO - VQAAAAtzc2gtZWQyNTUxOQAAACBbeWvHh/AWGWI6EIc1xlSihyXtacNQ9KeztlW/VUy8wQ - AAAEB7lbfkkdkJoE+4TKHPdPQWBKLSx+J54Eg8DaTr+3KoSlt5a8eH8BYZYjoQhzXGVKKH - Je1pw1D0p7O2Vb9VTLzBAAAACGJmb0BtaW5pAQIDBAU= - -----END OPENSSH PRIVATE KEY----- - ''; - - pub = pkgs.writeText "id_ed25519.pub" '' - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFt5a8eH8BYZYjoQhzXGVKKHJe1pw1D0p7O2Vb9VTLzB alice@client - ''; - }; - - bob = { - priv = pkgs.writeText "id_ed25519" '' - -----BEGIN OPENSSH PRIVATE KEY----- - b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW - QyNTUxOQAAACCWTaJ1D9Xjxy6759FvQ9oXTes1lmWBciXPkEeqTikBMAAAAJDQBmNV0AZj - VQAAAAtzc2gtZWQyNTUxOQAAACCWTaJ1D9Xjxy6759FvQ9oXTes1lmWBciXPkEeqTikBMA - AAAEDM1IYYFUwk/IVxauha9kuR6bbRtT3gZ6ZA0GLb9txb/pZNonUP1ePHLrvn0W9D2hdN - 6zWWZYFyJc+QR6pOKQEwAAAACGJmb0BtaW5pAQIDBAU= - -----END OPENSSH PRIVATE KEY----- - ''; - - pub = pkgs.writeText "id_ed25519.pub" '' - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJZNonUP1ePHLrvn0W9D2hdN6zWWZYFyJc+QR6pOKQEw bob@client - ''; - }; -} diff --git a/tests/nixos/vhack/git-server/test.nix b/tests/nixos/vhack/git-server/test.nix deleted file mode 100644 index 6d5edda..0000000 --- a/tests/nixos/vhack/git-server/test.nix +++ /dev/null @@ -1,244 +0,0 @@ -{ - nixos-lib, - pkgsUnstable, - nixpkgs-unstable, - pkgs, - extraModules, - ... -}: let - sshKeys = - import ./ssh_keys.nix {inherit pkgs;}; - - gitServerDomain = "server"; - - gitoliteAdminConfSnippet = pkgs.writeText "gitolite-admin-conf-snippet" '' - repo CREATOR/[a-zA-Z0-9].* - C = @all - RW+ = CREATOR - RW = WRITERS - R = READERS - option user-configs = cgit\.owner cgit\.desc cgit\.section cgit\.homepage - ''; - - expectedGitoliteConf = pkgs.writeText "expected-gitolite-conf" '' - repo gitolite-admin - RW+ = gitolite-admin - - repo testing - RW+ = @all - repo CREATOR/[a-zA-Z0-9].* - C = @all - RW+ = CREATOR - RW = WRITERS - R = READERS - option user-configs = cgit\.owner cgit\.desc cgit\.section cgit\.homepage - ''; - - expectedHtmlReadme = pkgs.writeText "expectedHtmlReadme" '' -

Alice's Repo

- ''; - expectedMdReadme = pkgs.writeText "expectedMdReadme" '' - # Alice's Repo - ''; -in - nixos-lib.runTest { - hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs - - name = "git-server"; - - node = { - specialArgs = {inherit pkgsUnstable nixpkgs-unstable;}; - - # Use the nixpkgs as constructed by the `nixpkgs.*` options - pkgs = null; - }; - - nodes = { - server = {config, ...}: { - imports = - extraModules - ++ [ - ../../../../modules/nixos - ]; - - system.activationScripts = { - gitolite = { - text = '' - if ! [ -d /srv/gitolite ]; then - mkdir --parents /srv/gitolite - chown -R git:git /srv/gitolite - fi - ''; - }; - }; - - vhack = { - openssh.enable = true; - nginx = { - enable = true; - selfsign = true; - }; - git-server = { - enable = true; - domain = gitServerDomain; - gitolite.adminPubkey = sshKeys.admin.pub; - }; - }; - }; - - client = {...}: { - environment.systemPackages = [pkgs.git]; - programs.ssh.extraConfig = '' - Host * - UserKnownHostsFile /dev/null - StrictHostKeyChecking no - # there's nobody around that can input password - PreferredAuthentications publickey - ''; - users.users.alice = {isNormalUser = true;}; - users.users.bob = {isNormalUser = true;}; - }; - }; - - testScript = {nodes, ...}: - /* - python - */ - '' - start_all() - - with subtest("can setup ssh keys on client"): - client.succeed( - "mkdir -p ~root/.ssh", - "cp ${sshKeys.admin.priv} ~root/.ssh/id_ed25519", - "chmod 600 ~root/.ssh/id_ed25519", - ) - client.succeed( - "sudo -u alice mkdir -p ~alice/.ssh", - "sudo -u alice cp ${sshKeys.alice.priv} ~alice/.ssh/id_ed25519", - "sudo -u alice chmod 600 ~alice/.ssh/id_ed25519", - ) - client.succeed( - "sudo -u bob mkdir -p ~bob/.ssh", - "sudo -u bob cp ${sshKeys.bob.priv} ~bob/.ssh/id_ed25519", - "sudo -u bob chmod 600 ~bob/.ssh/id_ed25519", - ) - - with subtest("gitolite server starts"): - server.wait_for_unit("gitolite-init.service") - server.wait_for_unit("sshd.service") - client.succeed("ssh -n git@server info") - - - with subtest("admin can clone and configure gitolite-admin.git"): - client.succeed("${pkgs.writeShellScript "setup-gitolite-admin.git" '' - set -xe - - git clone git@server:gitolite-admin.git - git config --global user.name 'System Administrator' - git config --global user.email root\@domain.example - - cp ${sshKeys.alice.pub} gitolite-admin/keydir/alice.pub - cp ${sshKeys.bob.pub} gitolite-admin/keydir/bob.pub - - (cd gitolite-admin && git switch -c master && git branch -D main) - - (cd gitolite-admin && git add . && git commit -m 'Add keys for alice, bob' && git push -u origin master) - cat ${gitoliteAdminConfSnippet} >> gitolite-admin/conf/gitolite.conf - (cd gitolite-admin && git add . && git commit -m 'Add support for wild repos' && git push) - (cd gitolite-admin && git push -d origin main) - ''}") - - server.succeed("${pkgs.writeShellScript "verify gitolite-admin.conf" '' - set -xe - - testFile=~git/.gitolite/conf/gitolite.conf.test - - cp ~git/.gitolite/conf/gitolite.conf "$testFile" - - # Normalize the white space - sed -i 's/\t/ /g' "$testFile" - sed -i 's/\s\+/ /g' "$testFile" - - diff "$testFile" ${expectedGitoliteConf} - ''}") - - - with subtest("non-admins cannot clone gitolite-admin.git"): - client.fail("sudo -i -u alice git clone git@server:gitolite-admin.git") - client.fail("sudo -i -u bob git clone git@server:gitolite-admin.git") - - with subtest("non-admins can clone testing.git"): - client.succeed("sudo -i -u alice git clone git@server:testing.git") - client.succeed("sudo -i -u bob git clone git@server:testing.git") - - - with subtest("alice can create a repo"): - client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-create-repo" '' - set -xe - - mkdir alice-repo && cd alice-repo; - - git init --initial-branch main - echo "# Alice's Repo" > README.md - git add README.md - git -c user.name=Alice -c user.email=alice@domain.example commit -m 'Add readme' - - git remote add origin git@server:alice/alice-project.git - git push --set-upstream origin main - ''}") - - with subtest("alice can clone alice-project.git"): - client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-clone-repo" '' - set -xe - - git clone git@server:alice/alice-project.git - diff --side-by-side ${expectedMdReadme} ./alice-project/README.md - ''}") - - with subtest("bob cannot clone alice-project.git"): - client.fail("sudo -i -u bob git clone git@server:alice/alice-project.git") - - with subtest("Alice can make her repo public"): - client.succeed( - "sudo -u alice ssh git@server perms alice/alice-project + READERS @all", - "sudo -u alice ssh git@server desc alice/alice-project 'My nice project.'" - ) - - with subtest("Bob can see alice config on cgit"): - client.succeed("sudo -u bob ${pkgs.writeShellScript "bob-clone-repo" '' - set -xe - - cd ~bob - # Disable ssl verification, as the certs are self-signed - git -c http.sslVerify=false clone https://server/alice/alice-project.git - ''}") - - with subtest("Alice can change settings in her repo"): - client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-change-settings" '' - set -xe - - echo 'Hi! You want to work with alice' | ssh git@server motd alice/alice-project set - ssh git@server config alice/alice-project --add 'cgit.owner' 'alice' - ssh git@server config alice/alice-project --add 'cgit.section' 'alice' - ssh git@server config alice/alice-project --add 'cgit.homepage' 'alice' - - owner="$(ssh git@server config alice/alice-project --get-all 'cgit.owner')" - [ "$owner" = "alice" ] || { - echo "owner should be alice but is '$owner'!" - exit 1 - } - ''}") - - - # He can't see the readme (FIXME: find out why this does not work. <2024-08-13> ) - # with subtest("Bob can see alice's README"): - # client.succeed("sudo -u bob ${pkgs.writeShellScript "bob-alice-readme" '' - # set -xe - # - # curl --insecure --silent --fail --show-error 'https://server/alice/alice-project/about' > readme.html - # cat readme.html - # diff --side-by-side ${expectedHtmlReadme} readme.html - # ''}") - ''; - } -- cgit 1.4.1