at 23.11-pre 25 kB view raw
1{ config, lib, options, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.gitea; 7 opt = options.services.gitea; 8 exe = lib.getExe cfg.package; 9 pg = config.services.postgresql; 10 useMysql = cfg.database.type == "mysql"; 11 usePostgresql = cfg.database.type == "postgres"; 12 useSqlite = cfg.database.type == "sqlite3"; 13 format = pkgs.formats.ini { }; 14 configFile = pkgs.writeText "app.ini" '' 15 APP_NAME = ${cfg.appName} 16 RUN_USER = ${cfg.user} 17 RUN_MODE = prod 18 19 ${generators.toINI {} cfg.settings} 20 21 ${optionalString (cfg.extraConfig != null) cfg.extraConfig} 22 ''; 23in 24 25{ 26 imports = [ 27 (mkRenamedOptionModule [ "services" "gitea" "cookieSecure" ] [ "services" "gitea" "settings" "session" "COOKIE_SECURE" ]) 28 (mkRenamedOptionModule [ "services" "gitea" "disableRegistration" ] [ "services" "gitea" "settings" "service" "DISABLE_REGISTRATION" ]) 29 (mkRenamedOptionModule [ "services" "gitea" "domain" ] [ "services" "gitea" "settings" "server" "DOMAIN" ]) 30 (mkRenamedOptionModule [ "services" "gitea" "httpAddress" ] [ "services" "gitea" "settings" "server" "HTTP_ADDR" ]) 31 (mkRenamedOptionModule [ "services" "gitea" "httpPort" ] [ "services" "gitea" "settings" "server" "HTTP_PORT" ]) 32 (mkRenamedOptionModule [ "services" "gitea" "log" "level" ] [ "services" "gitea" "settings" "log" "LEVEL" ]) 33 (mkRenamedOptionModule [ "services" "gitea" "log" "rootPath" ] [ "services" "gitea" "settings" "log" "ROOT_PATH" ]) 34 (mkRenamedOptionModule [ "services" "gitea" "rootUrl" ] [ "services" "gitea" "settings" "server" "ROOT_URL" ]) 35 (mkRenamedOptionModule [ "services" "gitea" "ssh" "clonePort" ] [ "services" "gitea" "settings" "server" "SSH_PORT" ]) 36 (mkRenamedOptionModule [ "services" "gitea" "staticRootPath" ] [ "services" "gitea" "settings" "server" "STATIC_ROOT_PATH" ]) 37 38 (mkChangedOptionModule [ "services" "gitea" "enableUnixSocket" ] [ "services" "gitea" "settings" "server" "PROTOCOL" ] ( 39 config: if config.services.gitea.enableUnixSocket then "http+unix" else "http" 40 )) 41 42 (mkRemovedOptionModule [ "services" "gitea" "ssh" "enable" ] "services.gitea.ssh.enable has been migrated into freeform setting services.gitea.settings.server.DISABLE_SSH. Keep in mind that the setting is inverted") 43 ]; 44 45 options = { 46 services.gitea = { 47 enable = mkOption { 48 default = false; 49 type = types.bool; 50 description = lib.mdDoc "Enable Gitea Service."; 51 }; 52 53 package = mkOption { 54 default = pkgs.gitea; 55 type = types.package; 56 defaultText = literalExpression "pkgs.gitea"; 57 description = lib.mdDoc "gitea derivation to use"; 58 }; 59 60 useWizard = mkOption { 61 default = false; 62 type = types.bool; 63 description = lib.mdDoc "Do not generate a configuration and use gitea' installation wizard instead. The first registered user will be administrator."; 64 }; 65 66 stateDir = mkOption { 67 default = "/var/lib/gitea"; 68 type = types.str; 69 description = lib.mdDoc "Gitea data directory."; 70 }; 71 72 customDir = mkOption { 73 default = "${cfg.stateDir}/custom"; 74 defaultText = literalExpression ''"''${config.${opt.stateDir}}/custom"''; 75 type = types.str; 76 description = lib.mdDoc "Gitea custom directory. Used for config, custom templates and other options."; 77 }; 78 79 user = mkOption { 80 type = types.str; 81 default = "gitea"; 82 description = lib.mdDoc "User account under which gitea runs."; 83 }; 84 85 group = mkOption { 86 type = types.str; 87 default = "gitea"; 88 description = lib.mdDoc "Group under which gitea runs."; 89 }; 90 91 database = { 92 type = mkOption { 93 type = types.enum [ "sqlite3" "mysql" "postgres" ]; 94 example = "mysql"; 95 default = "sqlite3"; 96 description = lib.mdDoc "Database engine to use."; 97 }; 98 99 host = mkOption { 100 type = types.str; 101 default = "127.0.0.1"; 102 description = lib.mdDoc "Database host address."; 103 }; 104 105 port = mkOption { 106 type = types.port; 107 default = if !usePostgresql then 3306 else pg.port; 108 defaultText = literalExpression '' 109 if config.${opt.database.type} != "postgresql" 110 then 3306 111 else config.${options.services.postgresql.port} 112 ''; 113 description = lib.mdDoc "Database host port."; 114 }; 115 116 name = mkOption { 117 type = types.str; 118 default = "gitea"; 119 description = lib.mdDoc "Database name."; 120 }; 121 122 user = mkOption { 123 type = types.str; 124 default = "gitea"; 125 description = lib.mdDoc "Database user."; 126 }; 127 128 password = mkOption { 129 type = types.str; 130 default = ""; 131 description = lib.mdDoc '' 132 The password corresponding to {option}`database.user`. 133 Warning: this is stored in cleartext in the Nix store! 134 Use {option}`database.passwordFile` instead. 135 ''; 136 }; 137 138 passwordFile = mkOption { 139 type = types.nullOr types.path; 140 default = null; 141 example = "/run/keys/gitea-dbpassword"; 142 description = lib.mdDoc '' 143 A file containing the password corresponding to 144 {option}`database.user`. 145 ''; 146 }; 147 148 socket = mkOption { 149 type = types.nullOr types.path; 150 default = if (cfg.database.createDatabase && usePostgresql) then "/run/postgresql" else if (cfg.database.createDatabase && useMysql) then "/run/mysqld/mysqld.sock" else null; 151 defaultText = literalExpression "null"; 152 example = "/run/mysqld/mysqld.sock"; 153 description = lib.mdDoc "Path to the unix socket file to use for authentication."; 154 }; 155 156 path = mkOption { 157 type = types.str; 158 default = "${cfg.stateDir}/data/gitea.db"; 159 defaultText = literalExpression ''"''${config.${opt.stateDir}}/data/gitea.db"''; 160 description = lib.mdDoc "Path to the sqlite3 database file."; 161 }; 162 163 createDatabase = mkOption { 164 type = types.bool; 165 default = true; 166 description = lib.mdDoc "Whether to create a local database automatically."; 167 }; 168 }; 169 170 dump = { 171 enable = mkOption { 172 type = types.bool; 173 default = false; 174 description = lib.mdDoc '' 175 Enable a timer that runs gitea dump to generate backup-files of the 176 current gitea database and repositories. 177 ''; 178 }; 179 180 interval = mkOption { 181 type = types.str; 182 default = "04:31"; 183 example = "hourly"; 184 description = lib.mdDoc '' 185 Run a gitea dump at this interval. Runs by default at 04:31 every day. 186 187 The format is described in 188 {manpage}`systemd.time(7)`. 189 ''; 190 }; 191 192 backupDir = mkOption { 193 type = types.str; 194 default = "${cfg.stateDir}/dump"; 195 defaultText = literalExpression ''"''${config.${opt.stateDir}}/dump"''; 196 description = lib.mdDoc "Path to the dump files."; 197 }; 198 199 type = mkOption { 200 type = types.enum [ "zip" "rar" "tar" "sz" "tar.gz" "tar.xz" "tar.bz2" "tar.br" "tar.lz4" "tar.zst" ]; 201 default = "zip"; 202 description = lib.mdDoc "Archive format used to store the dump file."; 203 }; 204 205 file = mkOption { 206 type = types.nullOr types.str; 207 default = null; 208 description = lib.mdDoc "Filename to be used for the dump. If `null` a default name is chosen by gitea."; 209 example = "gitea-dump"; 210 }; 211 }; 212 213 lfs = { 214 enable = mkOption { 215 type = types.bool; 216 default = false; 217 description = lib.mdDoc "Enables git-lfs support."; 218 }; 219 220 contentDir = mkOption { 221 type = types.str; 222 default = "${cfg.stateDir}/data/lfs"; 223 defaultText = literalExpression ''"''${config.${opt.stateDir}}/data/lfs"''; 224 description = lib.mdDoc "Where to store LFS files."; 225 }; 226 }; 227 228 appName = mkOption { 229 type = types.str; 230 default = "gitea: Gitea Service"; 231 description = lib.mdDoc "Application name."; 232 }; 233 234 repositoryRoot = mkOption { 235 type = types.str; 236 default = "${cfg.stateDir}/repositories"; 237 defaultText = literalExpression ''"''${config.${opt.stateDir}}/repositories"''; 238 description = lib.mdDoc "Path to the git repositories."; 239 }; 240 241 mailerPasswordFile = mkOption { 242 type = types.nullOr types.str; 243 default = null; 244 example = "/var/lib/secrets/gitea/mailpw"; 245 description = lib.mdDoc "Path to a file containing the SMTP password."; 246 }; 247 248 settings = mkOption { 249 default = {}; 250 description = lib.mdDoc '' 251 Gitea configuration. Refer to <https://docs.gitea.io/en-us/config-cheat-sheet/> 252 for details on supported values. 253 ''; 254 example = literalExpression '' 255 { 256 "cron.sync_external_users" = { 257 RUN_AT_START = true; 258 SCHEDULE = "@every 24h"; 259 UPDATE_EXISTING = true; 260 }; 261 mailer = { 262 ENABLED = true; 263 MAILER_TYPE = "sendmail"; 264 FROM = "do-not-reply@example.org"; 265 SENDMAIL_PATH = "''${pkgs.system-sendmail}/bin/sendmail"; 266 }; 267 other = { 268 SHOW_FOOTER_VERSION = false; 269 }; 270 } 271 ''; 272 type = types.submodule { 273 freeformType = format.type; 274 options = { 275 log = { 276 ROOT_PATH = mkOption { 277 default = "${cfg.stateDir}/log"; 278 defaultText = literalExpression ''"''${config.${opt.stateDir}}/log"''; 279 type = types.str; 280 description = lib.mdDoc "Root path for log files."; 281 }; 282 LEVEL = mkOption { 283 default = "Info"; 284 type = types.enum [ "Trace" "Debug" "Info" "Warn" "Error" "Critical" ]; 285 description = lib.mdDoc "General log level."; 286 }; 287 }; 288 289 server = { 290 PROTOCOL = mkOption { 291 type = types.enum [ "http" "https" "fcgi" "http+unix" "fcgi+unix" ]; 292 default = "http"; 293 description = lib.mdDoc ''Listen protocol. `+unix` means "over unix", not "in addition to."''; 294 }; 295 296 HTTP_ADDR = mkOption { 297 type = types.either types.str types.path; 298 default = if lib.hasSuffix "+unix" cfg.settings.server.PROTOCOL then "/run/gitea/gitea.sock" else "0.0.0.0"; 299 defaultText = literalExpression ''if lib.hasSuffix "+unix" cfg.settings.server.PROTOCOL then "/run/gitea/gitea.sock" else "0.0.0.0"''; 300 description = lib.mdDoc "Listen address. Must be a path when using a unix socket."; 301 }; 302 303 HTTP_PORT = mkOption { 304 type = types.port; 305 default = 3000; 306 description = lib.mdDoc "Listen port. Ignored when using a unix socket."; 307 }; 308 309 DOMAIN = mkOption { 310 type = types.str; 311 default = "localhost"; 312 description = lib.mdDoc "Domain name of your server."; 313 }; 314 315 ROOT_URL = mkOption { 316 type = types.str; 317 default = "http://${cfg.settings.server.DOMAIN}:${toString cfg.settings.server.HTTP_PORT}/"; 318 defaultText = literalExpression ''"http://''${config.services.gitea.settings.server.DOMAIN}:''${toString config.services.gitea.settings.server.HTTP_PORT}/"''; 319 description = lib.mdDoc "Full public URL of gitea server."; 320 }; 321 322 STATIC_ROOT_PATH = mkOption { 323 type = types.either types.str types.path; 324 default = cfg.package.data; 325 defaultText = literalExpression "config.${opt.package}.data"; 326 example = "/var/lib/gitea/data"; 327 description = lib.mdDoc "Upper level of template and static files path."; 328 }; 329 330 DISABLE_SSH = mkOption { 331 type = types.bool; 332 default = false; 333 description = lib.mdDoc "Disable external SSH feature."; 334 }; 335 336 SSH_PORT = mkOption { 337 type = types.port; 338 default = 22; 339 example = 2222; 340 description = lib.mdDoc '' 341 SSH port displayed in clone URL. 342 The option is required to configure a service when the external visible port 343 differs from the local listening port i.e. if port forwarding is used. 344 ''; 345 }; 346 }; 347 348 service = { 349 DISABLE_REGISTRATION = mkEnableOption (lib.mdDoc "the registration lock") // { 350 description = lib.mdDoc '' 351 By default any user can create an account on this `gitea` instance. 352 This can be disabled by using this option. 353 354 *Note:* please keep in mind that this should be added after the initial 355 deploy unless [](#opt-services.gitea.useWizard) 356 is `true` as the first registered user will be the administrator if 357 no install wizard is used. 358 ''; 359 }; 360 }; 361 362 session = { 363 COOKIE_SECURE = mkOption { 364 type = types.bool; 365 default = false; 366 description = lib.mdDoc '' 367 Marks session cookies as "secure" as a hint for browsers to only send 368 them via HTTPS. This option is recommend, if gitea is being served over HTTPS. 369 ''; 370 }; 371 }; 372 }; 373 }; 374 }; 375 376 extraConfig = mkOption { 377 type = with types; nullOr str; 378 default = null; 379 description = lib.mdDoc "Configuration lines appended to the generated gitea configuration file."; 380 }; 381 }; 382 }; 383 384 config = mkIf cfg.enable { 385 assertions = [ 386 { assertion = cfg.database.createDatabase -> useSqlite || cfg.database.user == cfg.user; 387 message = "services.gitea.database.user must match services.gitea.user if the database is to be automatically provisioned"; 388 } 389 ]; 390 391 services.gitea.settings = { 392 "cron.update_checker".ENABLED = lib.mkDefault false; 393 394 database = mkMerge [ 395 { 396 DB_TYPE = cfg.database.type; 397 } 398 (mkIf (useMysql || usePostgresql) { 399 HOST = if cfg.database.socket != null then cfg.database.socket else cfg.database.host + ":" + toString cfg.database.port; 400 NAME = cfg.database.name; 401 USER = cfg.database.user; 402 PASSWD = "#dbpass#"; 403 }) 404 (mkIf useSqlite { 405 PATH = cfg.database.path; 406 }) 407 (mkIf usePostgresql { 408 SSL_MODE = "disable"; 409 }) 410 ]; 411 412 repository = { 413 ROOT = cfg.repositoryRoot; 414 }; 415 416 server = mkIf cfg.lfs.enable { 417 LFS_START_SERVER = true; 418 LFS_JWT_SECRET = "#lfsjwtsecret#"; 419 }; 420 421 session = { 422 COOKIE_NAME = lib.mkDefault "session"; 423 }; 424 425 security = { 426 SECRET_KEY = "#secretkey#"; 427 INTERNAL_TOKEN = "#internaltoken#"; 428 INSTALL_LOCK = true; 429 }; 430 431 mailer = mkIf (cfg.mailerPasswordFile != null) { 432 PASSWD = "#mailerpass#"; 433 }; 434 435 oauth2 = { 436 JWT_SECRET = "#oauth2jwtsecret#"; 437 }; 438 439 lfs = mkIf cfg.lfs.enable { 440 PATH = cfg.lfs.contentDir; 441 }; 442 }; 443 444 services.postgresql = optionalAttrs (usePostgresql && cfg.database.createDatabase) { 445 enable = mkDefault true; 446 447 ensureDatabases = [ cfg.database.name ]; 448 ensureUsers = [ 449 { name = cfg.database.user; 450 ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; }; 451 } 452 ]; 453 }; 454 455 services.mysql = optionalAttrs (useMysql && cfg.database.createDatabase) { 456 enable = mkDefault true; 457 package = mkDefault pkgs.mariadb; 458 459 ensureDatabases = [ cfg.database.name ]; 460 ensureUsers = [ 461 { name = cfg.database.user; 462 ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; 463 } 464 ]; 465 }; 466 467 systemd.tmpfiles.rules = [ 468 "d '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -" 469 "z '${cfg.dump.backupDir}' 0750 ${cfg.user} ${cfg.group} - -" 470 "Z '${cfg.dump.backupDir}' - ${cfg.user} ${cfg.group} - -" 471 "d '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -" 472 "z '${cfg.repositoryRoot}' 0750 ${cfg.user} ${cfg.group} - -" 473 "Z '${cfg.repositoryRoot}' - ${cfg.user} ${cfg.group} - -" 474 "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" 475 "d '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" 476 "d '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -" 477 "d '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" 478 "d '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -" 479 "d '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -" 480 "z '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" 481 "z '${cfg.stateDir}/.ssh' 0700 ${cfg.user} ${cfg.group} - -" 482 "z '${cfg.stateDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" 483 "z '${cfg.customDir}' 0750 ${cfg.user} ${cfg.group} - -" 484 "z '${cfg.customDir}/conf' 0750 ${cfg.user} ${cfg.group} - -" 485 "z '${cfg.stateDir}/data' 0750 ${cfg.user} ${cfg.group} - -" 486 "z '${cfg.stateDir}/log' 0750 ${cfg.user} ${cfg.group} - -" 487 "Z '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -" 488 489 # If we have a folder or symlink with gitea locales, remove it 490 # And symlink the current gitea locales in place 491 "L+ '${cfg.stateDir}/conf/locale' - - - - ${cfg.package.out}/locale" 492 493 ] ++ lib.optionals cfg.lfs.enable [ 494 "d '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" 495 "z '${cfg.lfs.contentDir}' 0750 ${cfg.user} ${cfg.group} - -" 496 "Z '${cfg.lfs.contentDir}' - ${cfg.user} ${cfg.group} - -" 497 ]; 498 499 systemd.services.gitea = { 500 description = "gitea"; 501 after = [ "network.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service"; 502 wantedBy = [ "multi-user.target" ]; 503 path = [ cfg.package pkgs.git pkgs.gnupg ]; 504 505 # In older versions the secret naming for JWT was kind of confusing. 506 # The file jwt_secret hold the value for LFS_JWT_SECRET and JWT_SECRET 507 # wasn't persistent at all. 508 # To fix that, there is now the file oauth2_jwt_secret containing the 509 # values for JWT_SECRET and the file jwt_secret gets renamed to 510 # lfs_jwt_secret. 511 # We have to consider this to stay compatible with older installations. 512 preStart = let 513 runConfig = "${cfg.customDir}/conf/app.ini"; 514 secretKey = "${cfg.customDir}/conf/secret_key"; 515 oauth2JwtSecret = "${cfg.customDir}/conf/oauth2_jwt_secret"; 516 oldLfsJwtSecret = "${cfg.customDir}/conf/jwt_secret"; # old file for LFS_JWT_SECRET 517 lfsJwtSecret = "${cfg.customDir}/conf/lfs_jwt_secret"; # new file for LFS_JWT_SECRET 518 internalToken = "${cfg.customDir}/conf/internal_token"; 519 replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret"; 520 in '' 521 # copy custom configuration and generate random secrets if needed 522 ${optionalString (!cfg.useWizard) '' 523 function gitea_setup { 524 cp -f '${configFile}' '${runConfig}' 525 526 if [ ! -s '${secretKey}' ]; then 527 ${exe} generate secret SECRET_KEY > '${secretKey}' 528 fi 529 530 # Migrate LFS_JWT_SECRET filename 531 if [[ -s '${oldLfsJwtSecret}' && ! -s '${lfsJwtSecret}' ]]; then 532 mv '${oldLfsJwtSecret}' '${lfsJwtSecret}' 533 fi 534 535 if [ ! -s '${oauth2JwtSecret}' ]; then 536 ${exe} generate secret JWT_SECRET > '${oauth2JwtSecret}' 537 fi 538 539 ${lib.optionalString cfg.lfs.enable '' 540 if [ ! -s '${lfsJwtSecret}' ]; then 541 ${exe} generate secret LFS_JWT_SECRET > '${lfsJwtSecret}' 542 fi 543 ''} 544 545 if [ ! -s '${internalToken}' ]; then 546 ${exe} generate secret INTERNAL_TOKEN > '${internalToken}' 547 fi 548 549 chmod u+w '${runConfig}' 550 ${replaceSecretBin} '#secretkey#' '${secretKey}' '${runConfig}' 551 ${replaceSecretBin} '#dbpass#' '${cfg.database.passwordFile}' '${runConfig}' 552 ${replaceSecretBin} '#oauth2jwtsecret#' '${oauth2JwtSecret}' '${runConfig}' 553 ${replaceSecretBin} '#internaltoken#' '${internalToken}' '${runConfig}' 554 555 ${lib.optionalString cfg.lfs.enable '' 556 ${replaceSecretBin} '#lfsjwtsecret#' '${lfsJwtSecret}' '${runConfig}' 557 ''} 558 559 ${lib.optionalString (cfg.mailerPasswordFile != null) '' 560 ${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}' 561 ''} 562 chmod u-w '${runConfig}' 563 } 564 (umask 027; gitea_setup) 565 ''} 566 567 # run migrations/init the database 568 ${exe} migrate 569 570 # update all hooks' binary paths 571 ${exe} admin regenerate hooks 572 573 # update command option in authorized_keys 574 if [ -r ${cfg.stateDir}/.ssh/authorized_keys ] 575 then 576 ${exe} admin regenerate keys 577 fi 578 ''; 579 580 serviceConfig = { 581 Type = "simple"; 582 User = cfg.user; 583 Group = cfg.group; 584 WorkingDirectory = cfg.stateDir; 585 ExecStart = "${exe} web --pid /run/gitea/gitea.pid"; 586 Restart = "always"; 587 # Runtime directory and mode 588 RuntimeDirectory = "gitea"; 589 RuntimeDirectoryMode = "0755"; 590 # Access write directories 591 ReadWritePaths = [ cfg.customDir cfg.dump.backupDir cfg.repositoryRoot cfg.stateDir cfg.lfs.contentDir ]; 592 UMask = "0027"; 593 # Capabilities 594 CapabilityBoundingSet = ""; 595 # Security 596 NoNewPrivileges = true; 597 # Sandboxing 598 ProtectSystem = "strict"; 599 ProtectHome = true; 600 PrivateTmp = true; 601 PrivateDevices = true; 602 PrivateUsers = true; 603 ProtectHostname = true; 604 ProtectClock = true; 605 ProtectKernelTunables = true; 606 ProtectKernelModules = true; 607 ProtectKernelLogs = true; 608 ProtectControlGroups = true; 609 RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ]; 610 LockPersonality = true; 611 MemoryDenyWriteExecute = true; 612 RestrictRealtime = true; 613 RestrictSUIDSGID = true; 614 PrivateMounts = true; 615 # System Call Filtering 616 SystemCallArchitectures = "native"; 617 SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap"; 618 }; 619 620 environment = { 621 USER = cfg.user; 622 HOME = cfg.stateDir; 623 GITEA_WORK_DIR = cfg.stateDir; 624 GITEA_CUSTOM = cfg.customDir; 625 }; 626 }; 627 628 users.users = mkIf (cfg.user == "gitea") { 629 gitea = { 630 description = "Gitea Service"; 631 home = cfg.stateDir; 632 useDefaultShell = true; 633 group = cfg.group; 634 isSystemUser = true; 635 }; 636 }; 637 638 users.groups = mkIf (cfg.group == "gitea") { 639 gitea = {}; 640 }; 641 642 warnings = 643 optional (cfg.database.password != "") "config.services.gitea.database.password will be stored as plaintext in the Nix store. Use database.passwordFile instead." ++ 644 optional (cfg.extraConfig != null) '' 645 services.gitea.`extraConfig` is deprecated, please use services.gitea.`settings`. 646 ''; 647 648 # Create database passwordFile default when password is configured. 649 services.gitea.database.passwordFile = 650 mkDefault (toString (pkgs.writeTextFile { 651 name = "gitea-database-password"; 652 text = cfg.database.password; 653 })); 654 655 systemd.services.gitea-dump = mkIf cfg.dump.enable { 656 description = "gitea dump"; 657 after = [ "gitea.service" ]; 658 path = [ cfg.package ]; 659 660 environment = { 661 USER = cfg.user; 662 HOME = cfg.stateDir; 663 GITEA_WORK_DIR = cfg.stateDir; 664 }; 665 666 serviceConfig = { 667 Type = "oneshot"; 668 User = cfg.user; 669 ExecStart = "${exe} dump --type ${cfg.dump.type}" + optionalString (cfg.dump.file != null) " --file ${cfg.dump.file}"; 670 WorkingDirectory = cfg.dump.backupDir; 671 }; 672 }; 673 674 systemd.timers.gitea-dump = mkIf cfg.dump.enable { 675 description = "Update timer for gitea-dump"; 676 partOf = [ "gitea-dump.service" ]; 677 wantedBy = [ "timers.target" ]; 678 timerConfig.OnCalendar = cfg.dump.interval; 679 }; 680 }; 681 meta.maintainers = with lib.maintainers; [ srhb ma27 thehedgeh0g ]; 682}