Unverified Commit f2b767ea authored by github-actions[bot]'s avatar github-actions[bot] Committed by GitHub
Browse files

Merge master into staging-next

parents 3b1652aa 49ba5e4e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@

- [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr), proxy server to bypass Cloudflare protection. Available as [services.flaresolverr](#opt-services.flaresolverr.enable) service.

- [Gancio](https://gancio.org/), a shared agenda for local communities. Available as [services.gancio](#opt-services.gancio.enable).

- [Goatcounter](https://www.goatcounter.com/), Easy web analytics. No tracking of personal data. Available as [services.goatcounter](options.html#opt-services.goatcocunter.enable).

- [UWSM](https://github.com/Vladimir-csp/uwsm), a wayland session manager to wrap Wayland Compositors into useful systemd units such as `graphical-session.target`. Available as [programs.uwsm](#opt-programs.uwsm.enable).
@@ -458,6 +460,8 @@

- Kanidm previously had an incorrect systemd service type, causing dependent units with an `after` and `requires` directive to start before `kanidm*` finished startup. The module has now been updated in line with upstream recommendations.

- The kubelet configuration file can now be amended with arbitrary additional content using the `services.kubernetes.kubelet.extraConfig` option.

- To facilitate dependency injection, the `imgui` package now builds a static archive using vcpkg' CMake rules.
  The derivation now installs "impl" headers selectively instead of by a wildcard.
  Use `imgui.src` if you just want to access the unpacked sources.
+1 −0
Original line number Diff line number Diff line
@@ -1411,6 +1411,7 @@
  ./services/web-apps/fluidd.nix
  ./services/web-apps/freshrss.nix
  ./services/web-apps/galene.nix
  ./services/web-apps/gancio.nix
  ./services/web-apps/gerrit.nix
  ./services/web-apps/glance.nix
  ./services/web-apps/gotify-server.nix
+7 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ let
    // lib.optionalAttrs (cfg.clusterDomain != "")  { clusterDomain = cfg.clusterDomain; }
    // lib.optionalAttrs (cfg.clusterDns != [])     { clusterDNS = cfg.clusterDns; }
    // lib.optionalAttrs (cfg.featureGates != {})   { featureGates = cfg.featureGates; }
    // lib.optionalAttrs (cfg.extraConfig != {})    cfg.extraConfig
  ));

  manifestPath = "kubernetes/manifests";
@@ -184,6 +185,12 @@ in
      type = separatedString " ";
    };

    extraConfig = mkOption {
      description = "Kubernetes kubelet extra configuration file entries.";
      default = {};
      type = attrsOf attrs;
    };

    featureGates = mkOption {
      description = "Attribute set of feature gate";
      default = top.featureGates;
+29 −3
Original line number Diff line number Diff line
@@ -106,13 +106,39 @@ in
      '';

      settings = lib.mkOption {
        type = lib.types.submodule { freeformType = settingsFormat.type; };
        default = {};
        type = lib.types.submodule {
          freeformType = settingsFormat.type;
          options = {
            global.security = lib.mkOption {
              type = lib.types.enum [ "auto" "user" "domain" "ads" ];
              default = "user";
              description = "Samba security type.";
            };
            global."invalid users" = lib.mkOption {
              type = lib.types.listOf lib.types.str;
              default = [ "root" ];
              description = "List of users who are denied to login via Samba.";
              apply = x: lib.concatStringsSep " " x;
            };
            global."passwd program" = lib.mkOption {
              type = lib.types.str;
              default = "/run/wrappers/bin/passwd %u";
              description = "Path to a program that can be used to set UNIX user passwords.";
            };
          };
        };
        default = {
          "global" = {
            "security" = "user";
            "passwd program" = "/run/wrappers/bin/passwd %u";
            "invalid users" = [ "root" ];
          };
        };
        example = {
          "global" = {
            "security" = "user";
            "passwd program" = "/run/wrappers/bin/passwd %u";
            "invalid users" = "root";
            "invalid users" = [ "root" ];
          };
          "public" = {
            "path" = "/srv/public";
+280 −0
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.services.gancio;
  settingsFormat = pkgs.formats.json { };
  inherit (lib)
    mkEnableOption
    mkPackageOption
    mkOption
    types
    literalExpression
    mkIf
    optional
    mapAttrsToList
    concatStringsSep
    concatMapStringsSep
    getExe
    mkMerge
    mkDefault
    ;
in
{
  options.services.gancio = {
    enable = mkEnableOption "Gancio, a shared agenda for local communities";

    package = mkPackageOption pkgs "gancio" { };

    plugins = mkOption {
      type = with types; listOf package;
      default = [ ];
      example = literalExpression "[ pkgs.gancioPlugins.telegram-bridge ]";
      description = ''
        Paths of gancio plugins to activate (linked under $WorkingDirectory/plugins/).
      '';
    };

    user = mkOption {
      type = types.str;
      description = "The user (and PostgreSQL database name) used to run the gancio server";
      default = "gancio";
    };

    settings = mkOption rec {
      type = types.submodule {
        freeformType = settingsFormat.type;
        options = {
          hostname = mkOption {
            type = types.str;
            description = "The domain name under which the server is reachable.";
          };
          baseurl = mkOption {
            type = types.str;
            default = "";
            example = "/gancio";
            description = "The URL path under which the server is reachable.";
          };
          server = {
            host = mkOption {
              type = types.str;
              default = "localhost";
              example = "::";
              description = ''
                The address (IPv4, IPv6 or DNS) for the gancio server to listen on.
              '';
            };
            port = mkOption {
              type = types.port;
              default = 13120;
              description = ''
                Port number of the gancio server to listen on.
              '';
            };
          };
          db = {
            dialect = mkOption {
              type = types.enum [
                "sqlite"
                "postgres"
              ];
              default = "sqlite";
              description = ''
                The database dialect to use
              '';
            };
            storage = mkOption {
              description = ''
                Location for the SQLite database.
              '';
              readOnly = true;
              type = types.nullOr types.str;
              default = if cfg.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null;
              defaultText = ''
                if cfg.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null
              '';
            };
            host = mkOption {
              description = ''
                Connection string for the PostgreSQL database
              '';
              readOnly = true;
              type = types.nullOr types.str;
              default = if cfg.settings.db.dialect == "postgres" then "/run/postgresql" else null;
              defaultText = ''
                if cfg.settings.db.dialect == "postgres" then "/run/postgresql" else null
              '';
            };
            database = mkOption {
              description = ''
                Name of the PostgreSQL database
              '';
              readOnly = true;
              type = types.nullOr types.str;
              default = if cfg.settings.db.dialect == "postgres" then cfg.user else null;
              defaultText = ''
                if cfg.settings.db.dialect == "postgres" then cfg.user else null
              '';
            };
          };
          log_level = mkOption {
            description = "Gancio log level.";
            type = types.enum [
              "debug"
              "info"
              "warning"
              "error"
            ];
            default = "info";
          };
          # FIXME upstream proper journald logging
          log_path = mkOption {
            description = "Directory Gancio logs into";
            readOnly = true;
            type = types.str;
            default = "/var/log/gancio";
          };
        };
      };
      description = ''
        Configuration for Gancio, see <https://gancio.org/install/config> for supported values.
      '';
    };

    userLocale = mkOption {
      type = with types; attrsOf (attrsOf (attrsOf str));
      default = { };
      example = {
        en.register.description = "My new registration page description";
      };
      description = ''
        Override default locales within gancio.
        See [https://framagit.org/les/gancio/tree/master/locales](default languages and locales).
      '';
    };

    nginx = mkOption {
      type = types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; });
      default = { };
      example = {
        enableACME = true;
        forceSSL = true;
      };
      description = "Extra configuration for the nginx virtual host of gancio.";
    };
  };

  config = mkIf cfg.enable {
    environment.systemPackages = [ cfg.package ];

    users.users.gancio = lib.mkIf (cfg.user == "gancio") {
      isSystemUser = true;
      group = cfg.user;
      home = "/var/lib/gancio";
    };
    users.groups.gancio = lib.mkIf (cfg.user == "gancio") { };

    systemd.tmpfiles.settings."10-gancio" =
      let
        rules = {
          mode = "0755";
          user = cfg.user;
          group = config.users.users.${cfg.user}.group;
        };
      in
      {
        "/var/lib/gancio/user_locale".d = rules;
        "/var/lib/gancio/plugins".d = rules;
      };

    systemd.services.gancio =
      let
        configFile = settingsFormat.generate "gancio-config.json" cfg.settings;
      in
      {
        description = "Gancio server";
        documentation = [ "https://gancio.org/" ];

        wantedBy = [ "multi-user.target" ];
        after = [
          "network.target"
        ] ++ optional (cfg.settings.db.dialect == "postgres") "postgresql.service";

        environment = {
          NODE_ENV = "production";
        };

        preStart = ''
          # We need this so the gancio executable run by the user finds the right settings.
          ln -sf ${configFile} config.json

          rm -f user_locale/*
          ${concatStringsSep "\n" (
            mapAttrsToList (
              l: c: "ln -sf ${settingsFormat.generate "gancio-${l}-locale.json" c} user_locale/${l}.json"
            ) cfg.userLocale
          )}

          rm -f plugins/*
          ${concatMapStringsSep "\n" (p: "ln -sf ${p} plugins/") cfg.plugins}
        '';

        serviceConfig = {
          ExecStart = "${getExe cfg.package} start ${configFile}";
          StateDirectory = "gancio";
          WorkingDirectory = "/var/lib/gancio";
          LogsDirectory = "gancio";
          User = cfg.user;
          # hardening
          RestrictRealtime = true;
          RestrictNamespaces = true;
          LockPersonality = true;
          ProtectKernelModules = true;
          ProtectKernelTunables = true;
          ProtectKernelLogs = true;
          ProtectControlGroups = true;
          ProtectClock = true;
          RestrictSUIDSGID = true;
          SystemCallArchitectures = "native";
          CapabilityBoundingSet = "";
          ProtectProc = "invisible";
        };
      };

    services.postgresql = mkIf (cfg.settings.db.dialect == "postgres") {
      enable = true;
      ensureDatabases = [ cfg.user ];
      ensureUsers = [
        {
          name = cfg.user;
          ensureDBOwnership = true;
        }
      ];
    };

    services.nginx = {
      enable = true;
      virtualHosts."${cfg.settings.hostname}" = mkMerge [
        cfg.nginx
        {
          enableACME = mkDefault true;
          forceSSL = mkDefault true;
          locations = {
            "/" = {
              index = "index.html";
              tryFiles = "$uri $uri @proxy";
            };
            "@proxy" = {
              proxyWebsockets = true;
              proxyPass = "http://${cfg.settings.server.host}:${toString cfg.settings.server.port}";
              recommendedProxySettings = true;
            };
          };
        }
      ];
    };
  };
}
Loading