From 0c6c138da5350a128fe29413f2b3979f3a9e3dac Mon Sep 17 00:00:00 2001 From: Christoph Schmatzler Date: Sun, 4 Jan 2026 19:50:23 +0000 Subject: [PATCH] refactor(gitea): convert profile to configurable module - Move gitea.nix from profiles/ to modules/ with mkOption-based config - Make litestream/restic buckets and secret paths configurable - Rename secrets to consistent michael-gitea-* naming - Configure gitea module in hosts/michael/default.nix --- hosts/michael/default.nix | 16 +- hosts/michael/secrets.nix | 18 +- modules/gitea.nix | 191 ++++++++++++++++++ profiles/gitea.nix | 145 ------------- ...tic-gitea-env => michael-gitea-restic-env} | 0 ...password => michael-gitea-restic-password} | 0 6 files changed, 216 insertions(+), 154 deletions(-) create mode 100644 modules/gitea.nix delete mode 100644 profiles/gitea.nix rename secrets/{michael-restic-gitea-env => michael-gitea-restic-env} (100%) rename secrets/{michael-restic-gitea-password => michael-gitea-restic-password} (100%) diff --git a/hosts/michael/default.nix b/hosts/michael/default.nix index a56a58f..8f538b2 100644 --- a/hosts/michael/default.nix +++ b/hosts/michael/default.nix @@ -2,6 +2,7 @@ modulesPath, hostname, inputs, + config, user, ... }: { @@ -11,16 +12,29 @@ ./disk-config.nix ./hardware-configuration.nix ./secrets.nix + ../../modules/gitea.nix ../../profiles/core.nix ../../profiles/openssh.nix ../../profiles/fail2ban.nix - ../../profiles/gitea.nix ../../profiles/nixos.nix ../../profiles/tailscale.nix inputs.disko.nixosModules.disko inputs.sops-nix.nixosModules.sops ]; + my.gitea = { + enable = true; + litestream = { + bucket = "michael-gitea-litestream"; + secretFile = config.sops.secrets.michael-gitea-litestream.path; + }; + restic = { + bucket = "michael-gitea-repositories"; + passwordFile = config.sops.secrets.michael-gitea-restic-password.path; + environmentFile = config.sops.secrets.michael-gitea-restic-env.path; + }; + }; + home-manager.users.${user} = { imports = [ inputs.nixvim.homeModules.nixvim diff --git a/hosts/michael/secrets.nix b/hosts/michael/secrets.nix index 686a3c2..37d65d9 100644 --- a/hosts/michael/secrets.nix +++ b/hosts/michael/secrets.nix @@ -1,18 +1,20 @@ {...}: { - sops.secrets.gitea-litestream = { + sops.secrets.michael-gitea-litestream = { sopsFile = ../../secrets/michael-gitea-litestream; format = "binary"; - }; - - sops.secrets.restic-gitea-password = { - sopsFile = ../../secrets/michael-restic-gitea-password; - format = "binary"; owner = "gitea"; group = "gitea"; }; - sops.secrets.restic-gitea-env = { - sopsFile = ../../secrets/michael-restic-gitea-env; + sops.secrets.michael-gitea-restic-password = { + sopsFile = ../../secrets/michael-gitea-restic-password; + format = "binary"; + owner = "gitea"; + group = "gitea"; + }; + + sops.secrets.michael-gitea-restic-env = { + sopsFile = ../../secrets/michael-gitea-restic-env; format = "binary"; owner = "gitea"; group = "gitea"; diff --git a/modules/gitea.nix b/modules/gitea.nix new file mode 100644 index 0000000..d57a489 --- /dev/null +++ b/modules/gitea.nix @@ -0,0 +1,191 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.my.gitea; +in { + options.my.gitea = { + enable = mkEnableOption "Gitea git hosting service"; + + litestream = { + bucket = mkOption { + type = types.str; + description = "S3 bucket name for Litestream database replication"; + }; + + secretFile = mkOption { + type = types.path; + description = "Path to the environment file containing S3 credentials for Litestream"; + }; + }; + + restic = { + bucket = mkOption { + type = types.str; + description = "S3 bucket name for Restic repository backups"; + }; + + passwordFile = mkOption { + type = types.path; + description = "Path to the file containing the Restic repository password"; + }; + + environmentFile = mkOption { + type = types.path; + description = "Path to the environment file containing S3 credentials for Restic"; + }; + }; + + s3 = { + endpoint = mkOption { + type = types.str; + default = "s3.eu-central-003.backblazeb2.com"; + description = "S3 endpoint URL"; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [80 443]; + + services.redis.servers.gitea = { + enable = true; + port = 6380; + bind = "127.0.0.1"; + settings = { + maxmemory = "64mb"; + maxmemory-policy = "allkeys-lru"; + }; + }; + + services.gitea = { + enable = true; + database = { + type = "sqlite3"; + path = "/var/lib/gitea/data/gitea.db"; + }; + settings = { + server = { + ROOT_URL = "https://git.schmatzler.com/"; + DOMAIN = "git.schmatzler.com"; + HTTP_ADDR = "127.0.0.1"; + HTTP_PORT = 3000; + LANDING_PAGE = "explore"; + }; + service.DISABLE_REGISTRATION = true; + security.INSTALL_LOCK = true; + cache = { + ADAPTER = "redis"; + HOST = "redis://127.0.0.1:6380/0?pool_size=100&idle_timeout=180s"; + ITEM_TTL = "16h"; + }; + "cache.last_commit" = { + ITEM_TTL = "8760h"; + COMMITS_COUNT = 100; + }; + session = { + PROVIDER = "redis"; + PROVIDER_CONFIG = "redis://127.0.0.1:6380/1?pool_size=100&idle_timeout=180s"; + COOKIE_SECURE = true; + SAME_SITE = "strict"; + }; + api.ENABLE_SWAGGER = false; + }; + }; + + services.litestream = { + enable = true; + environmentFile = cfg.litestream.secretFile; + settings = { + dbs = [ + { + path = "/var/lib/gitea/data/gitea.db"; + replicas = [ + { + type = "s3"; + bucket = cfg.litestream.bucket; + path = "gitea"; + endpoint = cfg.s3.endpoint; + } + ]; + } + ]; + }; + }; + + systemd.services.litestream = { + serviceConfig = { + User = mkForce "gitea"; + Group = mkForce "gitea"; + }; + }; + + services.caddy = { + enable = true; + virtualHosts."git.schmatzler.com".extraConfig = '' + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains" + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + Referrer-Policy "strict-origin-when-cross-origin" + } + reverse_proxy localhost:3000 + ''; + }; + + services.restic.backups.gitea = { + repository = "s3:${cfg.s3.endpoint}/${cfg.restic.bucket}"; + paths = ["/var/lib/gitea"]; + exclude = [ + "/var/lib/gitea/log" + "/var/lib/gitea/data/gitea.db" + "/var/lib/gitea/data/gitea.db-shm" + "/var/lib/gitea/data/gitea.db-wal" + ]; + passwordFile = cfg.restic.passwordFile; + environmentFile = cfg.restic.environmentFile; + pruneOpts = [ + "--keep-daily 7" + "--keep-weekly 4" + "--keep-monthly 6" + ]; + timerConfig = { + OnCalendar = "daily"; + Persistent = true; + RandomizedDelaySec = "1h"; + }; + }; + + systemd.services.restic-backups-gitea = { + wants = ["restic-init-gitea.service"]; + after = ["restic-init-gitea.service"]; + serviceConfig = { + User = mkForce "gitea"; + Group = mkForce "gitea"; + }; + }; + + systemd.services.restic-init-gitea = { + description = "Initialize Restic repository for Gitea backups"; + wantedBy = ["multi-user.target"]; + after = ["network-online.target"]; + wants = ["network-online.target"]; + path = [pkgs.restic]; + serviceConfig = { + Type = "oneshot"; + User = "gitea"; + Group = "gitea"; + RemainAfterExit = true; + EnvironmentFile = cfg.restic.environmentFile; + }; + script = '' + export RESTIC_PASSWORD=$(cat ${cfg.restic.passwordFile}) + restic -r s3:${cfg.s3.endpoint}/${cfg.restic.bucket} snapshots &>/dev/null || \ + restic -r s3:${cfg.s3.endpoint}/${cfg.restic.bucket} init + ''; + }; + }; +} diff --git a/profiles/gitea.nix b/profiles/gitea.nix deleted file mode 100644 index a5169d0..0000000 --- a/profiles/gitea.nix +++ /dev/null @@ -1,145 +0,0 @@ -{ - lib, - pkgs, - config, - ... -}: { - networking.firewall.allowedTCPPorts = [80 443]; - - services.redis.servers.gitea = { - enable = true; - port = 6380; - bind = "127.0.0.1"; - settings = { - maxmemory = "64mb"; - maxmemory-policy = "allkeys-lru"; - }; - }; - - services.gitea = { - enable = true; - database = { - type = "sqlite3"; - path = "/var/lib/gitea/data/gitea.db"; - }; - settings = { - server = { - ROOT_URL = "https://git.schmatzler.com/"; - DOMAIN = "git.schmatzler.com"; - HTTP_ADDR = "127.0.0.1"; - HTTP_PORT = 3000; - LANDING_PAGE = "explore"; - }; - service.DISABLE_REGISTRATION = true; - security.INSTALL_LOCK = true; - cache = { - ADAPTER = "redis"; - HOST = "redis://127.0.0.1:6380/0?pool_size=100&idle_timeout=180s"; - ITEM_TTL = "16h"; - }; - "cache.last_commit" = { - ITEM_TTL = "8760h"; - COMMITS_COUNT = 100; - }; - session = { - PROVIDER = "redis"; - PROVIDER_CONFIG = "redis://127.0.0.1:6380/1?pool_size=100&idle_timeout=180s"; - COOKIE_SECURE = true; - SAME_SITE = "strict"; - }; - api.ENABLE_SWAGGER = false; - }; - }; - - services.litestream = { - enable = true; - environmentFile = "/run/secrets/gitea-litestream"; - settings = { - dbs = [ - { - path = "/var/lib/gitea/data/gitea.db"; - replicas = [ - { - type = "s3"; - bucket = "michael-gitea-litestream"; - path = "gitea"; - endpoint = "s3.eu-central-003.backblazeb2.com"; - } - ]; - } - ]; - }; - }; - - systemd.services.litestream = { - serviceConfig = { - User = lib.mkForce "gitea"; - Group = lib.mkForce "gitea"; - }; - }; - - services.caddy = { - enable = true; - virtualHosts."git.schmatzler.com".extraConfig = '' - header { - Strict-Transport-Security "max-age=31536000; includeSubDomains" - X-Content-Type-Options "nosniff" - X-Frame-Options "DENY" - Referrer-Policy "strict-origin-when-cross-origin" - } - reverse_proxy localhost:3000 - ''; - }; - - services.restic.backups.gitea = { - repository = "s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories"; - paths = ["/var/lib/gitea"]; - exclude = [ - "/var/lib/gitea/log" - "/var/lib/gitea/data/gitea.db" - "/var/lib/gitea/data/gitea.db-shm" - "/var/lib/gitea/data/gitea.db-wal" - ]; - passwordFile = "/run/secrets/restic-gitea-password"; - environmentFile = "/run/secrets/restic-gitea-env"; - pruneOpts = [ - "--keep-daily 7" - "--keep-weekly 4" - "--keep-monthly 6" - ]; - timerConfig = { - OnCalendar = "daily"; - Persistent = true; - RandomizedDelaySec = "1h"; - }; - }; - - systemd.services.restic-backups-gitea = { - wants = ["restic-init-gitea.service"]; - after = ["restic-init-gitea.service"]; - serviceConfig = { - User = lib.mkForce "gitea"; - Group = lib.mkForce "gitea"; - }; - }; - - systemd.services.restic-init-gitea = { - description = "Initialize Restic repository for Gitea backups"; - wantedBy = ["multi-user.target"]; - after = ["network-online.target"]; - wants = ["network-online.target"]; - path = [pkgs.restic]; - serviceConfig = { - Type = "oneshot"; - User = "gitea"; - Group = "gitea"; - RemainAfterExit = true; - EnvironmentFile = config.sops.secrets.restic-gitea-env.path; - }; - script = '' - export RESTIC_PASSWORD=$(cat ${config.sops.secrets.restic-gitea-password.path}) - restic -r s3:s3.eu-central-003.backblazeb2.com/gitea-restic snapshots &>/dev/null || \ - restic -r s3:s3.eu-central-003.backblazeb2.com/gitea-restic init - ''; - }; -} diff --git a/secrets/michael-restic-gitea-env b/secrets/michael-gitea-restic-env similarity index 100% rename from secrets/michael-restic-gitea-env rename to secrets/michael-gitea-restic-env diff --git a/secrets/michael-restic-gitea-password b/secrets/michael-gitea-restic-password similarity index 100% rename from secrets/michael-restic-gitea-password rename to secrets/michael-gitea-restic-password