Unverified Commit e248589c authored by Kerstin's avatar Kerstin Committed by GitHub
Browse files

gancio: init at 1.19.0 (#279011)

parents 73946e2f f5e44554
Loading
Loading
Loading
Loading
+2 −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).
+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
+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;
            };
          };
        }
      ];
    };
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -364,6 +364,7 @@ in {
  ft2-clone = handleTest ./ft2-clone.nix {};
  legit = handleTest ./legit.nix {};
  mimir = handleTest ./mimir.nix {};
  gancio = handleTest ./gancio.nix {};
  garage = handleTest ./garage {};
  gemstash = handleTest ./gemstash.nix {};
  geoserver = runTest ./geoserver.nix;

nixos/tests/gancio.nix

0 → 100644
+87 −0
Original line number Diff line number Diff line
import ./make-test-python.nix (
  { pkgs, ... }:
  let
    extraHosts = ''
      192.168.13.12 agenda.example.com
    '';
  in
  {
    name = "gancio";
    meta.maintainers = with pkgs.lib.maintainers; [ jbgi ];

    nodes = {
      server =
        { pkgs, ... }:
        {
          networking = {
            interfaces.eth1 = {
              ipv4.addresses = [
                {
                  address = "192.168.13.12";
                  prefixLength = 24;
                }
              ];
            };
            inherit extraHosts;
            firewall.allowedTCPPorts = [ 80 ];
          };
          environment.systemPackages = [ pkgs.gancio ];
          services.gancio = {
            enable = true;
            settings = {
              hostname = "agenda.example.com";
              db.dialect = "postgres";
            };
            plugins = [ pkgs.gancioPlugins.telegram-bridge ];
            userLocale = {
              en = {
                register = {
                  description = "My new registration page description";
                };
              };
            };
            nginx = {
              enableACME = false;
              forceSSL = false;
            };
          };
        };

      client =
        { pkgs, ... }:
        {
          environment.systemPackages = [ pkgs.jq ];
          networking = {
            interfaces.eth1 = {
              ipv4.addresses = [
                {
                  address = "192.168.13.1";
                  prefixLength = 24;
                }
              ];
            };
            inherit extraHosts;
          };
        };
    };

    testScript = ''
      start_all()

      server.wait_for_unit("postgresql")
      server.wait_for_unit("gancio")
      server.wait_for_unit("nginx")
      server.wait_for_open_port(13120)
      server.wait_for_open_port(80)

      # Check can create user via cli
      server.succeed("cd /var/lib/gancio && sudo -u gancio gancio users create admin dummy admin")

      # Check event list is returned
      client.wait_until_succeeds("curl --verbose --fail-with-body http://agenda.example.com/api/events", timeout=30)

      server.shutdown()
      client.shutdown()
    '';
  }
)
Loading