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