1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.graylog;
9
10 confFile = pkgs.writeText "graylog.conf" ''
11 is_master = ${lib.boolToString cfg.isMaster}
12 node_id_file = ${cfg.nodeIdFile}
13 password_secret = ${cfg.passwordSecret}
14 root_username = ${cfg.rootUsername}
15 root_password_sha2 = ${cfg.rootPasswordSha2}
16 elasticsearch_hosts = ${lib.concatStringsSep "," cfg.elasticsearchHosts}
17 message_journal_dir = ${cfg.messageJournalDir}
18 mongodb_uri = ${cfg.mongodbUri}
19 plugin_dir = /var/lib/graylog/plugins
20 data_dir = ${cfg.dataDir}
21
22 ${cfg.extraConfig}
23 '';
24
25 glPlugins = pkgs.buildEnv {
26 name = "graylog-plugins";
27 paths = cfg.plugins;
28 };
29
30in
31
32{
33 ###### interface
34
35 options = {
36
37 services.graylog = {
38
39 enable = lib.mkEnableOption "Graylog, a log management solution";
40
41 package = lib.mkPackageOption pkgs "graylog" {
42 example = "graylog-6_0";
43 };
44
45 user = lib.mkOption {
46 type = lib.types.str;
47 default = "graylog";
48 description = "User account under which graylog runs";
49 };
50
51 isMaster = lib.mkOption {
52 type = lib.types.bool;
53 default = true;
54 description = "Whether this is the master instance of your Graylog cluster";
55 };
56
57 nodeIdFile = lib.mkOption {
58 type = lib.types.str;
59 default = "/var/lib/graylog/server/node-id";
60 description = "Path of the file containing the graylog node-id";
61 };
62
63 passwordSecret = lib.mkOption {
64 type = lib.types.str;
65 description = ''
66 You MUST set a secret to secure/pepper the stored user passwords here. Use at least 64 characters.
67 Generate one by using for example: pwgen -N 1 -s 96
68 '';
69 };
70
71 rootUsername = lib.mkOption {
72 type = lib.types.str;
73 default = "admin";
74 description = "Name of the default administrator user";
75 };
76
77 rootPasswordSha2 = lib.mkOption {
78 type = lib.types.str;
79 example = "e3c652f0ba0b4801205814f8b6bc49672c4c74e25b497770bb89b22cdeb4e952";
80 description = ''
81 You MUST specify a hash password for the root user (which you only need to initially set up the
82 system and in case you lose connectivity to your authentication backend)
83 This password cannot be changed using the API or via the web interface. If you need to change it,
84 modify it here.
85 Create one by using for example: echo -n yourpassword | shasum -a 256
86 and use the resulting hash value as string for the option
87 '';
88 };
89
90 elasticsearchHosts = lib.mkOption {
91 type = lib.types.listOf lib.types.str;
92 example = lib.literalExpression ''[ "http://node1:9200" "http://user:password@node2:19200" ]'';
93 description = "List of valid URIs of the http ports of your elastic nodes. If one or more of your elasticsearch hosts require authentication, include the credentials in each node URI that requires authentication";
94 };
95
96 dataDir = lib.mkOption {
97 type = lib.types.str;
98 default = "/var/lib/graylog/data";
99 description = "Directory used to store Graylog server state.";
100 };
101
102 messageJournalDir = lib.mkOption {
103 type = lib.types.str;
104 default = "/var/lib/graylog/data/journal";
105 description = "The directory which will be used to store the message journal. The directory must be exclusively used by Graylog and must not contain any other files than the ones created by Graylog itself";
106 };
107
108 mongodbUri = lib.mkOption {
109 type = lib.types.str;
110 default = "mongodb://localhost/graylog";
111 description = "MongoDB connection string. See http://docs.mongodb.org/manual/reference/connection-string/ for details";
112 };
113
114 extraConfig = lib.mkOption {
115 type = lib.types.lines;
116 default = "";
117 description = "Any other configuration options you might want to add";
118 };
119
120 plugins = lib.mkOption {
121 description = "Extra graylog plugins";
122 default = [ ];
123 type = lib.types.listOf lib.types.package;
124 };
125
126 };
127 };
128
129 ###### implementation
130
131 config = lib.mkIf cfg.enable {
132
133 # Note: when changing the default, make it conditional on
134 # ‘system.stateVersion’ to maintain compatibility with existing
135 # systems!
136 services.graylog.package =
137 let
138 mkThrow = ver: throw "graylog-${ver} was removed, please upgrade your graylog version.";
139 base =
140 if lib.versionAtLeast config.system.stateVersion "25.05" then
141 pkgs.graylog-6_0
142 else if lib.versionAtLeast config.system.stateVersion "23.05" then
143 mkThrow "5_1"
144 else
145 mkThrow "3_3";
146 in
147 lib.mkDefault base;
148
149 users.users = lib.mkIf (cfg.user == "graylog") {
150 graylog = {
151 isSystemUser = true;
152 group = "graylog";
153 description = "Graylog server daemon user";
154 };
155 };
156 users.groups = lib.mkIf (cfg.user == "graylog") { graylog = { }; };
157
158 systemd.tmpfiles.rules = [
159 "d '${cfg.messageJournalDir}' - ${cfg.user} - - -"
160 ];
161
162 systemd.services.graylog = {
163 description = "Graylog Server";
164 wantedBy = [ "multi-user.target" ];
165 environment = {
166 GRAYLOG_CONF = "${confFile}";
167 };
168 path = [
169 pkgs.which
170 pkgs.procps
171 ];
172 preStart = ''
173 rm -rf /var/lib/graylog/plugins || true
174 mkdir -p /var/lib/graylog/plugins -m 755
175
176 mkdir -p "$(dirname ${cfg.nodeIdFile})"
177 chown -R ${cfg.user} "$(dirname ${cfg.nodeIdFile})"
178
179 for declarativeplugin in `ls ${glPlugins}/bin/`; do
180 ln -sf ${glPlugins}/bin/$declarativeplugin /var/lib/graylog/plugins/$declarativeplugin
181 done
182 for includedplugin in `ls ${cfg.package}/plugin/`; do
183 ln -s ${cfg.package}/plugin/$includedplugin /var/lib/graylog/plugins/$includedplugin || true
184 done
185 '';
186 serviceConfig = {
187 User = "${cfg.user}";
188 StateDirectory = "graylog";
189 ExecStart = "${cfg.package}/bin/graylogctl run";
190 };
191 };
192 };
193}