greenhouse

kore

my public infrastructure monorepo

git clone [email protected]:kore.git

feat(nixos/roles): add git server

commit072756556de767559d058588618d263b78cd4d9f
parent73acb928bd8e12bf55b24c20f226180547cf0391
authorlostduk <[email protected]>
date2026-10-14 09:45 +0000
home/features/cli/ssh.nix5+++
kore.json6+++
nixos/features/kore.nix38++++++++++++++++++-
nixos/roles/git.nix82++++++++++++++++++++++++++++++++++++++++

4 files changed, -4/+127

home/features/cli/ssh.nix

@@ -24,6 +24,11 @@         HostName = "github.com";         User = "git";       };++      ld = {+        HostName = "git.lostduk.com";+        User = "git";+      };     };   }; }

kore.json

@@ -16,9 +16,13 @@     "iris": {       "system": "x86_64-linux",       "stateVersion": "26.11",-      "roles": ["web"],+      "roles": ["web", "git"],       "users": ["lostduk"],+      "aliases": ["git"],       "ipv4": "82.153.138.53"     }+  },+  "repos": {+    "kore": { "write": ["lostduk"] }   } }

nixos/features/kore.nix

@@ -50,11 +50,30 @@ let       type = types.listOf types.str;       default = [];     };+    aliases = mkOption {+      type = types.listOf types.str;+      default = [];+    };     ipv4 = mkOption {       type = types.nullOr types.str;       default = null;     };   };++  repoOptions = with lib; {+    write = mkOption {+      type = types.listOf types.str;+      default = [];+    };+    read = mkOption {+      type = types.listOf types.str;+      default = [];+    };+  };++  hostNames = name: host:+    let names = [ name ] ++ host.aliases;+    in map (n: "${n}.${config.networking.domain}") names ++ names; in {   options.kore = with lib; {     identities = mkOption {@@ -68,6 +87,10 @@ in {     currentHost = mkOption {       type = types.submodule [{ options = hostOptions; }];     };+    repos = mkOption {+      type = types.attrsOf (types.submodule [{ options = repoOptions; }]);+      default = {};+    };      impermanence.enable = mkOption {       type = types.bool;@@ -80,12 +103,21 @@ in {       identities = kore.identities or {};       hosts = kore.hosts;       currentHost = kore.hosts.${hostname};+      repos = kore.repos or {};     };      assertions = map (user: {       assertion = cfg.identities ? ${user};       message = "kore: ${hostname} has user ${user}, but kore.json has no identity for it";-    }) cfg.currentHost.users;+    }) cfg.currentHost.users+    ++ lib.concatLists (lib.mapAttrsToList (repo: acl: [{+      assertion = builtins.match "[A-Za-z0-9_][A-Za-z0-9._-]*(/[A-Za-z0-9_][A-Za-z0-9._-]*)*" repo != null+        && !(lib.hasSuffix ".git" repo);+      message = "kore: repo name ${repo} is invalid";+    }] ++ map (id: {+      assertion = cfg.identities ? ${id};+      message = "kore: repo ${repo} lists ${id}, but kore.json has no identity for it";+    }) (acl.write ++ acl.read)) cfg.repos);      nixpkgs.hostPlatform = cfg.currentHost.system;     system.stateVersion = cfg.currentHost.stateVersion;@@ -107,11 +139,11 @@ in {     # /etc/hosts so machines resolve each other by name.     networking.hosts = lib.mkMerge (lib.mapAttrsToList (name: host:       lib.optionalAttrs (host.ipv4 != null) {-        "${host.ipv4}" = [ "${name}.${config.networking.domain}" name ];+        "${host.ipv4}" = hostNames name host;       }) cfg.hosts);      programs.ssh.knownHosts = lib.mapAttrs (name: host: {-      hostNames = [ name "${name}.${config.networking.domain}" ]+      hostNames = hostNames name host         ++ lib.optional (host.ipv4 != null) host.ipv4;       publicKeyFile = hostKey name;     }) (lib.filterAttrs (name: _: builtins.pathExists (hostKey name)) cfg.hosts);

nixos/roles/git.nix

@@ -0,0 +1,82 @@+{ config, lib, pkgs, ... }:++let+  inherit (config.kore) identities repos;+  root = "/srv/git";++  acl = pkgs.writeText "kore-git-acl" (lib.concatLines (lib.concatLists (lib.mapAttrsToList (repo: access:+    map (id: "${repo} write ${id}") access.write+    ++ map (id: "${repo} read ${id}") access.read+  ) repos)));++  kore-git = pkgs.writeShellApplication {+    name = "kore-git";+    runtimeInputs = [ pkgs.git pkgs.gnugrep ];+    text = ''+      id="$1"+      cmd="''${SSH_ORIGINAL_COMMAND:-}"++      deny() {+        echo "kore-git: access denied" >&2+        exit 1+      }++      verb="''${cmd%% *}"+      case "$verb" in+        git-upload-pack | git-upload-archive) perm="read" ;;+        git-receive-pack) perm="write" ;;+        *)+          echo "kore-git: hi $id, shell access is not provided" >&2+          exit 1+          ;;+      esac++      path="''${cmd#"$verb" }"+      path="''${path#\'}"+      path="''${path%\'}"+      path="''${path#/}"+      path="''${path%/}"+      path="''${path%.git}"++      [[ "$path" =~ ^[A-Za-z0-9_][A-Za-z0-9._-]*(/[A-Za-z0-9_][A-Za-z0-9._-]*)*$ ]] || deny++      grep -qFx "$path write $id" ${acl} \+        || { [ "$perm" = read ] && grep -qFx "$path read $id" ${acl}; } \+        || deny++      exec git-shell -c "$verb '${root}/$path.git'"+    '';+  };++  members = lib.unique (lib.concatLists (lib.mapAttrsToList (_: access: access.write ++ access.read) repos));+in {+  users.groups.git = {};+  users.users.git = {+    isSystemUser = true;+    group = "git";+    home = root;+    createHome = true;+    shell = pkgs.bash;++    openssh.authorizedKeys.keys = lib.concatMap (id:+      map (key: ''command="${kore-git}/bin/kore-git ${id}",restrict ${key}'') (identities.${id}.sshKeys or [])+    ) members;+  };++  systemd.services.kore-git-repos = {+    wantedBy = [ "multi-user.target" ];+    after = [ "local-fs.target" ];+    serviceConfig = {+      Type = "oneshot";+      User = "git";+      Group = "git";+    };+    path = [ pkgs.git pkgs.coreutils ];+    script = lib.concatMapStrings (repo: ''+      if [ ! -d '${root}/${repo}.git' ]; then+        mkdir -p "$(dirname '${root}/${repo}.git')"+        git init --quiet --bare --initial-branch=main '${root}/${repo}.git'+      fi+    '') (lib.attrNames repos);+  };+}