1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.rabbitmq;
7
8 inherit (builtins) concatStringsSep;
9
10 config_file_content = lib.generators.toKeyValue { } cfg.configItems;
11 config_file = pkgs.writeText "rabbitmq.conf" config_file_content;
12
13 advanced_config_file = pkgs.writeText "advanced.config" cfg.config;
14
15in
16{
17
18 imports = [
19 (mkRemovedOptionModule [ "services" "rabbitmq" "cookie" ] ''
20 This option wrote the Erlang cookie to the store, while it should be kept secret.
21 Please remove it from your NixOS configuration and deploy a cookie securely instead.
22 The renamed `unsafeCookie` must ONLY be used in isolated non-production environments such as NixOS VM tests.
23 '')
24 ];
25
26 ###### interface
27 options = {
28 services.rabbitmq = {
29 enable = mkOption {
30 type = types.bool;
31 default = false;
32 description = ''
33 Whether to enable the RabbitMQ server, an Advanced Message
34 Queuing Protocol (AMQP) broker.
35 '';
36 };
37
38 package = mkPackageOption pkgs "rabbitmq-server" { };
39
40 listenAddress = mkOption {
41 default = "127.0.0.1";
42 example = "";
43 description = ''
44 IP address on which RabbitMQ will listen for AMQP
45 connections. Set to the empty string to listen on all
46 interfaces. Note that RabbitMQ creates a user named
47 `guest` with password
48 `guest` by default, so you should delete
49 this user if you intend to allow external access.
50
51 Together with 'port' setting it's mostly an alias for
52 configItems."listeners.tcp.1" and it's left for backwards
53 compatibility with previous version of this module.
54 '';
55 type = types.str;
56 };
57
58 port = mkOption {
59 default = 5672;
60 description = ''
61 Port on which RabbitMQ will listen for AMQP connections.
62 '';
63 type = types.port;
64 };
65
66 dataDir = mkOption {
67 type = types.path;
68 default = "/var/lib/rabbitmq";
69 description = ''
70 Data directory for rabbitmq.
71 '';
72 };
73
74 unsafeCookie = mkOption {
75 default = "";
76 type = types.str;
77 description = ''
78 Erlang cookie is a string of arbitrary length which must
79 be the same for several nodes to be allowed to communicate.
80 Leave empty to generate automatically.
81
82 Setting the cookie via this option exposes the cookie to the store, which
83 is not recommended for security reasons.
84 Only use this option in an isolated non-production environment such as
85 NixOS VM tests.
86 '';
87 };
88
89 configItems = mkOption {
90 default = { };
91 type = types.attrsOf types.str;
92 example = literalExpression ''
93 {
94 "auth_backends.1.authn" = "rabbit_auth_backend_ldap";
95 "auth_backends.1.authz" = "rabbit_auth_backend_internal";
96 }
97 '';
98 description = ''
99 Configuration options in RabbitMQ's new config file format,
100 which is a simple key-value format that can not express nested
101 data structures. This is known as the `rabbitmq.conf` file,
102 although outside NixOS that filename may have Erlang syntax, particularly
103 prior to RabbitMQ 3.7.0.
104
105 If you do need to express nested data structures, you can use
106 `config` option. Configuration from `config`
107 will be merged into these options by RabbitMQ at runtime to
108 form the final configuration.
109
110 See https://www.rabbitmq.com/configure.html#config-items
111 For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats
112 '';
113 };
114
115 config = mkOption {
116 default = "";
117 type = types.str;
118 description = ''
119 Verbatim advanced configuration file contents using the Erlang syntax.
120 This is also known as the `advanced.config` file or the old config format.
121
122 `configItems` is preferred whenever possible. However, nested
123 data structures can only be expressed properly using the `config` option.
124
125 The contents of this option will be merged into the `configItems`
126 by RabbitMQ at runtime to form the final configuration.
127
128 See the second table on https://www.rabbitmq.com/configure.html#config-items
129 For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats
130 '';
131 };
132
133 plugins = mkOption {
134 default = [ ];
135 type = types.listOf types.str;
136 description = "The names of plugins to enable";
137 };
138
139 pluginDirs = mkOption {
140 default = [ ];
141 type = types.listOf types.path;
142 description = "The list of directories containing external plugins";
143 };
144
145 managementPlugin = {
146 enable = mkEnableOption "the management plugin";
147 port = mkOption {
148 default = 15672;
149 type = types.port;
150 description = ''
151 On which port to run the management plugin
152 '';
153 };
154 };
155 };
156 };
157
158
159 ###### implementation
160 config = mkIf cfg.enable {
161
162 # This is needed so we will have 'rabbitmqctl' in our PATH
163 environment.systemPackages = [ cfg.package ];
164
165 services.epmd.enable = true;
166
167 users.users.rabbitmq = {
168 description = "RabbitMQ server user";
169 home = "${cfg.dataDir}";
170 createHome = true;
171 group = "rabbitmq";
172 uid = config.ids.uids.rabbitmq;
173 };
174
175 users.groups.rabbitmq.gid = config.ids.gids.rabbitmq;
176
177 services.rabbitmq.configItems = {
178 "listeners.tcp.1" = mkDefault "${cfg.listenAddress}:${toString cfg.port}";
179 } // optionalAttrs cfg.managementPlugin.enable {
180 "management.tcp.port" = toString cfg.managementPlugin.port;
181 "management.tcp.ip" = cfg.listenAddress;
182 };
183
184 services.rabbitmq.plugins = optional cfg.managementPlugin.enable "rabbitmq_management";
185
186 systemd.services.rabbitmq = {
187 description = "RabbitMQ Server";
188
189 wantedBy = [ "multi-user.target" ];
190 after = [ "network.target" "epmd.socket" ];
191 wants = [ "network.target" "epmd.socket" ];
192
193 path = [
194 cfg.package
195 pkgs.coreutils # mkdir/chown/chmod for preStart
196 ];
197
198 environment = {
199 RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia";
200 RABBITMQ_LOGS = "-";
201 SYS_PREFIX = "";
202 RABBITMQ_CONFIG_FILE = config_file;
203 RABBITMQ_PLUGINS_DIR = concatStringsSep ":" cfg.pluginDirs;
204 RABBITMQ_ENABLED_PLUGINS_FILE = pkgs.writeText "enabled_plugins" ''
205 [ ${concatStringsSep "," cfg.plugins} ].
206 '';
207 } // optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
208
209 serviceConfig = {
210 ExecStart = "${cfg.package}/sbin/rabbitmq-server";
211 ExecStop = "${cfg.package}/sbin/rabbitmqctl shutdown";
212 User = "rabbitmq";
213 Group = "rabbitmq";
214 LogsDirectory = "rabbitmq";
215 WorkingDirectory = cfg.dataDir;
216 Type = "notify";
217 NotifyAccess = "all";
218 UMask = "0027";
219 LimitNOFILE = "100000";
220 Restart = "on-failure";
221 RestartSec = "10";
222 TimeoutStartSec = "3600";
223 };
224
225 preStart = ''
226 ${optionalString (cfg.unsafeCookie != "") ''
227 install -m 600 <(echo -n ${cfg.unsafeCookie}) ${cfg.dataDir}/.erlang.cookie
228 ''}
229 '';
230 };
231
232 };
233
234}