ngi-nix / ngipkgs

Nix packages and services for projects supported through the NGI program
https://ngi-nix.github.io/ngipkgs
MIT License
34 stars 18 forks source link

Canaille #14

Open fricklerhandwerk opened 1 year ago

fricklerhandwerk commented 1 year ago

Canaille is a zero-knowledge opinionated identity server. Canaille aims to lower the barrier to entry for identity management, by providing a simple lightweight interoperable software focused on accessibility for end-users, administrators and contributors.

This is a very active project, maintainers are responsive. We could build on top of the user-provided package, but the real deal is in the service configuration.

erictapen commented 1 month ago

I'll work on this in the coming weeks, though I'll only write a package/module for Nixpkgs.

Also related: https://gitlab.com/yaal/canaille/-/issues/190

Janik-Haag commented 1 month ago

@erictapen I already finished a Canaille package as part of my work for SoN before I terminated my contract but didn't upstream it yet. I can post the derivation code here in a bit once I'm at my laptop. Furthermore, I started working on a module, but that isn't closed to being finished.

erictapen commented 1 month ago

@Janik-Haag Oh yeah I would be very interested in your package + module! A link to your git branch would work best for me, or ofc you just paste your code.

Janik-Haag commented 1 month ago

Here is the package definition I wrote, nothing really special except for the poetry name patching, which was copied from another package. I suppose the buildInputs should be renamed to the pep style ones, e.g. buildSystem and dependencies.

# package.nix
{ lib
, python3
, fetchFromGitLab
}:

python3.pkgs.buildPythonApplication rec {
  pname = "canaille";
  version = "0.0.53";
  pyproject = true;

  src = fetchFromGitLab {
    owner = "yaal";
    repo = "canaille";
    rev = version;
    hash = "sha256-ljRDbAdw4Ri1lIY98pB/KRw4s5aYTHFkV9NkOEbygpE=";
  };

  prePatch = ''
    substituteInPlace pyproject.toml \
      --replace "poetry.masonry" "poetry.core.masonry" \
      --replace "poetry>=" "poetry-core>="
  '';

  nativeBuildInputs = [
    python3.pkgs.babel
    python3.pkgs.poetry-core
  ];

  propagatedBuildInputs = with python3.pkgs; [
    flask
    flask-wtf
    pydantic-settings
    wtforms
  ];

  passthru.optional-dependencies = with python3.pkgs; {
    all = [
      authlib
      click
      email_validator
      flask-babel
      flask-themer
      passlib
      pycountry
      python-ldap
      pytz
      sentry-sdk
      sqlalchemy
      sqlalchemy-json
      sqlalchemy-utils
      toml
    ];
    front = [
      click
      email_validator
      flask-babel
      flask-themer
      pycountry
      pytz
      toml
    ];
    ldap = [
      python-ldap
    ];
    oidc = [
      authlib
    ];
    sentry = [
      sentry-sdk
    ];
    sql = [
      passlib
      sqlalchemy
      sqlalchemy-json
      sqlalchemy-utils
    ];
  };

  pythonImportsCheck = [ "canaille" ];

  meta = with lib; {
    description = "Lightweight identity and authorization management software";
    homepage = "https://gitlab.com/yaal/canaille/";
    changelog = "https://gitlab.com/yaal/canaille/-/blob/${src.rev}/CHANGES.rst";
    license = licenses.mit;
    maintainers = with maintainers; [ janik ];
    mainProgram = "canaille";
  };
}

The module is really bare-bones so far and the settings config isn't yet implemented.

# module.nix
{ config, lib, pkgs, ... }: let
  cfg = config.services.canaille;
  settingsFormat = pkgs.formats.toml { };
  inherit (lib) mkOption mkEnableOption mkPackageOption types getExe;
in {
  options.services.canaille = {
    enable = mkEnableOption "Wether to enable the canaille service";
    package = mkPackageOption pkgs "canaille" { };
    settings = mkOption {
      default = { };
      description = "Configuration for canaille.";
      type = types.submodule {
        freeformType = settingsFormat.type;
        options = {
        };
      };
    };
    environmentFiles = mkOption {
      default = [];
      type = types.listOf types.path;
      example = [ "/run/secrets/canaille.env" ];
      description = "Files to load as environment variables.";
    };
  };
  config = {
    systemd.services.canaille = {
      wants = [ "network.target" ];
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      description = "canaille";
      serviceConfig = {
        Type = "simple";
        Restart = "on-failure";
        RestartSec = 15;
        ExecStart = "${getExe cfg.package}";
        EnvironmentFile = cfg.environmentFiles;
      };
    };
  };
}

Here is a small test snippet which should get the canaille server to start: (upstream had an example config file iirc)

  nodes.canailleServer = {
    services.canaille = let
      envSecretsFile = builtins.toFile ''
        SECRET_KEY="change me before you go in production"
      '';
    in {
      enable = true;
      environmentFiles = [
        envSecretsFile
      ];
      settings = {
        CANAILLE = {
          NAME = "NixOS";
          JWT = {
            PRIVATE_KEY = "bar";
            PUBLIC_KEY = "foo";
          };
        };
      };
    };
  };

IIRC the next step is integrating all the ldap stuff. Also note that canaille shows its experimental status in some places, e.g. it crashes if you call it with --help but there isn't a valid settings file.

erictapen commented 1 month ago

Thanks! :heart:

azmeuk commented 1 month ago

Hi. Canaille dev here :wave: Let me know if there is anything I can do to help.

erictapen commented 1 month ago

Thanks @azmeuk, would be great if you could take a look: https://github.com/NixOS/nixpkgs/pull/333225

azmeuk commented 1 month ago

I read and added a few comments on the PR, but I did not actually tested it yet as I am not really used to Nix.