283 lines
9.0 KiB
Nix
283 lines
9.0 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.services.meilisearch;
|
|
|
|
settingsFormat = pkgs.formats.toml { };
|
|
|
|
# These secrets are used in the config file and can be set to paths.
|
|
secrets-with-path =
|
|
builtins.map
|
|
(
|
|
{ environment, name }:
|
|
{
|
|
inherit name environment;
|
|
setting = cfg.settings.${name};
|
|
}
|
|
)
|
|
[
|
|
{
|
|
environment = "MEILI_SSL_CERT_PATH";
|
|
name = "ssl_cert_path";
|
|
}
|
|
{
|
|
environment = "MEILI_SSL_KEY_PATH";
|
|
name = "ssl_key_path";
|
|
}
|
|
{
|
|
environment = "MEILI_SSL_AUTH_PATH";
|
|
name = "ssl_auth_path";
|
|
}
|
|
{
|
|
environment = "MEILI_SSL_OCSP_PATH";
|
|
name = "ssl_ocsp_path";
|
|
}
|
|
];
|
|
|
|
# We also handle `master_key` separately.
|
|
# It cannot be set to a path, so we template it.
|
|
master-key-placeholder = "@MASTER_KEY@";
|
|
|
|
configFile = settingsFormat.generate "config.toml" (
|
|
builtins.removeAttrs (
|
|
if cfg.masterKeyFile != null then
|
|
cfg.settings // { master_key = master-key-placeholder; }
|
|
else
|
|
builtins.removeAttrs cfg.settings [ "master_key" ]
|
|
) (map (secret: secret.name) secrets-with-path)
|
|
);
|
|
|
|
in
|
|
{
|
|
meta.maintainers = with lib.maintainers; [
|
|
Br1ght0ne
|
|
happysalada
|
|
];
|
|
meta.doc = ./meilisearch.md;
|
|
|
|
imports = [
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "meilisearch" "environment" ]
|
|
[ "services" "meilisearch" "settings" "env" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "meilisearch" "logLevel" ]
|
|
[ "services" "meilisearch" "settings" "log_level" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "meilisearch" "noAnalytics" ]
|
|
[ "services" "meilisearch" "settings" "no_analytics" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "meilisearch" "maxIndexSize" ]
|
|
[ "services" "meilisearch" "settings" "max_index_size" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "meilisearch" "payloadSizeLimit" ]
|
|
[ "services" "meilisearch" "settings" "http_payload_size_limit" ]
|
|
)
|
|
(lib.mkRenamedOptionModule
|
|
[ "services" "meilisearch" "dumplessUpgrade" ]
|
|
[ "services" "meilisearch" "settings" "experimental_dumpless_upgrade" ]
|
|
)
|
|
(lib.mkRemovedOptionModule [ "services" "meilisearch" "masterKeyEnvironmentFile" ] ''
|
|
Use `services.meilisearch.masterKeyFile` instead. It does not require you to prefix the file with "MEILI_MASTER_KEY=".
|
|
If you were abusing this option to set other options, you can now configure them with `services.meilisearch.settings`.
|
|
'')
|
|
];
|
|
|
|
options.services.meilisearch = {
|
|
enable = lib.mkEnableOption "Meilisearch - a RESTful search API";
|
|
|
|
package = lib.mkPackageOption pkgs "meilisearch" {
|
|
extraDescription = ''
|
|
Use this if you require specific features to be enabled. The default package has no features.
|
|
'';
|
|
};
|
|
|
|
listenAddress = lib.mkOption {
|
|
default = "localhost";
|
|
type = lib.types.str;
|
|
description = ''
|
|
The IP address that Meilisearch will listen on.
|
|
|
|
It can also be a hostname like "localhost". If it resolves to an IPv4 and IPv6 address, Meilisearch will listen on both.
|
|
'';
|
|
};
|
|
|
|
listenPort = lib.mkOption {
|
|
default = 7700;
|
|
type = lib.types.port;
|
|
description = ''
|
|
The port that Meilisearch will listen on.
|
|
'';
|
|
};
|
|
|
|
masterKeyFile = lib.mkOption {
|
|
description = ''
|
|
Path to file which contains the master key.
|
|
By doing so, all routes will be protected and will require a key to be accessed.
|
|
If no master key is provided, all routes can be accessed without requiring any key.
|
|
'';
|
|
default = null;
|
|
type = lib.types.nullOr lib.types.path;
|
|
};
|
|
|
|
settings = lib.mkOption {
|
|
description = ''
|
|
Configuration settings for Meilisearch.
|
|
Look at the documentation for available options:
|
|
https://github.com/meilisearch/meilisearch/blob/main/config.toml
|
|
https://www.meilisearch.com/docs/learn/self_hosted/configure_meilisearch_at_launch#all-instance-options
|
|
'';
|
|
|
|
default = { };
|
|
|
|
type = lib.types.submodule {
|
|
freeformType = settingsFormat.type;
|
|
|
|
imports = builtins.map (secret: {
|
|
# give them proper types, just so they're easier to consume from this file
|
|
options.${secret.name} = lib.mkOption {
|
|
# but they should not show up in documentation as special in any way.
|
|
visible = false;
|
|
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = null;
|
|
};
|
|
}) secrets-with-path;
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = !cfg.settings ? master_key;
|
|
message = ''
|
|
Do not set `services.meilisearch.settings.master_key` in your configuration.
|
|
Use `services.meilisearch.masterKeyFile` instead.
|
|
'';
|
|
}
|
|
];
|
|
|
|
services.meilisearch.settings = {
|
|
# we use `listenAddress` and `listenPort` to derive the `http_addr` setting.
|
|
# this is the only setting we treat like this.
|
|
# we do this because some dependent services like Misskey/Sharkey need separate host,port for no good reason.
|
|
http_addr = "${cfg.listenAddress}:${toString cfg.listenPort}";
|
|
|
|
# upstream's default for `db_path` is `/var/lib/meilisearch/data.ms/`, but ours is different for no reason.
|
|
db_path = lib.mkDefault "/var/lib/meilisearch";
|
|
# these are equivalent to the upstream defaults, because we set a working directory.
|
|
# they are only set here for consistency with `db_path`.
|
|
dump_dir = lib.mkDefault "/var/lib/meilisearch/dumps";
|
|
snapshot_dir = lib.mkDefault "/var/lib/meilisearch/snapshots";
|
|
|
|
# this is intentionally different from upstream's default.
|
|
no_analytics = lib.mkDefault true;
|
|
};
|
|
|
|
warnings = lib.optional (lib.versionOlder cfg.package.version "1.12") ''
|
|
Meilisearch 1.11 will be removed in NixOS 25.11. As it was the last
|
|
version not to support dumpless upgrades, you will have to manually
|
|
migrate your data before that. Instructions can be found at
|
|
https://www.meilisearch.com/docs/learn/update_and_migration/updating#using-a-dump
|
|
and afterwards, you can set `services.meilisearch.package = pkgs.meilisearch;`
|
|
to use the latest version.
|
|
'';
|
|
|
|
services.meilisearch.package = lib.mkDefault (
|
|
if lib.versionAtLeast config.system.stateVersion "25.05" then
|
|
pkgs.meilisearch
|
|
else
|
|
pkgs.meilisearch_1_11
|
|
);
|
|
|
|
# used to restore dumps
|
|
environment.systemPackages = [ cfg.package ];
|
|
|
|
systemd.services.meilisearch = {
|
|
description = "Meilisearch daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network.target" ];
|
|
|
|
preStart = lib.mkMerge [
|
|
''
|
|
install -m 700 '${configFile}' "$RUNTIME_DIRECTORY/config.toml"
|
|
''
|
|
(lib.mkIf (cfg.masterKeyFile != null) ''
|
|
${lib.getExe pkgs.replace-secret} '${master-key-placeholder}' "$CREDENTIALS_DIRECTORY/master_key" "$RUNTIME_DIRECTORY/config.toml"
|
|
'')
|
|
];
|
|
|
|
environment = builtins.listToAttrs (
|
|
builtins.map (secret: {
|
|
name = secret.environment;
|
|
value = lib.mkIf (secret.setting != null) "%d/${secret.name}";
|
|
}) secrets-with-path
|
|
);
|
|
|
|
serviceConfig = {
|
|
LoadCredential = lib.mkMerge (
|
|
[
|
|
(lib.mkIf (cfg.masterKeyFile != null) [ "master_key:${cfg.masterKeyFile}" ])
|
|
]
|
|
++ builtins.map (
|
|
secret: lib.mkIf (secret.setting != null) [ "${secret.name}:${secret.setting}" ]
|
|
) secrets-with-path
|
|
);
|
|
ExecStart = "${lib.getExe cfg.package} --config-file-path \${RUNTIME_DIRECTORY}/config.toml";
|
|
DynamicUser = true;
|
|
StateDirectory = "meilisearch";
|
|
WorkingDirectory = "%S/meilisearch";
|
|
RuntimeDirectory = "meilisearch";
|
|
RuntimeDirectoryMode = "0700";
|
|
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
ProtectClock = true;
|
|
ProtectHostname = true;
|
|
ProtectKernelLogs = true;
|
|
ProtectKernelModules = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectControlGroups = true;
|
|
PrivateTmp = true;
|
|
PrivateMounts = true;
|
|
PrivateUsers = true;
|
|
PrivateDevices = true;
|
|
RestrictRealtime = true;
|
|
RestrictNamespaces = true;
|
|
RestrictSUIDSGID = true;
|
|
LockPersonality = true;
|
|
MemoryDenyWriteExecute = true;
|
|
|
|
ProcSubset = "pid";
|
|
ProtectProc = "invisible";
|
|
|
|
NoNewPrivileges = true;
|
|
|
|
# Meilisearch does not support listening on AF_UNIX sockets,
|
|
# so we currently restrict it to only AF_INET and AF_INET6.
|
|
RestrictAddressFamilies = [
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
];
|
|
|
|
CapabilityBoundingSet = "";
|
|
SystemCallArchitectures = "native";
|
|
SystemCallFilter = [
|
|
"@system-service"
|
|
"~@privileged @resources"
|
|
];
|
|
|
|
UMask = "0077";
|
|
};
|
|
};
|
|
};
|
|
}
|