main @ 165 LINES
[ HISTORY ] [ UP ]
┌─ NIX ──────────────────────────────────────────────────────────────────────┐
│ { │
│ config, │
│ lib, │
│ pkgs, │
│ ook, │
│ ... │
│ }: let │
│ inherit (lib) mkIf elem getExe optionalAttrs optionals; │
│ │
│ inherit (config.ooknet.server) services domain; │
│ inherit (config.services) fail2ban; │
│ │
│ forgejoCfg = config.ooknet.server.forgejo; │
│ themeCfg = forgejoCfg.customTheme; │
│ themeFile = pkgs.writeText "theme-${themeCfg.name}.css" ( │
│ ook.lib.color.export.toForgejoTheme {scheme = ook.themes.dark;} │
│ ); │
│ │
│ blankLogoSvg = pkgs.writeText "logo.svg" ''<svg xmlns="http://www.w3.org/2000/sv │
│ g" viewBox="0 0 1 1"></svg>''; │
│ blankLogoPng = pkgs.runCommand "logo.png" {nativeBuildInputs = [pkgs.imagemagick │
│ ];} '' │
│ magick -size 1x1 xc:transparent PNG32:$out │
│ ''; │
│ │
│ settingsFormat = lib.generators.toINI { │
│ mkKeyValue = lib.generators.mkKeyValueDefault {} " = "; │
│ }; │
│ in { │
│ imports = [./options.nix]; │
│ │
│ config = mkIf (elem "forgejo" services) { │
│ networking.firewall.allowedTCPPorts = [2222]; │
│ │
│ systemd.tmpfiles.rules = let │
│ fj = config.services.forgejo; │
│ pub = "${fj.customDir}/public"; │
│ mkdir = p: "d ${p} 0750 ${fj.user} ${fj.group} - -"; │
│ in │
│ optionals (themeCfg.enable || forgejoCfg.hideLogo) [ │
│ (mkdir pub) │
│ (mkdir "${pub}/assets") │
│ ] │
│ ++ optionals themeCfg.enable [ │
│ (mkdir "${pub}/assets/css") │
│ "L+ ${pub}/assets/css/theme-${themeCfg.name}.css - - - - ${themeFile}" │
│ ] │
│ ++ optionals forgejoCfg.hideLogo [ │
│ (mkdir "${pub}/assets/img") │
│ "L+ ${pub}/assets/img/logo.svg - - - - ${blankLogoSvg}" │
│ "L+ ${pub}/assets/img/logo.png - - - - ${blankLogoPng}" │
│ ]; │
│ │
│ ooknet.server = { │
│ webserver.caddy.enable = true; │
│ database.postgresql.enable = true; │
│ }; │
│ services = { │
│ forgejo = { │
│ enable = true; │
│ │
│ settings = { │
│ server = { │
│ DOMAIN = "git.${domain}"; │
│ ROOT_URL = "https://git.${domain}"; │
│ HTTP_PORT = 3000; │
│ LANDING_PAGE = "explore"; │
│ │
│ START_SSH_SERVER = true; │
│ SSH_PORT = 2222; │
│ SSH_LISTEN_PORT = 2222; │
│ }; │
│ database = { │
│ type = "postgres"; │
│ createDatabase = true; │
│ }; │
│ service = { │
│ DISABLE_REGISTRATION = true; │
│ }; │
│ security = { │
│ INSTALL_LOCK = true; │
│ }; │
│ ui = mkIf themeCfg.enable ({ │
│ THEMES = "forgejo-auto,forgejo-light,forgejo-dark,${themeCfg.name}"; │
│ } │
│ // optionalAttrs themeCfg.default { │
│ DEFAULT_THEME = themeCfg.name; │
│ }); │
│ }; │
│ }; │
│ caddy.virtualHosts = { │
│ "git.${domain}".extraConfig = '' │
│ header { │
│ Strict-Transport-Security "max-age=31536000;" │
│ X-XSS-Protection "1; mode=block" │
│ X-Frame-Options "DENY" │
│ X-Content-Type-Options "nosniff" │
│ -Server │
│ Referrer-Policy "no-referrer" │
│ } │
│ │
│ # rate limiting: 300 req/min per real client. {client_ip} resolves │
│ # the true client via trusted_proxies, not the shared cloudflare │
│ # edge IP that {remote_host} gave (which made one user's page load │
│ # of assets 429 everyone behind that edge) │
│ rate_limit { │
│ zone git { │
│ key {client_ip} │
│ events 300 │
│ window 1m │
│ } │
│ } │
│ │
│ # Handle proxying │
│ handle_path /* { │
│ reverse_proxy localhost:3000 { │
│ header_up X-Real-IP {client_ip} │
│ header_up X-Forwarded-For {client_ip} │
│ header_up X-Forwarded-Proto {scheme} │
│ } │
│ } │
│ ''; │
│ }; │
│ │
│ fail2ban.jails.forgejo.settings = { │
│ filter = "forgejo"; │
│ mode = "aggressive"; │
│ action = "nftables-multiport"; │
│ maxretry = 5; │
│ findTime = "24h"; │
│ bantime = "48h"; │
│ }; │
│ }; │
│ environment = { │
│ etc."fail2ban/filter.d/forgejo.conf".text = mkIf fail2ban.enable (settingsFo │
│ rmat { │
│ # fail2ban only reads [Definition] (singular); and the regex needs │
│ # <HOST> to extract the IP to ban. forgejo now logs the real client │
│ # IP (X-Real-IP {client_ip}) as "... from <ip>:<port>". │
│ Definition = { │
│ failregex = "^.*(Failed authentication attempt|invalid credentials|Attem │
│ pted access of unknown user).* from <HOST>"; │
│ journalmatch = "_SYSTEMD_UNIT=forgejo.service"; │
│ }; │
│ }); │
│ │
│ # credit to TLATER │
│ # https://discourse.nixos.org/t/how-to-access-forgejo-cli/45370 │
│ systemPackages = let │
│ cfg = config.services.forgejo; │
│ forgejo-cli = pkgs.writeScriptBin "forgejo-cli" '' │
│ #!${pkgs.runtimeShell} │
│ cd ${cfg.stateDir} │
│ sudo=exec │
│ if [[ "$USER" != forgejo ]]; then │
│ sudo='exec /run/wrappers/bin/sudo -u ${cfg.user} -g ${cfg.group} --pre │
│ serve-env=GITEA_WORK_DIR --preserve-env=GITEA_CUSTOM' │
│ fi │
│ # Note that these variable names will change │
│ export GITEA_WORK_DIR=${cfg.stateDir} │
│ export GITEA_CUSTOM=${cfg.customDir} │
│ $sudo ${getExe cfg.package} "$@" │
│ ''; │
│ in [ │
│ forgejo-cli │
│ ]; │
│ }; │
│ }; │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ NIX ────────────────────────────────┐
│ { │
│ config, │
│ lib, │
│ pkgs, │
│ ook, │
│ ... │
│ }: let │
│ inherit (lib) mkIf elem getExe optionalAtt │
│ rs optionals; │
│ │
│ inherit (config.ooknet.server) services do │
│ main; │
│ inherit (config.services) fail2ban; │
│ │
│ forgejoCfg = config.ooknet.server.forgejo; │
│ themeCfg = forgejoCfg.customTheme; │
│ themeFile = pkgs.writeText "theme-${themeC │
│ fg.name}.css" ( │
│ ook.lib.color.export.toForgejoTheme {sch │
│ eme = ook.themes.dark;} │
│ ); │
│ │
│ blankLogoSvg = pkgs.writeText "logo.svg" ' │
│ '<svg xmlns="http://www.w3.org/2000/svg" vie │
│ wBox="0 0 1 1"></svg>''; │
│ blankLogoPng = pkgs.runCommand "logo.png" │
│ {nativeBuildInputs = [pkgs.imagemagick];} '' │
│ magick -size 1x1 xc:transparent PNG32:$o │
│ ut │
│ ''; │
│ │
│ settingsFormat = lib.generators.toINI { │
│ mkKeyValue = lib.generators.mkKeyValueDe │
│ fault {} " = "; │
│ }; │
│ in { │
│ imports = [./options.nix]; │
│ │
│ config = mkIf (elem "forgejo" services) { │
│ networking.firewall.allowedTCPPorts = [2 │
│ 222]; │
│ │
│ systemd.tmpfiles.rules = let │
│ fj = config.services.forgejo; │
│ pub = "${fj.customDir}/public"; │
│ mkdir = p: "d ${p} 0750 ${fj.user} ${f │
│ j.group} - -"; │
│ in │
│ optionals (themeCfg.enable || forgejoC │
│ fg.hideLogo) [ │
│ (mkdir pub) │
│ (mkdir "${pub}/assets") │
│ ] │
│ ++ optionals themeCfg.enable [ │
│ (mkdir "${pub}/assets/css") │
│ "L+ ${pub}/assets/css/theme-${themeC │
│ fg.name}.css - - - - ${themeFile}" │
│ ] │
│ ++ optionals forgejoCfg.hideLogo [ │
│ (mkdir "${pub}/assets/img") │
│ "L+ ${pub}/assets/img/logo.svg - - - │
│ - ${blankLogoSvg}" │
│ "L+ ${pub}/assets/img/logo.png - - - │
│ - ${blankLogoPng}" │
│ ]; │
│ │
│ ooknet.server = { │
│ webserver.caddy.enable = true; │
│ database.postgresql.enable = true; │
│ }; │
│ services = { │
│ forgejo = { │
│ enable = true; │
│ │
│ settings = { │
│ server = { │
│ DOMAIN = "git.${domain}"; │
│ ROOT_URL = "https://git.${domain │
│ }"; │
│ HTTP_PORT = 3000; │
│ LANDING_PAGE = "explore"; │
│ │
│ START_SSH_SERVER = true; │
│ SSH_PORT = 2222; │
│ SSH_LISTEN_PORT = 2222; │
│ }; │
│ database = { │
│ type = "postgres"; │
│ createDatabase = true; │
│ }; │
│ service = { │
│ DISABLE_REGISTRATION = true; │
│ }; │
│ security = { │
│ INSTALL_LOCK = true; │
│ }; │
│ ui = mkIf themeCfg.enable ({ │
│ THEMES = "forgejo-auto,forgejo │
│ -light,forgejo-dark,${themeCfg.name}"; │
│ } │
│ // optionalAttrs themeCfg.defaul │
│ t { │
│ DEFAULT_THEME = themeCfg.name; │
│ }); │
│ }; │
│ }; │
│ caddy.virtualHosts = { │
│ "git.${domain}".extraConfig = '' │
│ header { │
│ Strict-Transport-Security "max-a │
│ ge=31536000;" │
│ X-XSS-Protection "1; mode=block" │
│ X-Frame-Options "DENY" │
│ X-Content-Type-Options "nosniff" │
│ -Server │
│ Referrer-Policy "no-referrer" │
│ } │
│ │
│ # rate limiting: 300 req/min per r │
│ eal client. {client_ip} resolves │
│ # the true client via trusted_prox │
│ ies, not the shared cloudflare │
│ # edge IP that {remote_host} gave │
│ (which made one user's page load │
│ # of assets 429 everyone behind th │
│ at edge) │
│ rate_limit { │
│ zone git { │
│ key {client_ip} │
│ events 300 │
│ window 1m │
│ } │
│ } │
│ │
│ # Handle proxying │
│ handle_path /* { │
│ reverse_proxy localhost:3000 { │
│ header_up X-Real-IP {client_ip │
│ } │
│ header_up X-Forwarded-For {cli │
│ ent_ip} │
│ header_up X-Forwarded-Proto {s │
│ cheme} │
│ } │
│ } │
│ ''; │
│ }; │
│ │
│ fail2ban.jails.forgejo.settings = { │
│ filter = "forgejo"; │
│ mode = "aggressive"; │
│ action = "nftables-multiport"; │
│ maxretry = 5; │
│ findTime = "24h"; │
│ bantime = "48h"; │
│ }; │
│ }; │
│ environment = { │
│ etc."fail2ban/filter.d/forgejo.conf".t │
│ ext = mkIf fail2ban.enable (settingsFormat { │
│ # fail2ban only reads [Definition] ( │
│ singular); and the regex needs │
│ # <HOST> to extract the IP to ban. f │
│ orgejo now logs the real client │
│ # IP (X-Real-IP {client_ip}) as "... │
│ from <ip>:<port>". │
│ Definition = { │
│ failregex = "^.*(Failed authentica │
│ tion attempt|invalid credentials|Attempted a │
│ ccess of unknown user).* from <HOST>"; │
│ journalmatch = "_SYSTEMD_UNIT=forg │
│ ejo.service"; │
│ }; │
│ }); │
│ │
│ # credit to TLATER │
│ # https://discourse.nixos.org/t/how-to │
│ -access-forgejo-cli/45370 │
│ systemPackages = let │
│ cfg = config.services.forgejo; │
│ forgejo-cli = pkgs.writeScriptBin "f │
│ orgejo-cli" '' │
│ #!${pkgs.runtimeShell} │
│ cd ${cfg.stateDir} │
│ sudo=exec │
│ if [[ "$USER" != forgejo ]]; then │
│ sudo='exec /run/wrappers/bin/sud │
│ o -u ${cfg.user} -g ${cfg.group} --preserve- │
│ env=GITEA_WORK_DIR --preserve-env=GITEA_CUST │
│ OM' │
│ fi │
│ # Note that these variable names w │
│ ill change │
│ export GITEA_WORK_DIR=${cfg.stateD │
│ ir} │
│ export GITEA_CUSTOM=${cfg.customDi │
│ r} │
│ $sudo ${getExe cfg.package} "$@" │
│ ''; │
│ in [ │
│ forgejo-cli │
│ ]; │
│ }; │
│ }; │
│ } │
└──────────────────────────────────────────────┘
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET