OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
main @ 165 LINES
 
[ HISTORY ]  [ UP ]
 

{
  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
      ];
    };
  };
}

{
  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