{ pkgs, lib, config, service_configs, ... }: { imports = [ (lib.serviceMountWithZpool "gitea" service_configs.zpool_ssds [ config.services.gitea.stateDir ]) (lib.serviceFilePerms "gitea" [ "Z ${config.services.gitea.stateDir} 0700 ${config.services.gitea.user} ${config.services.gitea.group}" ]) (lib.mkCaddyReverseProxy { domain = service_configs.gitea.domain; port = service_configs.ports.private.gitea.port; }) (lib.mkFail2banJail { name = "gitea"; failregex = "^.*Failed authentication attempt for .* from :.*$"; }) ]; services.gitea = { enable = true; appName = "Simon Gardling's Gitea instance"; stateDir = service_configs.gitea.dir; database = { type = "postgres"; socket = service_configs.postgres.socket; }; settings = { server = { SSH_USER = "gitea"; DOMAIN = service_configs.gitea.domain; ROOT_URL = "https://" + config.services.gitea.settings.server.DOMAIN; HTTP_PORT = service_configs.ports.private.gitea.port; LANDING_PAGE = "/explore/repos"; DISABLE_HTTP_GIT = true; }; session = { # https cookies or smth COOKIE_SECURE = true; }; # only I shall use gitea service.DISABLE_REGISTRATION = true; actions.ENABLED = true; }; }; # Hide repo Actions/workflow details from anonymous visitors. Gitea's own # REQUIRE_SIGNIN_VIEW=expensive does not cover /{user}/{repo}/actions, and # the API auth chain (routers/api/v1/api.go buildAuthGroup) deliberately # omits `auth_service.Session`, so an /api/v1/user probe would 401 even # for logged-in browser sessions. We gate at Caddy instead: forward_auth # probes a lightweight *web-UI* endpoint that does accept session cookies, # and Gitea's own reqSignIn middleware answers 303 to /user/login for # anonymous callers which we rewrite to preserve the original URL. # Workflow status badges stay public so README links keep rendering. services.caddy.virtualHosts.${service_configs.gitea.domain}.extraConfig = '' @repoActionsNotBadge { path_regexp ^/[^/]+/[^/]+/actions(/.*)?$ not path_regexp ^/[^/]+/[^/]+/actions/workflows/[^/]+/badge\.svg$ } handle @repoActionsNotBadge { forward_auth :${toString service_configs.ports.private.gitea.port} { uri /user/stopwatches @unauthorized status 302 303 handle_response @unauthorized { redir * /user/login?redirect_to={uri} 302 } } } ''; services.postgresql = { ensureDatabases = [ config.services.gitea.user ]; ensureUsers = [ { name = config.services.gitea.database.user; ensureDBOwnership = true; ensureClauses.login = true; } ]; }; services.openssh.settings.AllowUsers = [ config.services.gitea.user ]; }