diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 1f826220a0f3..cb2dd530de15 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -448,6 +448,7 @@
./services/hardware/xow.nix
./services/logging/SystemdJournal2Gelf.nix
./services/logging/awstats.nix
+ ./services/logging/filebeat.nix
./services/logging/fluentd.nix
./services/logging/graylog.nix
./services/logging/heartbeat.nix
diff --git a/nixos/modules/services/logging/filebeat.nix b/nixos/modules/services/logging/filebeat.nix
new file mode 100644
index 000000000000..223a993c505b
--- /dev/null
+++ b/nixos/modules/services/logging/filebeat.nix
@@ -0,0 +1,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 services.filebeat.settings.filebeat.inputs,
+ 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 <name> for each, but setting
+
+ to the same value.
+
+ See .
+ '';
+ 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 type: on
+ the individual input pages linked from
+ .
+ '';
+ };
+ };
+ }));
+ 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 services.filebeat.settings.filebeat.modules,
+ 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 <name> for each, but setting
+
+ to the same value.
+
+ See .
+ '';
+ 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 module: on
+ the individual input pages linked from
+ .
+ '';
+ };
+ };
+ }));
+ 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:
+ http://192.15.3.2,
+ https://es.found.io:9230 or
+ 192.24.3.2:9300. If no port is
+ specified, 9200 is used.
+ '';
+ };
+
+ filebeat = {
+ inputs = mkOption {
+ type = types.listOf json.type;
+ default = [];
+ internal = true;
+ description = ''
+ Inputs specify how Filebeat locates and processes
+ input data. Use instead.
+
+ See .
+ '';
+ };
+ 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 instead.
+
+ See .
+ '';
+ };
+ };
+ };
+ };
+ 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
+
+ for supported values.
+
+ Options containing secret data should be set to an attribute
+ set containing the attribute _secret - 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
+ filebeat.yml file, the
+ output.elasticsearch.password
+ key will be set to the contents of the
+ /var/keys/elasticsearch_password 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";
+ };
+ };
+ };
+}
diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix
index 8db49ecfb18a..f42be00f23b8 100644
--- a/nixos/tests/elk.nix
+++ b/nixos/tests/elk.nix
@@ -56,6 +56,23 @@ let
'');
};
+ filebeat = {
+ enable = elk ? filebeat;
+ package = elk.filebeat;
+ inputs.journald.id = "everything";
+
+ inputs.log = {
+ enabled = true;
+ paths = [
+ "/var/lib/filebeat/test"
+ ];
+ };
+
+ settings = {
+ logging.level = "info";
+ };
+ };
+
metricbeat = {
enable = true;
package = elk.metricbeat;
@@ -226,12 +243,27 @@ let
one.wait_until_succeeds(
expect_hits("Supercalifragilisticexpialidocious")
)
+ '' + lib.optionalString (elk ? filebeat) ''
+ with subtest(
+ "A message logged to the journal is ingested by elasticsearch via filebeat"
+ ):
+ one.wait_for_unit("filebeat.service")
+ one.execute("echo 'Superdupercalifragilisticexpialidocious' | systemd-cat")
+ one.wait_until_succeeds(
+ expect_hits("Superdupercalifragilisticexpialidocious")
+ )
+ one.execute(
+ "echo 'SuperdupercalifragilisticexpialidociousIndeed' >> /var/lib/filebeat/test"
+ )
+ one.wait_until_succeeds(
+ expect_hits("SuperdupercalifragilisticexpialidociousIndeed")
+ )
'' + ''
with subtest("Elasticsearch-curator works"):
one.systemctl("stop logstash")
one.systemctl("start elasticsearch-curator")
one.wait_until_succeeds(
- '! curl --silent --show-error "${esUrl}/_cat/indices" | grep logstash | grep ^'
+ '! curl --silent --show-error --fail-with-body "${esUrl}/_cat/indices" | grep logstash | grep ^'
)
'';
}) { inherit pkgs system; };
@@ -251,6 +283,7 @@ in {
# elasticsearch = pkgs.elasticsearch7-oss;
# logstash = pkgs.logstash7-oss;
# kibana = pkgs.kibana7-oss;
+ # filebeat = pkgs.filebeat7;
# metricbeat = pkgs.metricbeat7;
# };
unfree = lib.dontRecurseIntoAttrs {
@@ -265,6 +298,7 @@ in {
elasticsearch = pkgs.elasticsearch7;
logstash = pkgs.logstash7;
kibana = pkgs.kibana7;
+ filebeat = pkgs.filebeat7;
metricbeat = pkgs.metricbeat7;
};
};