{ config, lib, pkgs, ... }: with lib; let cfg = config.fudo.services.gitea-container; in { options.fudo.services.gitea-container = with types; { enable = mkEnableOption "Enable Gitea running in a container."; site-name = mkOption { type = str; description = "Name of this Gitea instance."; }; hostname = mkOption { type = str; description = "Hostname at which the server is reachable."; }; state-directory = mkOption { type = str; description = "Path at which to store Gitea state."; }; secret-key-file = mkOption { type = str; description = "Path to file containing Gitea secret key, for encrypting secrets."; }; trusted-networks = mkOption { type = listOf str; description = "List of networks to be considered trusted (for metrics access)."; default = [ ]; }; openid-urls = mkOption { type = listOf str; description = "List of authorized OpenID providers."; }; networking = { interface = mkOption { type = str; description = "Parent host interface on which to listen."; }; ipv4 = mkOption { type = nullOr (submodule { options = { address = mkOption { type = str; description = "IP address."; }; prefixLength = mkOption { type = int; description = "Significant bits in the address."; }; }; }); default = null; }; ipv6 = mkOption { type = nullOr (submodule { options = { address = mkOption { type = str; description = "IP address."; }; prefixLength = mkOption { type = int; description = "Significant bits in the address."; }; }; }); default = null; }; }; }; config = mkIf cfg.enable { systemd.tmpfiles.rules = [ "d ${cfg.state-directory} 700 root root - -" ]; containers.gitea = { autoStart = true; additionalCapabilities = [ "CAP_NET_ADMIN" ]; macvlans = [ cfg.networking.interface ]; bindMounts = { "/state" = { hostPath = cfg.state-directory; isReadOnly = false; }; }; config = { nixpkgs.pkgs = pkgs; systemd = { tmpfiles.rules = [ "d /state 0755 root root - -" ]; }; networking = { defaultGateway = config.networking.defaultGateway; enableIPv6 = !isNull cfg.networking.ipv6; firewall = { enable = true; allowedTCPPorts = [ 22 80 443 ]; }; interfaces."mv-${cfg.networking.interface}" = { ipv4.addresses = optional (!isNull cfg.networking.ipv4) { address = cfg.networking.ipv4.address; prefixLength = cfg.networking.ipv4.prefixLength; }; ipv6.addresses = optional (!isNull cfg.networking.ipv6) { address = cfg.networking.ipv6.address; prefixLength = cfg.networking.ipv6.prefixLength; }; }; }; services = { gitea = { enable = true; appName = cfg.site-name; database = { createDatabase = true; type = "sqlite3"; }; repositoryRoot = "/state/repositories"; stateDir = "/state/gitea"; settings = { service.DISABLE_REGISTRATION = true; security = { INSTALL_LOCK = true; SECRET_KEY = "file:${cfg.secret-key-file}"; LOGIN_REMEMBER_DAYS = 30; }; metrics.ENABLED = cfg.trusted-networks != [ ]; server = { START_SSH_SERVER = true; # Host & port to display in the clone URL SSH_DOMAIN = cfg.hostname; SSH_PORT = 22; SSH_LISTEN_PORT = 2222; SSH_LISTEN_HOST = "0.0.0.0"; DOMAIN = cfg.hostname; ROOT_URL = "https://${cfg.hostname}"; HTTP_ADDR = "127.0.0.1"; HTTP_PORT = 8080; }; openid = { ENABLE_OPENID_SIGNIN = true; WHITELISTED_URIS = cfg.openid-urls; }; oauth2_client = { REGISTER_EMAIL_CONFIRM = false; OPENID_CONNECT_SCOPES = [ "email" "profile" ]; ENABLE_AUTO_REGISTRATION = true; USERNAME = "email"; UPDATE_AVATAR = true; ACCOUNT_LINKING = "login"; }; }; }; xinetd = { enable = true; services = [{ name = "ssh"; # port = 22; # protocol = "tcp"; extraConfig = '' redirect = localhost 2222 wait = no socket_type = stream ''; user = "nobody"; # Must be defined, but not used server = "/usr/bin/env"; # unlisted = true; }]; }; nginx = { enable = true; recommendedOptimisation = true; recommendedProxySettings = true; recommendedTlsSettings = true; recommendedGzipSettings = true; virtualHosts."${cfg.hostname}" = { # enableACME = true; # forceSSL = true; locations."/".proxyPass = "http://127.0.0.1:8080"; locations."/metrics" = mkIf (cfg.trusted-networks != [ ]) (let networkAllowClauses = map (net: "allow ${net};") cfg.trusted-networks; in concatStringsSep "\n" (networkAllowClauses ++ [ "deny all;" ])); }; }; }; }; }; }; }