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