at 24.11-pre 5.4 kB view raw
1{ config, lib, pkgs, ... }: 2 3let 4 inherit (lib) 5 literalExpression 6 mkEnableOption 7 mkIf 8 mkOption 9 mkPackageOption 10 optionalAttrs 11 optional 12 types; 13 14 cfg = config.services.legit; 15 16 yaml = pkgs.formats.yaml { }; 17 configFile = yaml.generate "legit.yaml" cfg.settings; 18 19 defaultStateDir = "/var/lib/legit"; 20 defaultStaticDir = "${cfg.settings.repo.scanPath}/static"; 21 defaultTemplatesDir = "${cfg.settings.repo.scanPath}/templates"; 22in 23{ 24 options.services.legit = { 25 enable = mkEnableOption "legit git web frontend"; 26 27 package = mkPackageOption pkgs "legit-web" { }; 28 29 user = mkOption { 30 type = types.str; 31 default = "legit"; 32 description = "User account under which legit runs."; 33 }; 34 35 group = mkOption { 36 type = types.str; 37 default = "legit"; 38 description = "Group account under which legit runs."; 39 }; 40 41 settings = mkOption { 42 default = { }; 43 description = '' 44 The primary legit configuration. See the 45 [sample configuration](https://github.com/icyphox/legit/blob/master/config.yaml) 46 for possible values. 47 ''; 48 type = types.submodule { 49 options.repo = { 50 scanPath = mkOption { 51 type = types.path; 52 default = defaultStateDir; 53 description = "Directory where legit will scan for repositories."; 54 }; 55 readme = mkOption { 56 type = types.listOf types.str; 57 default = [ ]; 58 description = "Readme files to look for."; 59 }; 60 mainBranch = mkOption { 61 type = types.listOf types.str; 62 default = [ "main" "master" ]; 63 description = "Main branch to look for."; 64 }; 65 ignore = mkOption { 66 type = types.listOf types.str; 67 default = [ ]; 68 description = "Repositories to ignore."; 69 }; 70 }; 71 options.dirs = { 72 templates = mkOption { 73 type = types.path; 74 default = "${pkgs.legit-web}/lib/legit/templates"; 75 defaultText = literalExpression ''"''${pkgs.legit-web}/lib/legit/templates"''; 76 description = "Directories where template files are located."; 77 }; 78 static = mkOption { 79 type = types.path; 80 default = "${pkgs.legit-web}/lib/legit/static"; 81 defaultText = literalExpression ''"''${pkgs.legit-web}/lib/legit/static"''; 82 description = "Directories where static files are located."; 83 }; 84 }; 85 options.meta = { 86 title = mkOption { 87 type = types.str; 88 default = "legit"; 89 description = "Website title."; 90 }; 91 description = mkOption { 92 type = types.str; 93 default = "git frontend"; 94 description = "Website description."; 95 }; 96 }; 97 options.server = { 98 name = mkOption { 99 type = types.str; 100 default = "localhost"; 101 description = "Server name."; 102 }; 103 host = mkOption { 104 type = types.str; 105 default = "127.0.0.1"; 106 description = "Host address."; 107 }; 108 port = mkOption { 109 type = types.port; 110 default = 5555; 111 description = "Legit port."; 112 }; 113 }; 114 }; 115 }; 116 }; 117 118 config = mkIf cfg.enable { 119 users.groups = optionalAttrs (cfg.group == "legit") { 120 "${cfg.group}" = { }; 121 }; 122 123 users.users = optionalAttrs (cfg.user == "legit") { 124 "${cfg.user}" = { 125 group = cfg.group; 126 isSystemUser = true; 127 }; 128 }; 129 130 systemd.services.legit = { 131 description = "legit git frontend"; 132 133 after = [ "network.target" ]; 134 wantedBy = [ "multi-user.target" ]; 135 restartTriggers = [ configFile ]; 136 137 serviceConfig = { 138 Type = "simple"; 139 User = cfg.user; 140 Group = cfg.group; 141 ExecStart = "${cfg.package}/bin/legit -config ${configFile}"; 142 Restart = "always"; 143 144 WorkingDirectory = cfg.settings.repo.scanPath; 145 StateDirectory = [ ] ++ 146 optional (cfg.settings.repo.scanPath == defaultStateDir) "legit" ++ 147 optional (cfg.settings.dirs.static == defaultStaticDir) "legit/static" ++ 148 optional (cfg.settings.dirs.templates == defaultTemplatesDir) "legit/templates"; 149 150 # Hardening 151 CapabilityBoundingSet = [ "" ]; 152 DeviceAllow = [ "" ]; 153 LockPersonality = true; 154 MemoryDenyWriteExecute = true; 155 NoNewPrivileges = true; 156 PrivateDevices = true; 157 PrivateTmp = true; 158 PrivateUsers = true; 159 ProcSubset = "pid"; 160 ProtectClock = true; 161 ProtectControlGroups = true; 162 ProtectHome = true; 163 ProtectHostname = true; 164 ProtectKernelLogs = true; 165 ProtectKernelModules = true; 166 ProtectKernelTunables = true; 167 ProtectProc = "invisible"; 168 ProtectSystem = "strict"; 169 ReadWritePaths = cfg.settings.repo.scanPath; 170 RemoveIPC = true; 171 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 172 RestrictNamespaces = true; 173 RestrictRealtime = true; 174 RestrictSUIDSGID = true; 175 SystemCallArchitectures = "native"; 176 SystemCallFilter = [ "@system-service" "~@privileged" ]; 177 UMask = "0077"; 178 }; 179 }; 180 }; 181}