|
|
|
@ -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 = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = false; |
|
|
|
|
description = '' |
|
|
|
|
Set vm.overcommit_memory to 1 (Suggested for Background Saving: http://redis.io/topics/faq) |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
vmOverCommit = mkEnableOption '' |
|
|
|
|
setting of 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. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
servers = mkOption { |
|
|
|
|
type = with types; attrsOf (submodule ({config, name, ...}@args: { |
|
|
|
|
options = { |
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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"; |
|
|
|
|
}; |
|
|
|
|
port = mkOption { |
|
|
|
|
type = types.port; |
|
|
|
|
default = 6379; |
|
|
|
|
description = "The port for Redis to listen to."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
unixSocket = mkOption { |
|
|
|
|
type = with types; nullOr path; |
|
|
|
|
default = null; |
|
|
|
|
description = "The path to the socket to bind to."; |
|
|
|
|
example = "/run/redis/redis.sock"; |
|
|
|
|
}; |
|
|
|
|
openFirewall = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = false; |
|
|
|
|
description = '' |
|
|
|
|
Whether to open ports in the firewall for the server. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
unixSocketPerm = mkOption { |
|
|
|
|
type = types.int; |
|
|
|
|
default = 750; |
|
|
|
|
description = "Change permissions for the socket"; |
|
|
|
|
example = 700; |
|
|
|
|
}; |
|
|
|
|
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"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
logLevel = mkOption { |
|
|
|
|
type = types.str; |
|
|
|
|
default = "notice"; # debug, verbose, notice, warning |
|
|
|
|
example = "debug"; |
|
|
|
|
description = "Specify the server verbosity level, options: debug, verbose, notice, warning."; |
|
|
|
|
}; |
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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"; |
|
|
|
|
}; |
|
|
|
|
unixSocketPerm = mkOption { |
|
|
|
|
type = types.int; |
|
|
|
|
default = 660; |
|
|
|
|
description = "Change permissions for the socket"; |
|
|
|
|
example = 600; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
syslog = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = true; |
|
|
|
|
description = "Enable logging to the system logger."; |
|
|
|
|
}; |
|
|
|
|
logLevel = mkOption { |
|
|
|
|
type = types.str; |
|
|
|
|
default = "notice"; # debug, verbose, notice, warning |
|
|
|
|
example = "debug"; |
|
|
|
|
description = "Specify the server verbosity level, options: debug, verbose, notice, warning."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
databases = mkOption { |
|
|
|
|
type = types.int; |
|
|
|
|
default = 16; |
|
|
|
|
description = "Set the number of databases."; |
|
|
|
|
}; |
|
|
|
|
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"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
maxclients = mkOption { |
|
|
|
|
type = types.int; |
|
|
|
|
default = 10000; |
|
|
|
|
description = "Set the max number of connected clients at the same time."; |
|
|
|
|
}; |
|
|
|
|
syslog = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = true; |
|
|
|
|
description = "Enable logging to the system logger."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
databases = mkOption { |
|
|
|
|
type = types.int; |
|
|
|
|
default = 16; |
|
|
|
|
description = "Set the number of databases."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
slaveOf = mkOption { |
|
|
|
|
type = with types; nullOr (submodule ({ ... }: { |
|
|
|
|
options = { |
|
|
|
|
ip = mkOption { |
|
|
|
|
type = str; |
|
|
|
|
description = "IP of the Redis master"; |
|
|
|
|
example = "192.168.1.100"; |
|
|
|
|
maxclients = mkOption { |
|
|
|
|
type = types.int; |
|
|
|
|
default = 10000; |
|
|
|
|
description = "Set the max number of connected clients at the same time."; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
port = mkOption { |
|
|
|
|
type = port; |
|
|
|
|
description = "port of the Redis master"; |
|
|
|
|
default = 6379; |
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
}; |
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
default = null; |
|
|
|
|
description = "IP and port to which this redis instance acts as a slave."; |
|
|
|
|
example = { ip = "192.168.1.100"; port = 6379; }; |
|
|
|
|
}; |
|
|
|
|
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)''; |
|
|
|
|
}; |
|
|
|
|
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!"; |
|
|
|
|
}; |
|
|
|
|
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"; |
|
|
|
|
}; |
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
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; |
|
|
|
|
}; |
|
|
|
|
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."; |
|
|
|
|
}; |
|
|
|
|
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) ]); |
|
|
|
|
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; }) |
|
|
|
|
]; |
|
|
|
|
})); |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|