summary refs log tree commit diff
path: root/nixos/modules/services/logging/filebeat.nix
blob: 223a993c505b47b5e3c30e01731627f87858ed01 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
{ config, lib, utils, pkgs, ... }:

let
  inherit (lib)
    attrValues
    literalExpression
    mkEnableOption
    mkIf
    mkOption
    types;

  cfg = config.services.filebeat;

  json = pkgs.formats.json {};
in
{
  options = {

    services.filebeat = {

      enable = mkEnableOption "filebeat";

      package = mkOption {
        type = types.package;
        default = pkgs.filebeat;
        defaultText = literalExpression "pkgs.filebeat";
        example = literalExpression "pkgs.filebeat7";
        description = ''
          The filebeat package to use.
        '';
      };

      inputs = mkOption {
        description = ''
          Inputs specify how Filebeat locates and processes input data.

          This is like <literal>services.filebeat.settings.filebeat.inputs</literal>,
          but structured as an attribute set. This has the benefit
          that multiple NixOS modules can contribute settings to a
          single filebeat input.

          An input type can be specified multiple times by choosing a
          different <literal>&lt;name></literal> for each, but setting
          <xref linkend="opt-services.filebeat.inputs._name_.type"/>
          to the same value.

          See <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html"/>.
        '';
        default = {};
        type = types.attrsOf (types.submodule ({ name, ... }: {
          freeformType = json.type;
          options = {
            type = mkOption {
              type = types.str;
              default = name;
              description = ''
                The input type.

                Look for the value after <literal>type:</literal> on
                the individual input pages linked from
                <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html"/>.
              '';
            };
          };
        }));
        example = literalExpression ''
          {
            journald.id = "everything";  # Only for filebeat7
            log = {
              enabled = true;
              paths = [
                "/var/log/*.log"
              ];
            };
          };
        '';
      };

      modules = mkOption {
        description = ''
          Filebeat modules provide a quick way to get started
          processing common log formats. They contain default
          configurations, Elasticsearch ingest pipeline definitions,
          and Kibana dashboards to help you implement and deploy a log
          monitoring solution.

          This is like <literal>services.filebeat.settings.filebeat.modules</literal>,
          but structured as an attribute set. This has the benefit
          that multiple NixOS modules can contribute settings to a
          single filebeat module.

          A module can be specified multiple times by choosing a
          different <literal>&lt;name></literal> for each, but setting
          <xref linkend="opt-services.filebeat.modules._name_.module"/>
          to the same value.

          See <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html"/>.
        '';
        default = {};
        type = types.attrsOf (types.submodule ({ name, ... }: {
          freeformType = json.type;
          options = {
            module = mkOption {
              type = types.str;
              default = name;
              description = ''
                The name of the module.

                Look for the value after <literal>module:</literal> on
                the individual input pages linked from
                <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html"/>.
              '';
            };
          };
        }));
        example = literalExpression ''
          {
            nginx = {
              access = {
                enabled = true;
                var.paths = [ "/path/to/log/nginx/access.log*" ];
              };
              error = {
                enabled = true;
                var.paths = [ "/path/to/log/nginx/error.log*" ];
              };
            };
          };
        '';
      };

      settings = mkOption {
        type = types.submodule {
          freeformType = json.type;

          options = {

            output.elasticsearch.hosts = mkOption {
              type = with types; listOf str;
              default = [ "127.0.0.1:9200" ];
              example = [ "myEShost:9200" ];
              description = ''
                The list of Elasticsearch nodes to connect to.

                The events are distributed to these nodes in round
                robin order. If one node becomes unreachable, the
                event is automatically sent to another node. Each
                Elasticsearch node can be defined as a URL or
                IP:PORT. For example:
                <literal>http://192.15.3.2</literal>,
                <literal>https://es.found.io:9230</literal> or
                <literal>192.24.3.2:9300</literal>. If no port is
                specified, <literal>9200</literal> is used.
              '';
            };

            filebeat = {
              inputs = mkOption {
                type = types.listOf json.type;
                default = [];
                internal = true;
                description = ''
                  Inputs specify how Filebeat locates and processes
                  input data. Use <xref
                  linkend="opt-services.filebeat.inputs"/> instead.

                  See <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html"/>.
                '';
              };
              modules = mkOption {
                type = types.listOf json.type;
                default = [];
                internal = true;
                description = ''
                  Filebeat modules provide a quick way to get started
                  processing common log formats. They contain default
                  configurations, Elasticsearch ingest pipeline
                  definitions, and Kibana dashboards to help you
                  implement and deploy a log monitoring solution.

                  Use <xref linkend="opt-services.filebeat.modules"/> instead.

                  See <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html"/>.
                '';
              };
            };
          };
        };
        default = {};
        example = literalExpression ''
          {
            settings = {
              output.elasticsearch = {
                hosts = [ "myEShost:9200" ];
                username = "filebeat_internal";
                password = { _secret = "/var/keys/elasticsearch_password"; };
              };
              logging.level = "info";
            };
          };
        '';

        description = ''
          Configuration for filebeat. See
          <link xlink:href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-reference-yml.html"/>
          for supported values.

          Options containing secret data should be set to an attribute
          set containing the attribute <literal>_secret</literal> - a
          string pointing to a file containing the value the option
          should be set to. See the example to get a better picture of
          this: in the resulting
          <filename>filebeat.yml</filename> file, the
          <literal>output.elasticsearch.password</literal>
          key will be set to the contents of the
          <filename>/var/keys/elasticsearch_password</filename> file.
        '';
      };
    };
  };

  config = mkIf cfg.enable {

    services.filebeat.settings.filebeat.inputs = attrValues cfg.inputs;
    services.filebeat.settings.filebeat.modules = attrValues cfg.modules;

    systemd.services.filebeat = {
      description = "Filebeat log shipper";
      wantedBy = [ "multi-user.target" ];
      wants = [ "elasticsearch.service" ];
      after = [ "elasticsearch.service" ];
      serviceConfig = {
        ExecStartPre = pkgs.writeShellScript "filebeat-exec-pre" ''
          set -euo pipefail

          umask u=rwx,g=,o=

          ${utils.genJqSecretsReplacementSnippet
              cfg.settings
              "/var/lib/filebeat/filebeat.yml"
           }
        '';
        ExecStart = ''
          ${cfg.package}/bin/filebeat -e \
            -c "/var/lib/filebeat/filebeat.yml" \
            --path.data "/var/lib/filebeat"
        '';
        Restart = "always";
        StateDirectory = "filebeat";
      };
    };
  };
}