1{ config, lib, utils, pkgs, ... }:
2
3let
4 inherit (lib)
5 attrValues
6 literalExpression
7 mkEnableOption
8 mkPackageOption
9 mkIf
10 mkOption
11 types;
12
13 cfg = config.services.filebeat;
14
15 json = pkgs.formats.json {};
16in
17{
18 options = {
19
20 services.filebeat = {
21
22 enable = mkEnableOption "filebeat";
23
24 package = mkPackageOption pkgs "filebeat" {
25 example = "filebeat7";
26 };
27
28 inputs = mkOption {
29 description = ''
30 Inputs specify how Filebeat locates and processes input data.
31
32 This is like `services.filebeat.settings.filebeat.inputs`,
33 but structured as an attribute set. This has the benefit
34 that multiple NixOS modules can contribute settings to a
35 single filebeat input.
36
37 An input type can be specified multiple times by choosing a
38 different `<name>` for each, but setting
39 [](#opt-services.filebeat.inputs._name_.type)
40 to the same value.
41
42 See <https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html>.
43 '';
44 default = {};
45 type = types.attrsOf (types.submodule ({ name, ... }: {
46 freeformType = json.type;
47 options = {
48 type = mkOption {
49 type = types.str;
50 default = name;
51 description = ''
52 The input type.
53
54 Look for the value after `type:` on
55 the individual input pages linked from
56 <https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html>.
57 '';
58 };
59 };
60 }));
61 example = literalExpression ''
62 {
63 journald.id = "everything"; # Only for filebeat7
64 log = {
65 enabled = true;
66 paths = [
67 "/var/log/*.log"
68 ];
69 };
70 };
71 '';
72 };
73
74 modules = mkOption {
75 description = ''
76 Filebeat modules provide a quick way to get started
77 processing common log formats. They contain default
78 configurations, Elasticsearch ingest pipeline definitions,
79 and Kibana dashboards to help you implement and deploy a log
80 monitoring solution.
81
82 This is like `services.filebeat.settings.filebeat.modules`,
83 but structured as an attribute set. This has the benefit
84 that multiple NixOS modules can contribute settings to a
85 single filebeat module.
86
87 A module can be specified multiple times by choosing a
88 different `<name>` for each, but setting
89 [](#opt-services.filebeat.modules._name_.module)
90 to the same value.
91
92 See <https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html>.
93 '';
94 default = {};
95 type = types.attrsOf (types.submodule ({ name, ... }: {
96 freeformType = json.type;
97 options = {
98 module = mkOption {
99 type = types.str;
100 default = name;
101 description = ''
102 The name of the module.
103
104 Look for the value after `module:` on
105 the individual input pages linked from
106 <https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html>.
107 '';
108 };
109 };
110 }));
111 example = literalExpression ''
112 {
113 nginx = {
114 access = {
115 enabled = true;
116 var.paths = [ "/path/to/log/nginx/access.log*" ];
117 };
118 error = {
119 enabled = true;
120 var.paths = [ "/path/to/log/nginx/error.log*" ];
121 };
122 };
123 };
124 '';
125 };
126
127 settings = mkOption {
128 type = types.submodule {
129 freeformType = json.type;
130
131 options = {
132
133 output.elasticsearch.hosts = mkOption {
134 type = with types; listOf str;
135 default = [ "127.0.0.1:9200" ];
136 example = [ "myEShost:9200" ];
137 description = ''
138 The list of Elasticsearch nodes to connect to.
139
140 The events are distributed to these nodes in round
141 robin order. If one node becomes unreachable, the
142 event is automatically sent to another node. Each
143 Elasticsearch node can be defined as a URL or
144 IP:PORT. For example:
145 `http://192.15.3.2`,
146 `https://es.found.io:9230` or
147 `192.24.3.2:9300`. If no port is
148 specified, `9200` is used.
149 '';
150 };
151
152 filebeat = {
153 inputs = mkOption {
154 type = types.listOf json.type;
155 default = [];
156 internal = true;
157 description = ''
158 Inputs specify how Filebeat locates and processes
159 input data. Use [](#opt-services.filebeat.inputs) instead.
160
161 See <https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html>.
162 '';
163 };
164 modules = mkOption {
165 type = types.listOf json.type;
166 default = [];
167 internal = true;
168 description = ''
169 Filebeat modules provide a quick way to get started
170 processing common log formats. They contain default
171 configurations, Elasticsearch ingest pipeline
172 definitions, and Kibana dashboards to help you
173 implement and deploy a log monitoring solution.
174
175 Use [](#opt-services.filebeat.modules) instead.
176
177 See <https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html>.
178 '';
179 };
180 };
181 };
182 };
183 default = {};
184 example = literalExpression ''
185 {
186 settings = {
187 output.elasticsearch = {
188 hosts = [ "myEShost:9200" ];
189 username = "filebeat_internal";
190 password = { _secret = "/var/keys/elasticsearch_password"; };
191 };
192 logging.level = "info";
193 };
194 };
195 '';
196
197 description = ''
198 Configuration for filebeat. See
199 <https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-reference-yml.html>
200 for supported values.
201
202 Options containing secret data should be set to an attribute
203 set containing the attribute `_secret` - a
204 string pointing to a file containing the value the option
205 should be set to. See the example to get a better picture of
206 this: in the resulting
207 {file}`filebeat.yml` file, the
208 `output.elasticsearch.password`
209 key will be set to the contents of the
210 {file}`/var/keys/elasticsearch_password` file.
211 '';
212 };
213 };
214 };
215
216 config = mkIf cfg.enable {
217
218 services.filebeat.settings.filebeat.inputs = attrValues cfg.inputs;
219 services.filebeat.settings.filebeat.modules = attrValues cfg.modules;
220
221 systemd.services.filebeat = {
222 description = "Filebeat log shipper";
223 wantedBy = [ "multi-user.target" ];
224 wants = [ "elasticsearch.service" ];
225 after = [ "elasticsearch.service" ];
226 serviceConfig = {
227 ExecStartPre = pkgs.writeShellScript "filebeat-exec-pre" ''
228 set -euo pipefail
229
230 umask u=rwx,g=,o=
231
232 ${utils.genJqSecretsReplacementSnippet
233 cfg.settings
234 "/var/lib/filebeat/filebeat.yml"
235 }
236 '';
237 ExecStart = ''
238 ${cfg.package}/bin/filebeat -e \
239 -c "/var/lib/filebeat/filebeat.yml" \
240 --path.data "/var/lib/filebeat"
241 '';
242 Restart = "always";
243 StateDirectory = "filebeat";
244 };
245 };
246 };
247}