diff --git a/docs/redirects.json b/docs/redirects.json index cf8a6b7..5da3119 100644 --- a/docs/redirects.json +++ b/docs/redirects.json @@ -2978,6 +2978,54 @@ "services-karakeep-options-shb.karakeep.ldap.userGroup": [ "services-karakeep.html#services-karakeep-options-shb.karakeep.ldap.userGroup" ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.request": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.request" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.group": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.group" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.mode": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.mode" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.owner": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.owner" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.restartUnits": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.request.restartUnits" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.result": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.result" + ], + "services-karakeep-options-shb.karakeep.meilisearchMasterKey.result.path": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.meilisearchMasterKey.result.path" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.request": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.request" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.request.group": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.request.group" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.request.mode": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.request.mode" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.request.owner": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.request.owner" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.request.restartUnits": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.request.restartUnits" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.result": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.result" + ], + "services-karakeep-options-shb.karakeep.nextauthSecret.result.path": [ + "services-karakeep.html#services-karakeep-options-shb.karakeep.nextauthSecret.result.path" + ], "services-karakeep-options-shb.karakeep.port": [ "services-karakeep.html#services-karakeep-options-shb.karakeep.port" ], @@ -3011,30 +3059,6 @@ "services-karakeep-options-shb.karakeep.sso.enable": [ "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.enable" ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.request": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.request" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.group": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.group" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.mode": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.mode" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.owner": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.owner" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.restartUnits": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.request.restartUnits" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.result": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.result" - ], - "services-karakeep-options-shb.karakeep.sso.nextauthSecret.result.path": [ - "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.nextauthSecret.result.path" - ], "services-karakeep-options-shb.karakeep.sso.sharedSecret": [ "services-karakeep.html#services-karakeep-options-shb.karakeep.sso.sharedSecret" ], diff --git a/modules/services/karakeep.nix b/modules/services/karakeep.nix index 47e5ccf..024604c 100644 --- a/modules/services/karakeep.nix +++ b/modules/services/karakeep.nix @@ -96,22 +96,13 @@ in default = "one_factor"; }; - nextauthSecret = lib.mkOption { - description = "NextAuth secret."; - type = lib.types.submodule { - options = contracts.secret.mkRequester { - owner = "karakeep"; - restartUnits = [ "karakeep.service" ]; - }; - }; - }; - sharedSecret = lib.mkOption { description = "OIDC shared secret for Karakeep."; type = lib.types.submodule { options = contracts.secret.mkRequester { owner = "karakeep"; - restartUnits = [ "karakeep.service" ]; + # These services are the ones relying on the environment file containing the secrets. + restartUnits = [ "karakeep-init.service" "karakeep-workers.service" "karakeep-workers.service" ]; }; }; }; @@ -144,12 +135,35 @@ in }; }; }; + + nextauthSecret = lib.mkOption { + description = "NextAuth secret."; + type = lib.types.submodule { + options = contracts.secret.mkRequester { + owner = "karakeep"; + # These services are the ones relying on the environment file containing the secrets. + restartUnits = [ "karakeep-init.service" "karakeep-workers.service" "karakeep-workers.service" ]; + }; + }; + }; + + meilisearchMasterKey = lib.mkOption { + description = "Master key used to secure communication with Meilisearch."; + type = lib.types.submodule { + options = contracts.secret.mkRequester { + owner = "karakeep"; + # These services are the ones relying on the environment file containing the secrets. + restartUnits = [ "karakeep-init.service" "karakeep-workers.service" "karakeep-workers.service" ]; + }; + }; + }; }; config = (lib.mkMerge [ (lib.mkIf cfg.enable { services.karakeep = { enable = true; + meilisearch.enable = true; extraEnvironment = { PORT = toString cfg.port; @@ -163,6 +177,32 @@ in upstream = "http://127.0.0.1:${toString cfg.port}/"; } ]; + + # Piggybacking onto the upstream karakeep-init and replacing its script by ours. + # This is needed otherwise the MEILI_MASTER_KEY is generated randomly on first start + # instead of using the value from the cfg.meilisearchMasterKey option. + systemd.services.karakeep-init = { + script = lib.mkForce ((lib.shb.replaceSecrets { + userConfig = { + MEILI_MASTER_KEY = cfg.meilisearchMasterKey.result.path; + NEXTAUTH_SECRET.source = cfg.nextauthSecret.result.path; + } // lib.optionalAttrs cfg.sso.enable { + OAUTH_CLIENT_SECRET.source = cfg.sso.sharedSecret.result.path; + }; + resultPath = "/var/lib/karakeep/settings.env"; + generator = lib.shb.toEnvVar; + }) + '' + export DATA_DIR="$STATE_DIRECTORY" + exec ${config.services.karakeep.package}/lib/karakeep/migrate + ''); + }; + }) + (lib.mkIf cfg.enable { + services.meilisearch = { + dumplessUpgrade = true; + environment = "production"; + masterKeyFile = cfg.meilisearchMasterKey.result.path; + }; }) (lib.mkIf (cfg.enable && cfg.sso.enable) { shb.lldap.ensureGroups = { @@ -199,25 +239,6 @@ in OAUTH_CLIENT_ID = cfg.sso.clientID; OAUTH_SCOPE = "openid email profile"; }; - environmentFile = "/run/karakeep/secrets.env"; - }; - - systemd.tmpfiles.rules = [ - "d '/run/karakeep' 0750 root root - -" - ]; - systemd.services.karakeep-pre = { - script = lib.shb.replaceSecrets { - userConfig = { - NEXTAUTH_SECRET.source = cfg.sso.nextauthSecret.result.path; - OAUTH_CLIENT_SECRET.source = cfg.sso.sharedSecret.result.path; - }; - resultPath = "/run/karakeep/secrets.env"; - generator = lib.shb.toEnvVar; - }; - serviceConfig.Type = "oneshot"; - wantedBy = [ "multi-user.target" ]; - before = [ "karakeep-web.service" "karakeep-workers.service" ]; - requiredBy = [ "karakeep-web.service" "karakeep-workers.service" ]; }; }) ]); diff --git a/modules/services/karakeep/docs/default.md b/modules/services/karakeep/docs/default.md index 82bb5de..caef1c4 100644 --- a/modules/services/karakeep/docs/default.md +++ b/modules/services/karakeep/docs/default.md @@ -17,6 +17,7 @@ It integrates well with [Ollama][]. - Declarative [SSO](#services-karakeep-options-shb.karakeep.sso) Configuration. - When SSO is enabled, login with user and password is disabled. - Registration is enabled through SSO. +- Meilisearch configured with production environment and master key. - Access through [subdomain](#services-karakeep-options-shb.karakeep.subdomain) using reverse proxy. - Access through [HTTPS](#services-karakeep-options-shb.karakeep.ssl) using reverse proxy. - [Backup](#services-karakeep-options-shb.karakeep.sso) through the [backup block](./blocks-backup.html). @@ -39,17 +40,18 @@ The following snippet assumes a few blocks have been setup already: ssl = config.shb.certs.certs.letsencrypt.${domain}; + nextauthSecret.result = config.shb.sops.secret.nextauthSecret.result; + sso = { enable = true; authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; - nextauthSecret.result = config.shb.sops.secret.nextauthSecret.result; sharedSecret.result = config.shb.sops.secret.oidcSecret.result; sharedSecretForAuthelia.result = config.shb.sops.secret.oidcAutheliaSecret.result; }; }; - shb.sops.secret.nextauthSecret.request = config.shb.karakeep.sso.sharedSecret.request; + shb.sops.secret.nextauthSecret.request = config.shb.karakeep.nextauthSecret.request; shb.sops.secret.oidcSecret.request = config.shb.karakeep.sso.sharedSecret.request; shb.sops.secret.oidcAutheliaSecret = { request = config.shb.karakeep.sso.sharedSecretForAuthelia.request; diff --git a/test/services/karakeep.nix b/test/services/karakeep.nix index 842b502..e44f637 100644 --- a/test/services/karakeep.nix +++ b/test/services/karakeep.nix @@ -8,6 +8,8 @@ let commonTestScript = testLib.mkScripts { hasSSL = { node, ... }: !(isNull node.config.shb.karakeep.ssl); waitForServices = { ... }: [ + "karakeep-init.service" + "karakeep-browser.service" "karakeep-web.service" "karakeep-workers.service" "nginx.service" @@ -32,6 +34,18 @@ let shb.karakeep = { enable = true; inherit (config.test) subdomain domain; + + nextauthSecret.result = config.shb.hardcodedsecret.nextauthSecret.result; + meilisearchMasterKey.result = config.shb.hardcodedsecret.meilisearchMasterKey.result; + }; + + shb.hardcodedsecret.nextauthSecret = { + request = config.shb.karakeep.nextauthSecret.request; + settings.content = nextauthSecret; + }; + shb.hardcodedsecret.meilisearchMasterKey = { + request = config.shb.karakeep.meilisearchMasterKey.request; + settings.content = "meilisearch-master-key"; }; networking.hosts = { @@ -107,16 +121,11 @@ let authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; clientID = "karakeep"; - nextauthSecret.result = config.shb.hardcodedsecret.nextauthSecret.result; sharedSecret.result = config.shb.hardcodedsecret.oidcSecret.result; sharedSecretForAuthelia.result = config.shb.hardcodedsecret.oidcAutheliaSecret.result; }; }; - shb.hardcodedsecret.nextauthSecret = { - request = config.shb.karakeep.sso.nextauthSecret.request; - settings.content = nextauthSecret; - }; shb.hardcodedsecret.oidcSecret = { request = config.shb.karakeep.sso.sharedSecret.request; settings.content = oidcSecret;