diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 8ecdc692ef5d..7dcf787b65fb 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1560,6 +1560,7 @@ ./services/web-apps/isso.nix ./services/web-apps/jirafeau.nix ./services/web-apps/jitsi-meet.nix + ./services/web-apps/karakeep.nix ./services/web-apps/kasmweb/default.nix ./services/web-apps/kanboard.nix ./services/web-apps/kavita.nix diff --git a/nixos/modules/services/web-apps/karakeep.nix b/nixos/modules/services/web-apps/karakeep.nix new file mode 100644 index 000000000000..14ec552a41d6 --- /dev/null +++ b/nixos/modules/services/web-apps/karakeep.nix @@ -0,0 +1,225 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.karakeep; + + karakeepEnv = lib.mkMerge [ + { DATA_DIR = "/var/lib/karakeep"; } + (lib.mkIf cfg.meilisearch.enable { + MEILI_ADDR = "http://127.0.0.1:${toString config.services.meilisearch.listenPort}"; + }) + (lib.mkIf cfg.browser.enable { + BROWSER_WEB_URL = "http://127.0.0.1:${toString cfg.browser.port}"; + }) + cfg.extraEnvironment + ]; + + environmentFiles = [ + "/var/lib/karakeep/settings.env" + ] ++ (lib.optional (cfg.environmentFile != null) cfg.environmentFile); +in +{ + options = { + services.karakeep = { + enable = lib.mkEnableOption "Enable the Karakeep service"; + package = lib.mkPackageOption pkgs "karakeep" { }; + + extraEnvironment = lib.mkOption { + description = '' + Environment variables to pass to Karakaeep. This is how most settings + can be configured. Changing DATA_DIR is possible but not supported. + + See https://docs.karakeep.app/configuration/ + ''; + type = lib.types.attrsOf lib.types.str; + default = { }; + example = lib.literalExpression '' + { + PORT = "1234"; + DISABLE_SIGNUPS = "true"; + DISABLE_NEW_RELEASE_CHECK = "true"; + } + ''; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + An optional path to an environment file that will be used in the web and workers + services. This is useful for loading private keys. + ''; + example = "/var/lib/karakeep/secrets.env"; + }; + + browser = { + enable = lib.mkOption { + description = '' + Enable the karakeep-browser service that runs a chromium instance in + the background with debugging ports exposed. This is necessary for + certain features like screenshots. + ''; + type = lib.types.bool; + default = true; + }; + port = lib.mkOption { + description = "The port the browser should run on."; + type = lib.types.port; + default = 9222; + }; + exe = lib.mkOption { + description = "The browser executable (must be Chrome-like)."; + type = lib.types.str; + default = "${pkgs.chromium}/bin/chromium"; + defaultText = lib.literalExpression "\${pkgs.chromium}/bin/chromium"; + example = lib.literalExpression "\${pkgs.google-chrome}/bin/google-chrome-stable"; + }; + }; + + meilisearch = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Enable Meilisearch and configure Karakeep to use it. Meilisearch is + required for text search. + ''; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + users.groups.karakeep = { }; + users.users.karakeep = { + isSystemUser = true; + group = "karakeep"; + }; + + services.meilisearch = lib.mkIf cfg.meilisearch.enable { + enable = true; + }; + + systemd.services.karakeep-init = { + description = "Initialize Karakeep Data"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + partOf = [ "karakeep.service" ]; + path = [ pkgs.openssl ]; + script = '' + umask 0077 + + if [ ! -f "$STATE_DIRECTORY/settings.env" ]; then + cat <"$STATE_DIRECTORY/settings.env" + # Generated by NixOS Karakeep module + MEILI_MASTER_KEY=$(openssl rand -base64 36) + NEXTAUTH_SECRET=$(openssl rand -base64 36) + EOF + fi + + export DATA_DIR="$STATE_DIRECTORY" + exec "${cfg.package}/lib/karakeep/migrate" + ''; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "karakeep"; + Group = "karakeep"; + StateDirectory = "karakeep"; + PrivateTmp = "yes"; + }; + }; + + systemd.services.karakeep-workers = { + description = "Karakeep Workers"; + wantedBy = [ "multi-user.target" ]; + after = [ + "network.target" + "karakeep-init.service" + ]; + partOf = [ "karakeep.service" ]; + path = [ + pkgs.monolith + pkgs.yt-dlp + ]; + environment = karakeepEnv; + serviceConfig = { + User = "karakeep"; + Group = "karakeep"; + ExecStart = "${cfg.package}/lib/karakeep/start-workers"; + StateDirectory = "karakeep"; + EnvironmentFile = environmentFiles; + PrivateTmp = "yes"; + }; + }; + + systemd.services.karakeep-web = { + description = "Karakeep Web"; + wantedBy = [ "multi-user.target" ]; + after = [ + "network.target" + "karakeep-init.service" + "karakeep-workers.service" + ]; + partOf = [ "karakeep.service" ]; + environment = karakeepEnv; + serviceConfig = { + ExecStart = "${cfg.package}/lib/karakeep/start-web"; + User = "karakeep"; + Group = "karakeep"; + StateDirectory = "karakeep"; + EnvironmentFile = environmentFiles; + PrivateTmp = "yes"; + }; + }; + + systemd.services.karakeep-browser = lib.mkIf cfg.browser.enable { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + partOf = [ "karakeep.service" ]; + script = '' + export HOME="$CACHE_DIRECTORY" + exec ${cfg.browser.exe} \ + --headless --no-sandbox --disable-gpu --disable-dev-shm-usage \ + --remote-debugging-address=127.0.0.1 \ + --remote-debugging-port=${toString cfg.browser.port} \ + --hide-scrollbars \ + --user-data-dir="$STATE_DIRECTORY" + ''; + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + + CacheDirectory = "karakeep-browser"; + StateDirectory = "karakeep-browser"; + + DevicePolicy = "closed"; + DynamicUser = true; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RestrictNamespaces = true; + RestrictRealtime = true; + }; + }; + }; + + meta = { + maintainers = [ lib.maintainers.three ]; + }; +}