nixos/redis: enable multiple instances of redis-server
This commit is contained in:
parent
f40283cf62
commit
7475554372
@ -87,7 +87,32 @@
|
||||
</section>
|
||||
<section xml:id="sec-release-22.05-notable-changes">
|
||||
<title>Other Notable Changes</title>
|
||||
<para>
|
||||
</para>
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
The option
|
||||
<link linkend="opt-services.redis.servers">services.redis.servers</link>
|
||||
was added to support per-application
|
||||
<literal>redis-server</literal> which is more secure since
|
||||
Redis databases are only mere key prefixes without any
|
||||
configuration or ACL of their own. Backward-compatibility is
|
||||
preserved by mapping old
|
||||
<literal>services.redis.settings</literal> to
|
||||
<literal>services.redis.servers."".settings</literal>,
|
||||
but you are strongly encouraged to name each
|
||||
<literal>redis-server</literal> instance after the application
|
||||
using it, instead of keeping that nameless one. Except for the
|
||||
nameless
|
||||
<literal>services.redis.servers.""</literal> still
|
||||
accessible at <literal>127.0.0.1:6379</literal>, and to the
|
||||
members of the Unix group <literal>redis</literal> through the
|
||||
Unix socket <literal>/run/redis/redis.sock</literal>, all
|
||||
other <literal>services.redis.servers.${serverName}</literal>
|
||||
are only accessible by default to the members of the Unix
|
||||
group <literal>redis-${serverName}</literal> through the Unix
|
||||
socket <literal>/run/redis-${serverName}/redis.sock</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -35,3 +35,19 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
Please switch to `claws-mail`, which is Claws Mail's latest release based on GTK+3 and Python 3.
|
||||
|
||||
## Other Notable Changes {#sec-release-22.05-notable-changes}
|
||||
|
||||
- The option [services.redis.servers](#opt-services.redis.servers) was added
|
||||
to support per-application `redis-server` which is more secure since Redis databases
|
||||
are only mere key prefixes without any configuration or ACL of their own.
|
||||
Backward-compatibility is preserved by mapping old `services.redis.settings`
|
||||
to `services.redis.servers."".settings`, but you are strongly encouraged
|
||||
to name each `redis-server` instance after the application using it,
|
||||
instead of keeping that nameless one.
|
||||
Except for the nameless `services.redis.servers.""`
|
||||
still accessible at `127.0.0.1:6379`,
|
||||
and to the members of the Unix group `redis`
|
||||
through the Unix socket `/run/redis/redis.sock`,
|
||||
all other `services.redis.servers.${serverName}`
|
||||
are only accessible by default
|
||||
to the members of the Unix group `redis-${serverName}`
|
||||
through the Unix socket `/run/redis-${serverName}/redis.sock`.
|
||||
|
@ -5,17 +5,18 @@ with lib;
|
||||
let
|
||||
cfg = config.services.redis;
|
||||
|
||||
ulimitNofile = cfg.maxclients + 32;
|
||||
|
||||
mkValueString = value:
|
||||
if value == true then "yes"
|
||||
else if value == false then "no"
|
||||
else generators.mkValueStringDefault { } value;
|
||||
|
||||
redisConfig = pkgs.writeText "redis.conf" (generators.toKeyValue {
|
||||
redisConfig = settings: pkgs.writeText "redis.conf" (generators.toKeyValue {
|
||||
listsAsDuplicateKeys = true;
|
||||
mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " ";
|
||||
} cfg.settings);
|
||||
} settings);
|
||||
|
||||
redisName = name: "redis" + optionalString (name != "") ("-"+name);
|
||||
enabledServers = filterAttrs (name: conf: conf.enable) config.services.redis.servers;
|
||||
|
||||
in {
|
||||
imports = [
|
||||
@ -24,7 +25,28 @@ in {
|
||||
(mkRemovedOptionModule [ "services" "redis" "dbFilename" ] "The redis module now uses /var/lib/redis/dump.rdb as database dump location.")
|
||||
(mkRemovedOptionModule [ "services" "redis" "appendOnlyFilename" ] "This option was never used.")
|
||||
(mkRemovedOptionModule [ "services" "redis" "pidFile" ] "This option was removed.")
|
||||
(mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.settings instead.")
|
||||
(mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.servers.*.settings instead.")
|
||||
(mkRenamedOptionModule [ "services" "redis" "enable"] [ "services" "redis" "servers" "" "enable" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "port"] [ "services" "redis" "servers" "" "port" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "openFirewall"] [ "services" "redis" "servers" "" "openFirewall" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "bind"] [ "services" "redis" "servers" "" "bind" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "unixSocket"] [ "services" "redis" "servers" "" "unixSocket" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "unixSocketPerm"] [ "services" "redis" "servers" "" "unixSocketPerm" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "logLevel"] [ "services" "redis" "servers" "" "logLevel" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "logfile"] [ "services" "redis" "servers" "" "logfile" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "syslog"] [ "services" "redis" "servers" "" "syslog" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "databases"] [ "services" "redis" "servers" "" "databases" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "maxclients"] [ "services" "redis" "servers" "" "maxclients" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "save"] [ "services" "redis" "servers" "" "save" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "slaveOf"] [ "services" "redis" "servers" "" "slaveOf" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "masterAuth"] [ "services" "redis" "servers" "" "masterAuth" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "requirePass"] [ "services" "redis" "servers" "" "requirePass" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "requirePassFile"] [ "services" "redis" "servers" "" "requirePassFile" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "appendOnly"] [ "services" "redis" "servers" "" "appendOnly" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "appendFsync"] [ "services" "redis" "servers" "" "appendFsync" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "slowLogLogSlowerThan"] [ "services" "redis" "servers" "" "slowLogLogSlowerThan" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "slowLogMaxLen"] [ "services" "redis" "servers" "" "slowLogMaxLen" ])
|
||||
(mkRenamedOptionModule [ "services" "redis" "settings"] [ "services" "redis" "servers" "" "settings" ])
|
||||
];
|
||||
|
||||
###### interface
|
||||
@ -32,18 +54,6 @@ in {
|
||||
options = {
|
||||
|
||||
services.redis = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable the Redis server. Note that the NixOS module for
|
||||
Redis disables kernel support for Transparent Huge Pages (THP),
|
||||
because this features causes major performance problems for Redis,
|
||||
e.g. (https://redis.io/topics/latency).
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.redis;
|
||||
@ -51,176 +61,226 @@ in {
|
||||
description = "Which Redis derivation to use.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 6379;
|
||||
description = "The port for Redis to listen to.";
|
||||
};
|
||||
vmOverCommit = mkEnableOption ''
|
||||
setting of vm.overcommit_memory to 1
|
||||
(Suggested for Background Saving: http://redis.io/topics/faq)
|
||||
'';
|
||||
|
||||
vmOverCommit = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Set vm.overcommit_memory to 1 (Suggested for Background Saving: http://redis.io/topics/faq)
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to open ports in the firewall for the server.
|
||||
'';
|
||||
};
|
||||
|
||||
bind = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = "127.0.0.1";
|
||||
description = ''
|
||||
The IP interface to bind to.
|
||||
<literal>null</literal> means "all interfaces".
|
||||
'';
|
||||
example = "192.0.2.1";
|
||||
};
|
||||
|
||||
unixSocket = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = "The path to the socket to bind to.";
|
||||
example = "/run/redis/redis.sock";
|
||||
};
|
||||
|
||||
unixSocketPerm = mkOption {
|
||||
type = types.int;
|
||||
default = 750;
|
||||
description = "Change permissions for the socket";
|
||||
example = 700;
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
type = types.str;
|
||||
default = "notice"; # debug, verbose, notice, warning
|
||||
example = "debug";
|
||||
description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
|
||||
};
|
||||
|
||||
logfile = mkOption {
|
||||
type = types.str;
|
||||
default = "/dev/null";
|
||||
description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
|
||||
example = "/var/log/redis.log";
|
||||
};
|
||||
|
||||
syslog = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable logging to the system logger.";
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = types.int;
|
||||
default = 16;
|
||||
description = "Set the number of databases.";
|
||||
};
|
||||
|
||||
maxclients = mkOption {
|
||||
type = types.int;
|
||||
default = 10000;
|
||||
description = "Set the max number of connected clients at the same time.";
|
||||
};
|
||||
|
||||
save = mkOption {
|
||||
type = with types; listOf (listOf int);
|
||||
default = [ [900 1] [300 10] [60 10000] ];
|
||||
description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
|
||||
};
|
||||
|
||||
slaveOf = mkOption {
|
||||
type = with types; nullOr (submodule ({ ... }: {
|
||||
servers = mkOption {
|
||||
type = with types; attrsOf (submodule ({config, name, ...}@args: {
|
||||
options = {
|
||||
ip = mkOption {
|
||||
type = str;
|
||||
description = "IP of the Redis master";
|
||||
example = "192.168.1.100";
|
||||
enable = mkEnableOption ''
|
||||
Redis server.
|
||||
|
||||
Note that the NixOS module for Redis disables kernel support
|
||||
for Transparent Huge Pages (THP),
|
||||
because this features causes major performance problems for Redis,
|
||||
e.g. (https://redis.io/topics/latency).
|
||||
'';
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = redisName name;
|
||||
defaultText = "\"redis\" or \"redis-\${name}\" if name != \"\"";
|
||||
description = "The username and groupname for redis-server.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "port of the Redis master";
|
||||
type = types.port;
|
||||
default = 6379;
|
||||
description = "The port for Redis to listen to.";
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to open ports in the firewall for the server.
|
||||
'';
|
||||
};
|
||||
|
||||
bind = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = if name == "" then "127.0.0.1" else null;
|
||||
defaultText = "127.0.0.1 or null if name != \"\"";
|
||||
description = ''
|
||||
The IP interface to bind to.
|
||||
<literal>null</literal> means "all interfaces".
|
||||
'';
|
||||
example = "192.0.2.1";
|
||||
};
|
||||
|
||||
unixSocket = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = "/run/${redisName name}/redis.sock";
|
||||
defaultText = "\"/run/redis/redis.sock\" or \"/run/redis-\${name}/redis.sock\" if name != \"\"";
|
||||
description = "The path to the socket to bind to.";
|
||||
};
|
||||
|
||||
unixSocketPerm = mkOption {
|
||||
type = types.int;
|
||||
default = 660;
|
||||
description = "Change permissions for the socket";
|
||||
example = 600;
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
type = types.str;
|
||||
default = "notice"; # debug, verbose, notice, warning
|
||||
example = "debug";
|
||||
description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
|
||||
};
|
||||
|
||||
logfile = mkOption {
|
||||
type = types.str;
|
||||
default = "/dev/null";
|
||||
description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
|
||||
example = "/var/log/redis.log";
|
||||
};
|
||||
|
||||
syslog = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable logging to the system logger.";
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = types.int;
|
||||
default = 16;
|
||||
description = "Set the number of databases.";
|
||||
};
|
||||
|
||||
maxclients = mkOption {
|
||||
type = types.int;
|
||||
default = 10000;
|
||||
description = "Set the max number of connected clients at the same time.";
|
||||
};
|
||||
|
||||
save = mkOption {
|
||||
type = with types; listOf (listOf int);
|
||||
default = [ [900 1] [300 10] [60 10000] ];
|
||||
description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
|
||||
};
|
||||
|
||||
slaveOf = mkOption {
|
||||
type = with types; nullOr (submodule ({ ... }: {
|
||||
options = {
|
||||
ip = mkOption {
|
||||
type = str;
|
||||
description = "IP of the Redis master";
|
||||
example = "192.168.1.100";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
description = "port of the Redis master";
|
||||
default = 6379;
|
||||
};
|
||||
};
|
||||
}));
|
||||
|
||||
default = null;
|
||||
description = "IP and port to which this redis instance acts as a slave.";
|
||||
example = { ip = "192.168.1.100"; port = 6379; };
|
||||
};
|
||||
|
||||
masterAuth = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''If the master is password protected (using the requirePass configuration)
|
||||
it is possible to tell the slave to authenticate before starting the replication synchronization
|
||||
process, otherwise the master will refuse the slave request.
|
||||
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
|
||||
};
|
||||
|
||||
requirePass = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
|
||||
Use requirePassFile to store it outside of the nix store in a dedicated file.
|
||||
'';
|
||||
example = "letmein!";
|
||||
};
|
||||
|
||||
requirePassFile = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = "File with password for the database.";
|
||||
example = "/run/keys/redis-password";
|
||||
};
|
||||
|
||||
appendOnly = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
|
||||
};
|
||||
|
||||
appendFsync = mkOption {
|
||||
type = types.str;
|
||||
default = "everysec"; # no, always, everysec
|
||||
description = "How often to fsync the append-only log, options: no, always, everysec.";
|
||||
};
|
||||
|
||||
slowLogLogSlowerThan = mkOption {
|
||||
type = types.int;
|
||||
default = 10000;
|
||||
description = "Log queries whose execution take longer than X in milliseconds.";
|
||||
example = 1000;
|
||||
};
|
||||
|
||||
slowLogMaxLen = mkOption {
|
||||
type = types.int;
|
||||
default = 128;
|
||||
description = "Maximum number of items to keep in slow log.";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
# TODO: this should be converted to freeformType
|
||||
type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
|
||||
default = {};
|
||||
description = ''
|
||||
Redis configuration. Refer to
|
||||
<link xlink:href="https://redis.io/topics/config"/>
|
||||
for details on supported values.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
config.settings = mkMerge [
|
||||
{
|
||||
port = if config.bind == null then 0 else config.port;
|
||||
daemonize = false;
|
||||
supervised = "systemd";
|
||||
loglevel = config.logLevel;
|
||||
logfile = config.logfile;
|
||||
syslog-enabled = config.syslog;
|
||||
databases = config.databases;
|
||||
maxclients = config.maxclients;
|
||||
save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") config.save;
|
||||
dbfilename = "dump.rdb";
|
||||
dir = "/var/lib/${redisName name}";
|
||||
appendOnly = config.appendOnly;
|
||||
appendfsync = config.appendFsync;
|
||||
slowlog-log-slower-than = config.slowLogLogSlowerThan;
|
||||
slowlog-max-len = config.slowLogMaxLen;
|
||||
}
|
||||
(mkIf (config.bind != null) { bind = config.bind; })
|
||||
(mkIf (config.unixSocket != null) {
|
||||
unixsocket = config.unixSocket;
|
||||
unixsocketperm = toString config.unixSocketPerm;
|
||||
})
|
||||
(mkIf (config.slaveOf != null) { slaveof = "${config.slaveOf.ip} ${toString config.slaveOf.port}"; })
|
||||
(mkIf (config.masterAuth != null) { masterauth = config.masterAuth; })
|
||||
(mkIf (config.requirePass != null) { requirepass = config.requirePass; })
|
||||
];
|
||||
}));
|
||||
|
||||
default = null;
|
||||
description = "IP and port to which this redis instance acts as a slave.";
|
||||
example = { ip = "192.168.1.100"; port = 6379; };
|
||||
};
|
||||
|
||||
masterAuth = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''If the master is password protected (using the requirePass configuration)
|
||||
it is possible to tell the slave to authenticate before starting the replication synchronization
|
||||
process, otherwise the master will refuse the slave request.
|
||||
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
|
||||
};
|
||||
|
||||
requirePass = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
|
||||
Use requirePassFile to store it outside of the nix store in a dedicated file.
|
||||
'';
|
||||
example = "letmein!";
|
||||
};
|
||||
|
||||
requirePassFile = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = "File with password for the database.";
|
||||
example = "/run/keys/redis-password";
|
||||
};
|
||||
|
||||
appendOnly = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
|
||||
};
|
||||
|
||||
appendFsync = mkOption {
|
||||
type = types.str;
|
||||
default = "everysec"; # no, always, everysec
|
||||
description = "How often to fsync the append-only log, options: no, always, everysec.";
|
||||
};
|
||||
|
||||
slowLogLogSlowerThan = mkOption {
|
||||
type = types.int;
|
||||
default = 10000;
|
||||
description = "Log queries whose execution take longer than X in milliseconds.";
|
||||
example = 1000;
|
||||
};
|
||||
|
||||
slowLogMaxLen = mkOption {
|
||||
type = types.int;
|
||||
default = 128;
|
||||
description = "Maximum number of items to keep in slow log.";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
|
||||
description = "Configuration of multiple <literal>redis-server</literal> instances.";
|
||||
default = {};
|
||||
description = ''
|
||||
Redis configuration. Refer to
|
||||
<link xlink:href="https://redis.io/topics/config"/>
|
||||
for details on supported values.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
@ -229,78 +289,61 @@ in {
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf config.services.redis.enable {
|
||||
assertions = [{
|
||||
assertion = cfg.requirePass != null -> cfg.requirePassFile == null;
|
||||
message = "You can only set one services.redis.requirePass or services.redis.requirePassFile";
|
||||
}];
|
||||
boot.kernel.sysctl = (mkMerge [
|
||||
config = mkIf (enabledServers != {}) {
|
||||
|
||||
assertions = attrValues (mapAttrs (name: conf: {
|
||||
assertion = conf.requirePass != null -> conf.requirePassFile == null;
|
||||
message = ''
|
||||
You can only set one services.redis.servers.${name}.requirePass
|
||||
or services.redis.servers.${name}.requirePassFile
|
||||
'';
|
||||
}) enabledServers);
|
||||
|
||||
boot.kernel.sysctl = mkMerge [
|
||||
{ "vm.nr_hugepages" = "0"; }
|
||||
( mkIf cfg.vmOverCommit { "vm.overcommit_memory" = "1"; } )
|
||||
]);
|
||||
];
|
||||
|
||||
networking.firewall = mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ cfg.port ];
|
||||
};
|
||||
|
||||
users.users.redis = {
|
||||
description = "Redis database user";
|
||||
group = "redis";
|
||||
isSystemUser = true;
|
||||
};
|
||||
users.groups.redis = {};
|
||||
networking.firewall.allowedTCPPorts = concatMap (conf:
|
||||
optional conf.openFirewall conf.port
|
||||
) (attrValues enabledServers);
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
services.redis.settings = mkMerge [
|
||||
{
|
||||
port = cfg.port;
|
||||
daemonize = false;
|
||||
supervised = "systemd";
|
||||
loglevel = cfg.logLevel;
|
||||
logfile = cfg.logfile;
|
||||
syslog-enabled = cfg.syslog;
|
||||
databases = cfg.databases;
|
||||
maxclients = cfg.maxclients;
|
||||
save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") cfg.save;
|
||||
dbfilename = "dump.rdb";
|
||||
dir = "/var/lib/redis";
|
||||
appendOnly = cfg.appendOnly;
|
||||
appendfsync = cfg.appendFsync;
|
||||
slowlog-log-slower-than = cfg.slowLogLogSlowerThan;
|
||||
slowlog-max-len = cfg.slowLogMaxLen;
|
||||
}
|
||||
(mkIf (cfg.bind != null) { bind = cfg.bind; })
|
||||
(mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; })
|
||||
(mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; })
|
||||
(mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; })
|
||||
(mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; })
|
||||
];
|
||||
users.users = mapAttrs' (name: conf: nameValuePair (redisName name) {
|
||||
description = "System user for the redis-server instance ${name}";
|
||||
isSystemUser = true;
|
||||
group = redisName name;
|
||||
}) enabledServers;
|
||||
users.groups = mapAttrs' (name: conf: nameValuePair (redisName name) {
|
||||
}) enabledServers;
|
||||
|
||||
systemd.services.redis = {
|
||||
description = "Redis Server";
|
||||
systemd.services = mapAttrs' (name: conf: nameValuePair (redisName name) {
|
||||
description = "Redis Server - ${redisName name}";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
preStart = ''
|
||||
install -m 600 ${redisConfig} /run/redis/redis.conf
|
||||
'' + optionalString (cfg.requirePassFile != null) ''
|
||||
password=$(cat ${escapeShellArg cfg.requirePassFile})
|
||||
echo "requirePass $password" >> /run/redis/redis.conf
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/redis-server /run/redis/redis.conf";
|
||||
ExecStart = "${cfg.package}/bin/redis-server /run/${redisName name}/redis.conf";
|
||||
ExecStartPre = [("+"+pkgs.writeShellScript "${redisName name}-credentials" (''
|
||||
install -o '${conf.user}' -m 600 ${redisConfig conf.settings} /run/${redisName name}/redis.conf
|
||||
'' + optionalString (conf.requirePassFile != null) ''
|
||||
{
|
||||
printf requirePass' '
|
||||
cat ${escapeShellArg conf.requirePassFile}
|
||||
} >>/run/${redisName name}/redis.conf
|
||||
'')
|
||||
)];
|
||||
Type = "notify";
|
||||
# User and group
|
||||
User = "redis";
|
||||
Group = "redis";
|
||||
User = conf.user;
|
||||
Group = conf.user;
|
||||
# Runtime directory and mode
|
||||
RuntimeDirectory = "redis";
|
||||
RuntimeDirectory = redisName name;
|
||||
RuntimeDirectoryMode = "0750";
|
||||
# State directory and mode
|
||||
StateDirectory = "redis";
|
||||
StateDirectory = redisName name;
|
||||
StateDirectoryMode = "0700";
|
||||
# Access write directories
|
||||
UMask = "0077";
|
||||
@ -309,7 +352,7 @@ in {
|
||||
# Security
|
||||
NoNewPrivileges = true;
|
||||
# Process Properties
|
||||
LimitNOFILE = "${toString ulimitNofile}";
|
||||
LimitNOFILE = mkDefault "${toString (conf.maxclients + 32)}";
|
||||
# Sandboxing
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
@ -322,7 +365,9 @@ in {
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
||||
RestrictAddressFamilies =
|
||||
optionals (conf.bind != null) ["AF_INET" "AF_INET6"] ++
|
||||
optional (conf.unixSocket != null) "AF_UNIX";
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
@ -333,6 +378,7 @@ in {
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
|
||||
};
|
||||
};
|
||||
}) enabledServers;
|
||||
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user