uboot: (firmwareOdroidC2/C4) don't invoke patch tool, use patches = [] instead
https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh#L948 this can do it nicely. Signed-off-by: Anton Arapov <anton@deadbeef.mx>
This commit is contained in:
commit
56de2bcd43
30691 changed files with 3076956 additions and 0 deletions
60
nixos/modules/services/logging/SystemdJournal2Gelf.nix
Normal file
60
nixos/modules/services/logging/SystemdJournal2Gelf.nix
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let cfg = config.services.SystemdJournal2Gelf;
|
||||
in
|
||||
|
||||
{ options = {
|
||||
services.SystemdJournal2Gelf = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable SystemdJournal2Gelf.
|
||||
'';
|
||||
};
|
||||
|
||||
graylogServer = mkOption {
|
||||
type = types.str;
|
||||
example = "graylog2.example.com:11201";
|
||||
description = ''
|
||||
Host and port of your graylog2 input. This should be a GELF
|
||||
UDP input.
|
||||
'';
|
||||
};
|
||||
|
||||
extraOptions = mkOption {
|
||||
type = types.separatedString " ";
|
||||
default = "";
|
||||
description = ''
|
||||
Any extra flags to pass to SystemdJournal2Gelf. Note that
|
||||
these are basically <literal>journalctl</literal> flags.
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.systemd-journal2gelf;
|
||||
defaultText = literalExpression "pkgs.systemd-journal2gelf";
|
||||
description = ''
|
||||
SystemdJournal2Gelf package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.SystemdJournal2Gelf = {
|
||||
description = "SystemdJournal2Gelf";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/SystemdJournal2Gelf ${cfg.graylogServer} --follow ${cfg.extraOptions}";
|
||||
Restart = "on-failure";
|
||||
RestartSec = "30";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
257
nixos/modules/services/logging/awstats.nix
Normal file
257
nixos/modules/services/logging/awstats.nix
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.awstats;
|
||||
package = pkgs.awstats;
|
||||
configOpts = {name, config, ...}: {
|
||||
options = {
|
||||
type = mkOption{
|
||||
type = types.enum [ "mail" "web" ];
|
||||
default = "web";
|
||||
example = "mail";
|
||||
description = ''
|
||||
The type of log being collected.
|
||||
'';
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = "The domain name to collect stats for.";
|
||||
example = "example.com";
|
||||
};
|
||||
|
||||
logFile = mkOption {
|
||||
type = types.str;
|
||||
example = "/var/log/nginx/access.log";
|
||||
description = ''
|
||||
The log file to be scanned.
|
||||
|
||||
For mail, set this to
|
||||
<literal>
|
||||
journalctl $OLD_CURSOR -u postfix.service | ''${pkgs.perl}/bin/perl ''${pkgs.awstats.out}/share/awstats/tools/maillogconvert.pl standard |
|
||||
</literal>
|
||||
'';
|
||||
};
|
||||
|
||||
logFormat = mkOption {
|
||||
type = types.str;
|
||||
default = "1";
|
||||
description = ''
|
||||
The log format being used.
|
||||
|
||||
For mail, set this to
|
||||
<literal>
|
||||
%time2 %email %email_r %host %host_r %method %url %code %bytesd
|
||||
</literal>
|
||||
'';
|
||||
};
|
||||
|
||||
hostAliases = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = [ "www.example.org" ];
|
||||
description = ''
|
||||
List of aliases the site has.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
example = literalExpression ''
|
||||
{
|
||||
"ValidHTTPCodes" = "404";
|
||||
}
|
||||
'';
|
||||
description = "Extra configuration to be appended to awstats.\${name}.conf.";
|
||||
};
|
||||
|
||||
webService = {
|
||||
enable = mkEnableOption "awstats web service";
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
default = config.domain;
|
||||
description = "The hostname the web service appears under.";
|
||||
};
|
||||
|
||||
urlPrefix = mkOption {
|
||||
type = types.str;
|
||||
default = "/awstats";
|
||||
description = "The URL prefix under which the awstats pages appear.";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
webServices = filterAttrs (name: value: value.webService.enable) cfg.configs;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "services" "awstats" "service" "enable" ] "Please enable per domain with `services.awstats.configs.<name>.webService.enable`")
|
||||
(mkRemovedOptionModule [ "services" "awstats" "service" "urlPrefix" ] "Please set per domain with `services.awstats.configs.<name>.webService.urlPrefix`")
|
||||
(mkRenamedOptionModule [ "services" "awstats" "vardir" ] [ "services" "awstats" "dataDir" ])
|
||||
];
|
||||
|
||||
options.services.awstats = {
|
||||
enable = mkEnableOption "awstats";
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/awstats";
|
||||
description = "The directory where awstats data will be stored.";
|
||||
};
|
||||
|
||||
configs = mkOption {
|
||||
type = types.attrsOf (types.submodule configOpts);
|
||||
default = {};
|
||||
example = literalExpression ''
|
||||
{
|
||||
"mysite" = {
|
||||
domain = "example.com";
|
||||
logFile = "/var/log/nginx/access.log";
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = "Attribute set of domains to collect stats for.";
|
||||
};
|
||||
|
||||
updateAt = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "hourly";
|
||||
description = ''
|
||||
Specification of the time at which awstats will get updated.
|
||||
(in the format described by <citerefentry>
|
||||
<refentrytitle>systemd.time</refentrytitle>
|
||||
<manvolnum>7</manvolnum></citerefentry>)
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ package.bin ];
|
||||
|
||||
environment.etc = mapAttrs' (name: opts:
|
||||
nameValuePair "awstats/awstats.${name}.conf" {
|
||||
source = pkgs.runCommand "awstats.${name}.conf"
|
||||
{ preferLocalBuild = true; }
|
||||
(''
|
||||
sed \
|
||||
''
|
||||
# set up mail stats
|
||||
+ optionalString (opts.type == "mail")
|
||||
''
|
||||
-e 's|^\(LogType\)=.*$|\1=M|' \
|
||||
-e 's|^\(LevelForBrowsersDetection\)=.*$|\1=0|' \
|
||||
-e 's|^\(LevelForOSDetection\)=.*$|\1=0|' \
|
||||
-e 's|^\(LevelForRefererAnalyze\)=.*$|\1=0|' \
|
||||
-e 's|^\(LevelForRobotsDetection\)=.*$|\1=0|' \
|
||||
-e 's|^\(LevelForSearchEnginesDetection\)=.*$|\1=0|' \
|
||||
-e 's|^\(LevelForFileTypesDetection\)=.*$|\1=0|' \
|
||||
-e 's|^\(LevelForWormsDetection\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowMenu\)=.*$|\1=1|' \
|
||||
-e 's|^\(ShowSummary\)=.*$|\1=HB|' \
|
||||
-e 's|^\(ShowMonthStats\)=.*$|\1=HB|' \
|
||||
-e 's|^\(ShowDaysOfMonthStats\)=.*$|\1=HB|' \
|
||||
-e 's|^\(ShowDaysOfWeekStats\)=.*$|\1=HB|' \
|
||||
-e 's|^\(ShowHoursStats\)=.*$|\1=HB|' \
|
||||
-e 's|^\(ShowDomainsStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowHostsStats\)=.*$|\1=HB|' \
|
||||
-e 's|^\(ShowAuthenticatedUsers\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowRobotsStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowEMailSenders\)=.*$|\1=HBML|' \
|
||||
-e 's|^\(ShowEMailReceivers\)=.*$|\1=HBML|' \
|
||||
-e 's|^\(ShowSessionsStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowPagesStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowFileTypesStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowFileSizesStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowBrowsersStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowOSStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowOriginStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowKeyphrasesStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowKeywordsStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowMiscStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowHTTPErrorsStats\)=.*$|\1=0|' \
|
||||
-e 's|^\(ShowSMTPErrorsStats\)=.*$|\1=1|' \
|
||||
''
|
||||
+
|
||||
# common options
|
||||
''
|
||||
-e 's|^\(DirData\)=.*$|\1="${cfg.dataDir}/${name}"|' \
|
||||
-e 's|^\(DirIcons\)=.*$|\1="icons"|' \
|
||||
-e 's|^\(CreateDirDataIfNotExists\)=.*$|\1=1|' \
|
||||
-e 's|^\(SiteDomain\)=.*$|\1="${name}"|' \
|
||||
-e 's|^\(LogFile\)=.*$|\1="${opts.logFile}"|' \
|
||||
-e 's|^\(LogFormat\)=.*$|\1="${opts.logFormat}"|' \
|
||||
''
|
||||
+
|
||||
# extra config
|
||||
concatStringsSep "\n" (mapAttrsToList (n: v: ''
|
||||
-e 's|^\(${n}\)=.*$|\1="${v}"|' \
|
||||
'') opts.extraConfig)
|
||||
+
|
||||
''
|
||||
< '${package.out}/wwwroot/cgi-bin/awstats.model.conf' > "$out"
|
||||
'');
|
||||
}) cfg.configs;
|
||||
|
||||
# create data directory with the correct permissions
|
||||
systemd.tmpfiles.rules =
|
||||
[ "d '${cfg.dataDir}' 755 root root - -" ] ++
|
||||
mapAttrsToList (name: opts: "d '${cfg.dataDir}/${name}' 755 root root - -") cfg.configs ++
|
||||
[ "Z '${cfg.dataDir}' 755 root root - -" ];
|
||||
|
||||
# nginx options
|
||||
services.nginx.virtualHosts = mapAttrs'(name: opts: {
|
||||
name = opts.webService.hostname;
|
||||
value = {
|
||||
locations = {
|
||||
"${opts.webService.urlPrefix}/css/" = {
|
||||
alias = "${package.out}/wwwroot/css/";
|
||||
};
|
||||
"${opts.webService.urlPrefix}/icons/" = {
|
||||
alias = "${package.out}/wwwroot/icon/";
|
||||
};
|
||||
"${opts.webService.urlPrefix}/" = {
|
||||
alias = "${cfg.dataDir}/${name}/";
|
||||
extraConfig = ''
|
||||
autoindex on;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}) webServices;
|
||||
|
||||
# update awstats
|
||||
systemd.services = mkIf (cfg.updateAt != null) (mapAttrs' (name: opts:
|
||||
nameValuePair "awstats-${name}-update" {
|
||||
description = "update awstats for ${name}";
|
||||
script = optionalString (opts.type == "mail")
|
||||
''
|
||||
if [[ -f "${cfg.dataDir}/${name}-cursor" ]]; then
|
||||
CURSOR="$(cat "${cfg.dataDir}/${name}-cursor" | tr -d '\n')"
|
||||
if [[ -n "$CURSOR" ]]; then
|
||||
echo "Using cursor: $CURSOR"
|
||||
export OLD_CURSOR="--cursor $CURSOR"
|
||||
fi
|
||||
fi
|
||||
NEW_CURSOR="$(journalctl $OLD_CURSOR -u postfix.service --show-cursor | tail -n 1 | tr -d '\n' | sed -e 's#^-- cursor: \(.*\)#\1#')"
|
||||
echo "New cursor: $NEW_CURSOR"
|
||||
${package.bin}/bin/awstats -update -config=${name}
|
||||
if [ -n "$NEW_CURSOR" ]; then
|
||||
echo -n "$NEW_CURSOR" > ${cfg.dataDir}/${name}-cursor
|
||||
fi
|
||||
'' + ''
|
||||
${package.out}/share/awstats/tools/awstats_buildstaticpages.pl \
|
||||
-config=${name} -update -dir=${cfg.dataDir}/${name} \
|
||||
-awstatsprog=${package.bin}/bin/awstats
|
||||
'';
|
||||
startAt = cfg.updateAt;
|
||||
}) cfg.configs);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
253
nixos/modules/services/logging/filebeat.nix
Normal file
253
nixos/modules/services/logging/filebeat.nix
Normal file
|
|
@ -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 <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><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><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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
58
nixos/modules/services/logging/fluentd.nix
Normal file
58
nixos/modules/services/logging/fluentd.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.fluentd;
|
||||
|
||||
pluginArgs = concatStringsSep " " (map (x: "-p ${x}") cfg.plugins);
|
||||
in {
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.fluentd = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable fluentd.";
|
||||
};
|
||||
|
||||
config = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Fluentd config.";
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.path;
|
||||
default = pkgs.fluentd;
|
||||
defaultText = literalExpression "pkgs.fluentd";
|
||||
description = "The fluentd package to use.";
|
||||
};
|
||||
|
||||
plugins = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of plugin paths to pass into fluentd. It will make plugins defined in ruby files
|
||||
there available in your config.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.fluentd = with pkgs; {
|
||||
description = "Fluentd Daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/fluentd -c ${pkgs.writeText "fluentd.conf" cfg.config} ${pluginArgs}";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
169
nixos/modules/services/logging/graylog.nix
Normal file
169
nixos/modules/services/logging/graylog.nix
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.graylog;
|
||||
|
||||
confFile = pkgs.writeText "graylog.conf" ''
|
||||
is_master = ${boolToString cfg.isMaster}
|
||||
node_id_file = ${cfg.nodeIdFile}
|
||||
password_secret = ${cfg.passwordSecret}
|
||||
root_username = ${cfg.rootUsername}
|
||||
root_password_sha2 = ${cfg.rootPasswordSha2}
|
||||
elasticsearch_hosts = ${concatStringsSep "," cfg.elasticsearchHosts}
|
||||
message_journal_dir = ${cfg.messageJournalDir}
|
||||
mongodb_uri = ${cfg.mongodbUri}
|
||||
plugin_dir = /var/lib/graylog/plugins
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
glPlugins = pkgs.buildEnv {
|
||||
name = "graylog-plugins";
|
||||
paths = cfg.plugins;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.graylog = {
|
||||
|
||||
enable = mkEnableOption "Graylog";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.graylog;
|
||||
defaultText = literalExpression "pkgs.graylog";
|
||||
description = "Graylog package to use.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "graylog";
|
||||
description = "User account under which graylog runs";
|
||||
};
|
||||
|
||||
isMaster = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Whether this is the master instance of your Graylog cluster";
|
||||
};
|
||||
|
||||
nodeIdFile = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/graylog/server/node-id";
|
||||
description = "Path of the file containing the graylog node-id";
|
||||
};
|
||||
|
||||
passwordSecret = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
You MUST set a secret to secure/pepper the stored user passwords here. Use at least 64 characters.
|
||||
Generate one by using for example: pwgen -N 1 -s 96
|
||||
'';
|
||||
};
|
||||
|
||||
rootUsername = mkOption {
|
||||
type = types.str;
|
||||
default = "admin";
|
||||
description = "Name of the default administrator user";
|
||||
};
|
||||
|
||||
rootPasswordSha2 = mkOption {
|
||||
type = types.str;
|
||||
example = "e3c652f0ba0b4801205814f8b6bc49672c4c74e25b497770bb89b22cdeb4e952";
|
||||
description = ''
|
||||
You MUST specify a hash password for the root user (which you only need to initially set up the
|
||||
system and in case you lose connectivity to your authentication backend)
|
||||
This password cannot be changed using the API or via the web interface. If you need to change it,
|
||||
modify it here.
|
||||
Create one by using for example: echo -n yourpassword | shasum -a 256
|
||||
and use the resulting hash value as string for the option
|
||||
'';
|
||||
};
|
||||
|
||||
elasticsearchHosts = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = literalExpression ''[ "http://node1:9200" "http://user:password@node2:19200" ]'';
|
||||
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";
|
||||
};
|
||||
|
||||
messageJournalDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/graylog/data/journal";
|
||||
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";
|
||||
};
|
||||
|
||||
mongodbUri = mkOption {
|
||||
type = types.str;
|
||||
default = "mongodb://localhost/graylog";
|
||||
description = "MongoDB connection string. See http://docs.mongodb.org/manual/reference/connection-string/ for details";
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Any other configuration options you might want to add";
|
||||
};
|
||||
|
||||
plugins = mkOption {
|
||||
description = "Extra graylog plugins";
|
||||
default = [ ];
|
||||
type = types.listOf types.package;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users.users = mkIf (cfg.user == "graylog") {
|
||||
graylog = {
|
||||
isSystemUser = true;
|
||||
group = "graylog";
|
||||
description = "Graylog server daemon user";
|
||||
};
|
||||
};
|
||||
users.groups = mkIf (cfg.user == "graylog") { graylog = {}; };
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${cfg.messageJournalDir}' - ${cfg.user} - - -"
|
||||
];
|
||||
|
||||
systemd.services.graylog = {
|
||||
description = "Graylog Server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
GRAYLOG_CONF = "${confFile}";
|
||||
};
|
||||
path = [ pkgs.which pkgs.procps ];
|
||||
preStart = ''
|
||||
rm -rf /var/lib/graylog/plugins || true
|
||||
mkdir -p /var/lib/graylog/plugins -m 755
|
||||
|
||||
mkdir -p "$(dirname ${cfg.nodeIdFile})"
|
||||
chown -R ${cfg.user} "$(dirname ${cfg.nodeIdFile})"
|
||||
|
||||
for declarativeplugin in `ls ${glPlugins}/bin/`; do
|
||||
ln -sf ${glPlugins}/bin/$declarativeplugin /var/lib/graylog/plugins/$declarativeplugin
|
||||
done
|
||||
for includedplugin in `ls ${cfg.package}/plugin/`; do
|
||||
ln -s ${cfg.package}/plugin/$includedplugin /var/lib/graylog/plugins/$includedplugin || true
|
||||
done
|
||||
'';
|
||||
serviceConfig = {
|
||||
User="${cfg.user}";
|
||||
StateDirectory = "graylog";
|
||||
ExecStart = "${cfg.package}/bin/graylogctl run";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
74
nixos/modules/services/logging/heartbeat.nix
Normal file
74
nixos/modules/services/logging/heartbeat.nix
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.heartbeat;
|
||||
|
||||
heartbeatYml = pkgs.writeText "heartbeat.yml" ''
|
||||
name: ${cfg.name}
|
||||
tags: ${builtins.toJSON cfg.tags}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
||||
services.heartbeat = {
|
||||
|
||||
enable = mkEnableOption "heartbeat";
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "heartbeat";
|
||||
description = "Name of the beat";
|
||||
};
|
||||
|
||||
tags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Tags to place on the shipped log messages";
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/heartbeat";
|
||||
description = "The state directory. heartbeat's own logs and other data are stored here.";
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = ''
|
||||
heartbeat.monitors:
|
||||
- type: http
|
||||
urls: ["http://localhost:9200"]
|
||||
schedule: '@every 10s'
|
||||
'';
|
||||
description = "Any other configuration options you want to add";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${cfg.stateDir}' - nobody nogroup - -"
|
||||
];
|
||||
|
||||
systemd.services.heartbeat = with pkgs; {
|
||||
description = "heartbeat log shipper";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
preStart = ''
|
||||
mkdir -p "${cfg.stateDir}"/{data,logs}
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = "nobody";
|
||||
AmbientCapabilities = "cap_net_raw";
|
||||
ExecStart = "${pkgs.heartbeat}/bin/heartbeat -c \"${heartbeatYml}\" -path.data \"${cfg.stateDir}/data\" -path.logs \"${cfg.stateDir}/logs\"";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
94
nixos/modules/services/logging/journalbeat.nix
Normal file
94
nixos/modules/services/logging/journalbeat.nix
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.journalbeat;
|
||||
|
||||
journalbeatYml = pkgs.writeText "journalbeat.yml" ''
|
||||
name: ${cfg.name}
|
||||
tags: ${builtins.toJSON cfg.tags}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
||||
services.journalbeat = {
|
||||
|
||||
enable = mkEnableOption "journalbeat";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.journalbeat;
|
||||
defaultText = literalExpression "pkgs.journalbeat";
|
||||
description = ''
|
||||
The journalbeat package to use
|
||||
'';
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "journalbeat";
|
||||
description = "Name of the beat";
|
||||
};
|
||||
|
||||
tags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Tags to place on the shipped log messages";
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.str;
|
||||
default = "journalbeat";
|
||||
description = ''
|
||||
Directory below <literal>/var/lib/</literal> to store journalbeat's
|
||||
own logs and other data. This directory will be created automatically
|
||||
using systemd's StateDirectory mechanism.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Any other configuration options you want to add";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = !hasPrefix "/" cfg.stateDir;
|
||||
message =
|
||||
"The option services.journalbeat.stateDir shouldn't be an absolute directory." +
|
||||
" It should be a directory relative to /var/lib/.";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.journalbeat = {
|
||||
description = "Journalbeat log shipper";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = [ "elasticsearch.service" ];
|
||||
after = [ "elasticsearch.service" ];
|
||||
preStart = ''
|
||||
mkdir -p ${cfg.stateDir}/data
|
||||
mkdir -p ${cfg.stateDir}/logs
|
||||
'';
|
||||
serviceConfig = {
|
||||
StateDirectory = cfg.stateDir;
|
||||
ExecStart = ''
|
||||
${cfg.package}/bin/journalbeat \
|
||||
-c ${journalbeatYml} \
|
||||
-path.data /var/lib/${cfg.stateDir}/data \
|
||||
-path.logs /var/lib/${cfg.stateDir}/logs'';
|
||||
Restart = "always";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
112
nixos/modules/services/logging/journaldriver.nix
Normal file
112
nixos/modules/services/logging/journaldriver.nix
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# This module implements a systemd service for running journaldriver,
|
||||
# a log forwarding agent that sends logs from journald to Stackdriver
|
||||
# Logging.
|
||||
#
|
||||
# It can be enabled without extra configuration when running on GCP.
|
||||
# On machines hosted elsewhere, the other configuration options need
|
||||
# to be set.
|
||||
#
|
||||
# For further information please consult the documentation in the
|
||||
# upstream repository at: https://github.com/tazjin/journaldriver/
|
||||
|
||||
{ config, lib, pkgs, ...}:
|
||||
|
||||
with lib; let cfg = config.services.journaldriver;
|
||||
in {
|
||||
options.services.journaldriver = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable journaldriver to forward journald logs to
|
||||
Stackdriver Logging.
|
||||
'';
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
type = types.str;
|
||||
default = "info";
|
||||
description = ''
|
||||
Log level at which journaldriver logs its own output.
|
||||
'';
|
||||
};
|
||||
|
||||
logName = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Configures the name of the target log in Stackdriver Logging.
|
||||
This option can be set to, for example, the hostname of a
|
||||
machine to improve the user experience in the logging
|
||||
overview.
|
||||
'';
|
||||
};
|
||||
|
||||
googleCloudProject = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Configures the name of the Google Cloud project to which to
|
||||
forward journald logs.
|
||||
|
||||
This option is required on non-GCP machines, but should not be
|
||||
set on GCP instances.
|
||||
'';
|
||||
};
|
||||
|
||||
logStream = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Configures the name of the Stackdriver Logging log stream into
|
||||
which to write journald entries.
|
||||
|
||||
This option is required on non-GCP machines, but should not be
|
||||
set on GCP instances.
|
||||
'';
|
||||
};
|
||||
|
||||
applicationCredentials = mkOption {
|
||||
type = with types; nullOr path;
|
||||
default = null;
|
||||
description = ''
|
||||
Path to the service account private key (in JSON-format) used
|
||||
to forward log entries to Stackdriver Logging on non-GCP
|
||||
instances.
|
||||
|
||||
This option is required on non-GCP machines, but should not be
|
||||
set on GCP instances.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.journaldriver = {
|
||||
description = "Stackdriver Logging journal forwarder";
|
||||
script = "${pkgs.journaldriver}/bin/journaldriver";
|
||||
after = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
DynamicUser = true;
|
||||
|
||||
# This directive lets systemd automatically configure
|
||||
# permissions on /var/lib/journaldriver, the directory in
|
||||
# which journaldriver persists its cursor state.
|
||||
StateDirectory = "journaldriver";
|
||||
|
||||
# This group is required for accessing journald.
|
||||
SupplementaryGroups = "systemd-journal";
|
||||
};
|
||||
|
||||
environment = {
|
||||
RUST_LOG = cfg.logLevel;
|
||||
LOG_NAME = cfg.logName;
|
||||
LOG_STREAM = cfg.logStream;
|
||||
GOOGLE_CLOUD_PROJECT = cfg.googleCloudProject;
|
||||
GOOGLE_APPLICATION_CREDENTIALS = cfg.applicationCredentials;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
265
nixos/modules/services/logging/journalwatch.nix
Normal file
265
nixos/modules/services/logging/journalwatch.nix
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.journalwatch;
|
||||
user = "journalwatch";
|
||||
# for journal access
|
||||
group = "systemd-journal";
|
||||
dataDir = "/var/lib/${user}";
|
||||
|
||||
journalwatchConfig = pkgs.writeText "config" (''
|
||||
# (File Generated by NixOS journalwatch module.)
|
||||
[DEFAULT]
|
||||
mail_binary = ${cfg.mailBinary}
|
||||
priority = ${toString cfg.priority}
|
||||
mail_from = ${cfg.mailFrom}
|
||||
''
|
||||
+ optionalString (cfg.mailTo != null) ''
|
||||
mail_to = ${cfg.mailTo}
|
||||
''
|
||||
+ cfg.extraConfig);
|
||||
|
||||
journalwatchPatterns = pkgs.writeText "patterns" ''
|
||||
# (File Generated by NixOS journalwatch module.)
|
||||
|
||||
${mkPatterns cfg.filterBlocks}
|
||||
'';
|
||||
|
||||
# empty line at the end needed to to separate the blocks
|
||||
mkPatterns = filterBlocks: concatStringsSep "\n" (map (block: ''
|
||||
${block.match}
|
||||
${block.filters}
|
||||
|
||||
'') filterBlocks);
|
||||
|
||||
# can't use joinSymlinks directly, because when we point $XDG_CONFIG_HOME
|
||||
# to the /nix/store path, we still need the subdirectory "journalwatch" inside that
|
||||
# to match journalwatch's expectations
|
||||
journalwatchConfigDir = pkgs.runCommand "journalwatch-config"
|
||||
{ preferLocalBuild = true; allowSubstitutes = false; }
|
||||
''
|
||||
mkdir -p $out/journalwatch
|
||||
ln -sf ${journalwatchConfig} $out/journalwatch/config
|
||||
ln -sf ${journalwatchPatterns} $out/journalwatch/patterns
|
||||
'';
|
||||
|
||||
|
||||
in {
|
||||
options = {
|
||||
services.journalwatch = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If enabled, periodically check the journal with journalwatch and report the results by mail.
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = types.int;
|
||||
default = 6;
|
||||
description = ''
|
||||
Lowest priority of message to be considered.
|
||||
A value between 7 ("debug"), and 0 ("emerg"). Defaults to 6 ("info").
|
||||
If you don't care about anything with "info" priority, you can reduce
|
||||
this to e.g. 5 ("notice") to considerably reduce the amount of
|
||||
messages without needing many <option>filterBlocks</option>.
|
||||
'';
|
||||
};
|
||||
|
||||
# HACK: this is a workaround for journalwatch's usage of socket.getfqdn() which always returns localhost if
|
||||
# there's an alias for the localhost on a separate line in /etc/hosts, or take for ages if it's not present and
|
||||
# then return something right-ish in the direction of /etc/hostname. Just bypass it completely.
|
||||
mailFrom = mkOption {
|
||||
type = types.str;
|
||||
default = "journalwatch@${config.networking.hostName}";
|
||||
defaultText = literalExpression ''"journalwatch@''${config.networking.hostName}"'';
|
||||
description = ''
|
||||
Mail address to send journalwatch reports from.
|
||||
'';
|
||||
};
|
||||
|
||||
mailTo = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Mail address to send journalwatch reports to.
|
||||
'';
|
||||
};
|
||||
|
||||
mailBinary = mkOption {
|
||||
type = types.path;
|
||||
default = "/run/wrappers/bin/sendmail";
|
||||
description = ''
|
||||
Sendmail-compatible binary to be used to send the messages.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = ''
|
||||
Extra lines to be added verbatim to the journalwatch/config configuration file.
|
||||
You can add any commandline argument to the config, without the '--'.
|
||||
See <literal>journalwatch --help</literal> for all arguments and their description.
|
||||
'';
|
||||
};
|
||||
|
||||
filterBlocks = mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
match = mkOption {
|
||||
type = types.str;
|
||||
example = "SYSLOG_IDENTIFIER = systemd";
|
||||
description = ''
|
||||
Syntax: <literal>field = value</literal>
|
||||
Specifies the log entry <literal>field</literal> this block should apply to.
|
||||
If the <literal>field</literal> of a message matches this <literal>value</literal>,
|
||||
this patternBlock's <option>filters</option> are applied.
|
||||
If <literal>value</literal> starts and ends with a slash, it is interpreted as
|
||||
an extended python regular expression, if not, it's an exact match.
|
||||
The journal fields are explained in systemd.journal-fields(7).
|
||||
'';
|
||||
};
|
||||
|
||||
filters = mkOption {
|
||||
type = types.str;
|
||||
example = ''
|
||||
(Stopped|Stopping|Starting|Started) .*
|
||||
(Reached target|Stopped target) .*
|
||||
'';
|
||||
description = ''
|
||||
The filters to apply on all messages which satisfy <option>match</option>.
|
||||
Any of those messages that match any specified filter will be removed from journalwatch's output.
|
||||
Each filter is an extended Python regular expression.
|
||||
You can specify multiple filters and separate them by newlines.
|
||||
Lines starting with '#' are comments. Inline-comments are not permitted.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
example = [
|
||||
# examples taken from upstream
|
||||
{
|
||||
match = "_SYSTEMD_UNIT = systemd-logind.service";
|
||||
filters = ''
|
||||
New session [a-z]?\d+ of user \w+\.
|
||||
Removed session [a-z]?\d+\.
|
||||
'';
|
||||
}
|
||||
|
||||
{
|
||||
match = "SYSLOG_IDENTIFIER = /(CROND|crond)/";
|
||||
filters = ''
|
||||
pam_unix\(crond:session\): session (opened|closed) for user \w+
|
||||
\(\w+\) CMD .*
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
# another example from upstream.
|
||||
# very useful on priority = 6, and required as journalwatch throws an error when no pattern is defined at all.
|
||||
default = [
|
||||
{
|
||||
match = "SYSLOG_IDENTIFIER = systemd";
|
||||
filters = ''
|
||||
(Stopped|Stopping|Starting|Started) .*
|
||||
(Created slice|Removed slice) user-\d*\.slice\.
|
||||
Received SIGRTMIN\+24 from PID .*
|
||||
(Reached target|Stopped target) .*
|
||||
Startup finished in \d*ms\.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
description = ''
|
||||
filterBlocks can be defined to blacklist journal messages which are not errors.
|
||||
Each block matches on a log entry field, and the filters in that block then are matched
|
||||
against all messages with a matching log entry field.
|
||||
|
||||
All messages whose PRIORITY is at least 6 (INFO) are processed by journalwatch.
|
||||
If you don't specify any filterBlocks, PRIORITY is reduced to 5 (NOTICE) by default.
|
||||
|
||||
All regular expressions are extended Python regular expressions, for details
|
||||
see: http://doc.pyschools.com/html/regex.html
|
||||
'';
|
||||
};
|
||||
|
||||
interval = mkOption {
|
||||
type = types.str;
|
||||
default = "hourly";
|
||||
description = ''
|
||||
How often to run journalwatch.
|
||||
|
||||
The format is described in systemd.time(7).
|
||||
'';
|
||||
};
|
||||
accuracy = mkOption {
|
||||
type = types.str;
|
||||
default = "10min";
|
||||
description = ''
|
||||
The time window around the interval in which the journalwatch run will be scheduled.
|
||||
|
||||
The format is described in systemd.time(7).
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users.users.${user} = {
|
||||
isSystemUser = true;
|
||||
home = dataDir;
|
||||
group = group;
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
# present since NixOS 19.09: remove old stateful symlink join directory,
|
||||
# which has been replaced with the journalwatchConfigDir store path
|
||||
"R ${dataDir}/config"
|
||||
];
|
||||
|
||||
systemd.services.journalwatch = {
|
||||
|
||||
environment = {
|
||||
# journalwatch stores the last processed timpestamp here
|
||||
# the share subdirectory is historic now that config home lives in /nix/store,
|
||||
# but moving this in a backwards-compatible way is much more work than what's justified
|
||||
# for cleaning that up.
|
||||
XDG_DATA_HOME = "${dataDir}/share";
|
||||
XDG_CONFIG_HOME = journalwatchConfigDir;
|
||||
};
|
||||
serviceConfig = {
|
||||
User = user;
|
||||
Group = group;
|
||||
Type = "oneshot";
|
||||
# requires a relative directory name to create beneath /var/lib
|
||||
StateDirectory = user;
|
||||
StateDirectoryMode = 0750;
|
||||
ExecStart = "${pkgs.python3Packages.journalwatch}/bin/journalwatch mail";
|
||||
# lowest CPU and IO priority, but both still in best-effort class to prevent starvation
|
||||
Nice=19;
|
||||
IOSchedulingPriority=7;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.journalwatch = {
|
||||
description = "Periodic journalwatch run";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = cfg.interval;
|
||||
AccuracySec = cfg.accuracy;
|
||||
Persistent = true;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ florianjacob ];
|
||||
};
|
||||
}
|
||||
9
nixos/modules/services/logging/klogd.nix
Normal file
9
nixos/modules/services/logging/klogd.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{ lib, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "security" "klogd" "enable" ] ''
|
||||
Logging of kernel messages is now handled by systemd.
|
||||
'')
|
||||
];
|
||||
}
|
||||
242
nixos/modules/services/logging/logcheck.nix
Normal file
242
nixos/modules/services/logging/logcheck.nix
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.logcheck;
|
||||
|
||||
defaultRules = pkgs.runCommand "logcheck-default-rules" { preferLocalBuild = true; } ''
|
||||
cp -prd ${pkgs.logcheck}/etc/logcheck $out
|
||||
chmod u+w $out
|
||||
rm -r $out/logcheck.*
|
||||
'';
|
||||
|
||||
rulesDir = pkgs.symlinkJoin
|
||||
{ name = "logcheck-rules-dir";
|
||||
paths = ([ defaultRules ] ++ cfg.extraRulesDirs);
|
||||
};
|
||||
|
||||
configFile = pkgs.writeText "logcheck.conf" cfg.config;
|
||||
|
||||
logFiles = pkgs.writeText "logcheck.logfiles" cfg.files;
|
||||
|
||||
flags = "-r ${rulesDir} -c ${configFile} -L ${logFiles} -${levelFlag} -m ${cfg.mailTo}";
|
||||
|
||||
levelFlag = getAttrFromPath [cfg.level]
|
||||
{ paranoid = "p";
|
||||
server = "s";
|
||||
workstation = "w";
|
||||
};
|
||||
|
||||
cronJob = ''
|
||||
@reboot logcheck env PATH=/run/wrappers/bin:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck -R ${flags}
|
||||
2 ${cfg.timeOfDay} * * * logcheck env PATH=/run/wrappers/bin:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck ${flags}
|
||||
'';
|
||||
|
||||
writeIgnoreRule = name: {level, regex, ...}:
|
||||
pkgs.writeTextFile
|
||||
{ inherit name;
|
||||
destination = "/ignore.d.${level}/${name}";
|
||||
text = ''
|
||||
^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ ${regex}
|
||||
'';
|
||||
};
|
||||
|
||||
writeIgnoreCronRule = name: {level, user, regex, cmdline, ...}:
|
||||
let escapeRegex = escape (stringToCharacters "\\[]{}()^$?*+|.");
|
||||
cmdline_ = builtins.unsafeDiscardStringContext cmdline;
|
||||
re = if regex != "" then regex else if cmdline_ == "" then ".*" else escapeRegex cmdline_;
|
||||
in writeIgnoreRule "cron-${name}" {
|
||||
inherit level;
|
||||
regex = ''
|
||||
(/usr/bin/)?cron\[[0-9]+\]: \(${user}\) CMD \(${re}\)$
|
||||
'';
|
||||
};
|
||||
|
||||
levelOption = mkOption {
|
||||
default = "server";
|
||||
type = types.enum [ "workstation" "server" "paranoid" ];
|
||||
description = ''
|
||||
Set the logcheck level.
|
||||
'';
|
||||
};
|
||||
|
||||
ignoreOptions = {
|
||||
options = {
|
||||
level = levelOption;
|
||||
|
||||
regex = mkOption {
|
||||
default = "";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Regex specifying which log lines to ignore.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ignoreCronOptions = {
|
||||
options = {
|
||||
user = mkOption {
|
||||
default = "root";
|
||||
type = types.str;
|
||||
description = ''
|
||||
User that runs the cronjob.
|
||||
'';
|
||||
};
|
||||
|
||||
cmdline = mkOption {
|
||||
default = "";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Command line for the cron job. Will be turned into a regex for the logcheck ignore rule.
|
||||
'';
|
||||
};
|
||||
|
||||
timeArgs = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr (types.str);
|
||||
example = "02 06 * * *";
|
||||
description = ''
|
||||
"min hr dom mon dow" crontab time args, to auto-create a cronjob too.
|
||||
Leave at null to not do this and just add a logcheck ignore rule.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.logcheck = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enable the logcheck cron job.
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
default = "logcheck";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Username for the logcheck user.
|
||||
'';
|
||||
};
|
||||
|
||||
timeOfDay = mkOption {
|
||||
default = "*";
|
||||
example = "6";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Time of day to run logcheck. A logcheck will be scheduled at xx:02 each day.
|
||||
Leave default (*) to run every hour. Of course when nothing special was logged,
|
||||
logcheck will be silent.
|
||||
'';
|
||||
};
|
||||
|
||||
mailTo = mkOption {
|
||||
default = "root";
|
||||
example = "you@domain.com";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Email address to send reports to.
|
||||
'';
|
||||
};
|
||||
|
||||
level = mkOption {
|
||||
default = "server";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Set the logcheck level. Either "workstation", "server", or "paranoid".
|
||||
'';
|
||||
};
|
||||
|
||||
config = mkOption {
|
||||
default = "FQDN=1";
|
||||
type = types.lines;
|
||||
description = ''
|
||||
Config options that you would like in logcheck.conf.
|
||||
'';
|
||||
};
|
||||
|
||||
files = mkOption {
|
||||
default = [ "/var/log/messages" ];
|
||||
type = types.listOf types.path;
|
||||
example = [ "/var/log/messages" "/var/log/mail" ];
|
||||
description = ''
|
||||
Which log files to check.
|
||||
'';
|
||||
};
|
||||
|
||||
extraRulesDirs = mkOption {
|
||||
default = [];
|
||||
example = [ "/etc/logcheck" ];
|
||||
type = types.listOf types.path;
|
||||
description = ''
|
||||
Directories with extra rules.
|
||||
'';
|
||||
};
|
||||
|
||||
ignore = mkOption {
|
||||
default = {};
|
||||
description = ''
|
||||
This option defines extra ignore rules.
|
||||
'';
|
||||
type = with types; attrsOf (submodule ignoreOptions);
|
||||
};
|
||||
|
||||
ignoreCron = mkOption {
|
||||
default = {};
|
||||
description = ''
|
||||
This option defines extra ignore rules for cronjobs.
|
||||
'';
|
||||
type = with types; attrsOf (submodule ignoreCronOptions);
|
||||
};
|
||||
|
||||
extraGroups = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.str;
|
||||
example = [ "postdrop" "mongodb" ];
|
||||
description = ''
|
||||
Extra groups for the logcheck user, for example to be able to use sendmail,
|
||||
or to access certain log files.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.logcheck.extraRulesDirs =
|
||||
mapAttrsToList writeIgnoreRule cfg.ignore
|
||||
++ mapAttrsToList writeIgnoreCronRule cfg.ignoreCron;
|
||||
|
||||
users.users = optionalAttrs (cfg.user == "logcheck") {
|
||||
logcheck = {
|
||||
group = "logcheck";
|
||||
isSystemUser = true;
|
||||
shell = "/bin/sh";
|
||||
description = "Logcheck user account";
|
||||
extraGroups = cfg.extraGroups;
|
||||
};
|
||||
};
|
||||
users.groups = optionalAttrs (cfg.user == "logcheck") {
|
||||
logcheck = {};
|
||||
};
|
||||
|
||||
system.activationScripts.logcheck = ''
|
||||
mkdir -m 700 -p /var/{lib,lock}/logcheck
|
||||
chown ${cfg.user} /var/{lib,lock}/logcheck
|
||||
'';
|
||||
|
||||
services.cron.systemCronJobs =
|
||||
let withTime = name: {timeArgs, ...}: timeArgs != null;
|
||||
mkCron = name: {user, cmdline, timeArgs, ...}: ''
|
||||
${timeArgs} ${user} ${cmdline}
|
||||
'';
|
||||
in mapAttrsToList mkCron (filterAttrs withTime cfg.ignoreCron)
|
||||
++ [ cronJob ];
|
||||
};
|
||||
}
|
||||
403
nixos/modules/services/logging/logrotate.nix
Normal file
403
nixos/modules/services/logging/logrotate.nix
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.logrotate;
|
||||
|
||||
# deprecated legacy compat settings
|
||||
# these options will be removed before 22.11 in the following PR:
|
||||
# https://github.com/NixOS/nixpkgs/pull/164169
|
||||
pathOpts = { name, ... }: {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to enable log rotation for this path. This can be used to explicitly disable
|
||||
logging that has been configured by NixOS.
|
||||
'';
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
internal = true;
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = name;
|
||||
defaultText = "attribute name";
|
||||
description = ''
|
||||
The path to log files to be rotated.
|
||||
Spaces are allowed and normal shell quoting rules apply,
|
||||
with ', ", and \ characters supported.
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
The user account to use for rotation.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
The group to use for rotation.
|
||||
'';
|
||||
};
|
||||
|
||||
frequency = mkOption {
|
||||
type = types.enum [ "hourly" "daily" "weekly" "monthly" "yearly" ];
|
||||
default = "daily";
|
||||
description = ''
|
||||
How often to rotate the logs.
|
||||
'';
|
||||
};
|
||||
|
||||
keep = mkOption {
|
||||
type = types.int;
|
||||
default = 20;
|
||||
description = ''
|
||||
How many rotations to keep.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Extra logrotate config options for this path. Refer to
|
||||
<link xlink:href="https://linux.die.net/man/8/logrotate"/> for details.
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = types.int;
|
||||
default = 1000;
|
||||
description = ''
|
||||
Order of this logrotate block in relation to the others. The semantics are
|
||||
the same as with `lib.mkOrder`. Smaller values have a greater priority.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config.name = name;
|
||||
};
|
||||
|
||||
generateLine = n: v:
|
||||
if builtins.elem n [ "files" "priority" "enable" "global" ] || v == null then null
|
||||
else if builtins.elem n [ "extraConfig" "frequency" ] then "${v}\n"
|
||||
else if builtins.elem n [ "firstaction" "lastaction" "prerotate" "postrotate" "preremove" ]
|
||||
then "${n}\n ${v}\n endscript\n"
|
||||
else if isInt v then "${n} ${toString v}\n"
|
||||
else if v == true then "${n}\n"
|
||||
else if v == false then "no${n}\n"
|
||||
else "${n} ${v}\n";
|
||||
generateSection = indent: settings: concatStringsSep (fixedWidthString indent " " "") (
|
||||
filter (x: x != null) (mapAttrsToList generateLine settings)
|
||||
);
|
||||
|
||||
# generateSection includes a final newline hence weird closing brace
|
||||
mkConf = settings:
|
||||
if settings.global or false then generateSection 0 settings
|
||||
else ''
|
||||
${concatMapStringsSep "\n" (files: ''"${files}"'') (toList settings.files)} {
|
||||
${generateSection 2 settings}}
|
||||
'';
|
||||
|
||||
# below two mapPaths are compat functions
|
||||
mapPathOptToSetting = n: v:
|
||||
if n == "keep" then nameValuePair "rotate" v
|
||||
else if n == "path" then nameValuePair "files" v
|
||||
else nameValuePair n v;
|
||||
|
||||
mapPathsToSettings = path: pathOpts:
|
||||
nameValuePair path (
|
||||
filterAttrs (n: v: ! builtins.elem n [ "user" "group" "name" ] && v != "") (
|
||||
(mapAttrs' mapPathOptToSetting pathOpts) //
|
||||
{
|
||||
su =
|
||||
if pathOpts.user != null
|
||||
then "${pathOpts.user} ${pathOpts.group}"
|
||||
else null;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
settings = sortProperties (attrValues (filterAttrs (_: settings: settings.enable) (
|
||||
foldAttrs recursiveUpdate { } [
|
||||
{
|
||||
header = {
|
||||
enable = true;
|
||||
missingok = true;
|
||||
notifempty = true;
|
||||
frequency = "weekly";
|
||||
rotate = 4;
|
||||
};
|
||||
# compat section
|
||||
extraConfig = {
|
||||
enable = (cfg.extraConfig != "");
|
||||
global = true;
|
||||
extraConfig = cfg.extraConfig;
|
||||
priority = 101;
|
||||
};
|
||||
}
|
||||
(mapAttrs' mapPathsToSettings cfg.paths)
|
||||
cfg.settings
|
||||
{ header = { global = true; priority = 100; }; }
|
||||
]
|
||||
)));
|
||||
configFile = pkgs.writeTextFile {
|
||||
name = "logrotate.conf";
|
||||
text = concatStringsSep "\n" (
|
||||
map mkConf settings
|
||||
);
|
||||
checkPhase = optionalString cfg.checkConfig ''
|
||||
# logrotate --debug also checks that users specified in config
|
||||
# file exist, but we only have sandboxed users here so brown these
|
||||
# out. according to man page that means su, create and createolddir.
|
||||
# files required to exist also won't be present, so missingok is forced.
|
||||
user=$(${pkgs.buildPackages.coreutils}/bin/id -un)
|
||||
group=$(${pkgs.buildPackages.coreutils}/bin/id -gn)
|
||||
sed -e "s/\bsu\s.*/su $user $group/" \
|
||||
-e "s/\b\(create\s\+[0-9]*\s*\|createolddir\s\+[0-9]*\s\+\).*/\1$user $group/" \
|
||||
-e "1imissingok" -e "s/\bnomissingok\b//" \
|
||||
$out > /tmp/logrotate.conf
|
||||
# Since this makes for very verbose builds only show real error.
|
||||
# There is no way to control log level, but logrotate hardcodes
|
||||
# 'error:' at common log level, so we can use grep, taking care
|
||||
# to keep error codes
|
||||
set -o pipefail
|
||||
if ! ${pkgs.buildPackages.logrotate}/sbin/logrotate --debug /tmp/logrotate.conf 2>&1 \
|
||||
| ( ! grep "error:" ) > /tmp/logrotate-error; then
|
||||
echo "Logrotate configuration check failed."
|
||||
echo "The failing configuration (after adjustments to pass tests in sandbox) was:"
|
||||
printf "%s\n" "-------"
|
||||
cat /tmp/logrotate.conf
|
||||
printf "%s\n" "-------"
|
||||
echo "The error reported by logrotate was as follow:"
|
||||
printf "%s\n" "-------"
|
||||
cat /tmp/logrotate-error
|
||||
printf "%s\n" "-------"
|
||||
echo "You can disable this check with services.logrotate.checkConfig = false,"
|
||||
echo "but if you think it should work please report this failure along with"
|
||||
echo "the config file being tested!"
|
||||
false
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
mailOption =
|
||||
if foldr (n: a: a || n ? mail) false (attrValues cfg.settings)
|
||||
then "--mail=${pkgs.mailutils}/bin/mail"
|
||||
else "";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "logrotate" "config" ] [ "services" "logrotate" "extraConfig" ])
|
||||
];
|
||||
|
||||
options = {
|
||||
services.logrotate = {
|
||||
enable = mkEnableOption "the logrotate systemd service" // {
|
||||
default = foldr (n: a: a || n.enable) false (attrValues cfg.settings);
|
||||
defaultText = literalExpression "cfg.settings != {}";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
default = { };
|
||||
description = ''
|
||||
logrotate freeform settings: each attribute here will define its own section,
|
||||
ordered by priority, which can either define files to rotate with their settings
|
||||
or settings common to all further files settings.
|
||||
Refer to <link xlink:href="https://linux.die.net/man/8/logrotate"/> for details.
|
||||
'';
|
||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||
freeformType = with types; attrsOf (nullOr (oneOf [ int bool str ]));
|
||||
|
||||
options = {
|
||||
enable = mkEnableOption "setting individual kill switch" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
global = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether this setting is a global option or not: set to have these
|
||||
settings apply to all files settings with a higher priority.
|
||||
'';
|
||||
};
|
||||
files = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = name;
|
||||
defaultText = ''
|
||||
The attrset name if not specified
|
||||
'';
|
||||
description = ''
|
||||
Single or list of files for which rules are defined.
|
||||
The files are quoted with double-quotes in logrotate configuration,
|
||||
so globs and spaces are supported.
|
||||
Note this setting is ignored if globals is true.
|
||||
'';
|
||||
};
|
||||
|
||||
frequency = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
How often to rotate the logs. Defaults to previously set global setting,
|
||||
which itself defauts to weekly.
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = types.int;
|
||||
default = 1000;
|
||||
description = ''
|
||||
Order of this logrotate block in relation to the others. The semantics are
|
||||
the same as with `lib.mkOrder`. Smaller values are inserted first.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
}));
|
||||
};
|
||||
|
||||
configFile = mkOption {
|
||||
type = types.path;
|
||||
default = configFile;
|
||||
defaultText = ''
|
||||
A configuration file automatically generated by NixOS.
|
||||
'';
|
||||
description = ''
|
||||
Override the configuration file used by MySQL. By default,
|
||||
NixOS generates one automatically from <xref linkend="opt-services.logrotate.settings"/>.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
pkgs.writeText "logrotate.conf" '''
|
||||
missingok
|
||||
"/var/log/*.log" {
|
||||
rotate 4
|
||||
weekly
|
||||
}
|
||||
''';
|
||||
'';
|
||||
};
|
||||
|
||||
checkConfig = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether the config should be checked at build time.
|
||||
|
||||
Some options are not checkable at build time because of the build sandbox:
|
||||
for example, the test does not know about existing files and system users are
|
||||
not known.
|
||||
These limitations mean we must adjust the file for tests (missingok is forced
|
||||
and users are replaced by dummy users), so tests are complemented by a
|
||||
logrotate-checkconf service that is enabled by default.
|
||||
This extra check can be disabled by disabling it at the systemd level with the
|
||||
<option>services.systemd.services.logrotate-checkconf.enable</option> option.
|
||||
|
||||
Conversely there are still things that might make this check fail incorrectly
|
||||
(e.g. a file path where we don't have access to intermediate directories):
|
||||
in this case you can disable the failing check with this option.
|
||||
'';
|
||||
};
|
||||
|
||||
# deprecated legacy compat settings
|
||||
paths = mkOption {
|
||||
type = with types; attrsOf (submodule pathOpts);
|
||||
default = { };
|
||||
description = ''
|
||||
Attribute set of paths to rotate. The order each block appears in the generated configuration file
|
||||
can be controlled by the <link linkend="opt-services.logrotate.paths._name_.priority">priority</link> option
|
||||
using the same semantics as `lib.mkOrder`. Smaller values have a greater priority.
|
||||
This setting has been deprecated in favor of <link linkend="opt-services.logrotate.settings">logrotate settings</link>.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
httpd = {
|
||||
path = "/var/log/httpd/*.log";
|
||||
user = config.services.httpd.user;
|
||||
group = config.services.httpd.group;
|
||||
keep = 7;
|
||||
};
|
||||
|
||||
myapp = {
|
||||
path = "/var/log/myapp/*.log";
|
||||
user = "myuser";
|
||||
group = "mygroup";
|
||||
frequency = "weekly";
|
||||
keep = 5;
|
||||
priority = 1;
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
default = "";
|
||||
type = types.lines;
|
||||
description = ''
|
||||
Extra contents to append to the logrotate configuration file. Refer to
|
||||
<link xlink:href="https://linux.die.net/man/8/logrotate"/> for details.
|
||||
This setting has been deprecated in favor of
|
||||
<link linkend="opt-services.logrotate.settings">logrotate settings</link>.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions =
|
||||
mapAttrsToList
|
||||
(name: pathOpts:
|
||||
{
|
||||
assertion = (pathOpts.user != null) == (pathOpts.group != null);
|
||||
message = ''
|
||||
If either of `services.logrotate.paths.${name}.user` or `services.logrotate.paths.${name}.group` are specified then *both* must be specified.
|
||||
'';
|
||||
})
|
||||
cfg.paths;
|
||||
|
||||
warnings =
|
||||
(mapAttrsToList
|
||||
(name: pathOpts: ''
|
||||
Using config.services.logrotate.paths.${name} is deprecated and will become unsupported in a future release.
|
||||
Please use services.logrotate.settings instead.
|
||||
'')
|
||||
cfg.paths
|
||||
) ++
|
||||
(optional (cfg.extraConfig != "") ''
|
||||
Using config.services.logrotate.extraConfig is deprecated and will become unsupported in a future release.
|
||||
Please use services.logrotate.settings with globals=true instead.
|
||||
'');
|
||||
|
||||
systemd.services.logrotate = {
|
||||
description = "Logrotate Service";
|
||||
startAt = "hourly";
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "no";
|
||||
User = "root";
|
||||
ExecStart = "${pkgs.logrotate}/sbin/logrotate ${mailOption} ${cfg.configFile}";
|
||||
};
|
||||
};
|
||||
systemd.services.logrotate-checkconf = {
|
||||
description = "Logrotate configuration check";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${pkgs.logrotate}/sbin/logrotate --debug ${cfg.configFile}";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
194
nixos/modules/services/logging/logstash.nix
Normal file
194
nixos/modules/services/logging/logstash.nix
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.logstash;
|
||||
ops = lib.optionalString;
|
||||
verbosityFlag = "--log.level " + cfg.logLevel;
|
||||
|
||||
logstashConf = pkgs.writeText "logstash.conf" ''
|
||||
input {
|
||||
${cfg.inputConfig}
|
||||
}
|
||||
|
||||
filter {
|
||||
${cfg.filterConfig}
|
||||
}
|
||||
|
||||
output {
|
||||
${cfg.outputConfig}
|
||||
}
|
||||
'';
|
||||
|
||||
logstashSettingsYml = pkgs.writeText "logstash.yml" cfg.extraSettings;
|
||||
|
||||
logstashJvmOptionsFile = pkgs.writeText "jvm.options" cfg.extraJvmOptions;
|
||||
|
||||
logstashSettingsDir = pkgs.runCommand "logstash-settings" {
|
||||
inherit logstashJvmOptionsFile;
|
||||
inherit logstashSettingsYml;
|
||||
preferLocalBuild = true;
|
||||
} ''
|
||||
mkdir -p $out
|
||||
ln -s $logstashSettingsYml $out/logstash.yml
|
||||
ln -s $logstashJvmOptionsFile $out/jvm.options
|
||||
'';
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "logstash" "address" ] [ "services" "logstash" "listenAddress" ])
|
||||
(mkRemovedOptionModule [ "services" "logstash" "enableWeb" ] "The web interface was removed from logstash")
|
||||
];
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.logstash = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable logstash.";
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.logstash;
|
||||
defaultText = literalExpression "pkgs.logstash";
|
||||
description = "Logstash package to use.";
|
||||
};
|
||||
|
||||
plugins = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [ ];
|
||||
example = literalExpression "[ pkgs.logstash-contrib ]";
|
||||
description = "The paths to find other logstash plugins in.";
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/logstash";
|
||||
description = ''
|
||||
A path to directory writable by logstash that it uses to store data.
|
||||
Plugins will also have access to this path.
|
||||
'';
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
type = types.enum [ "debug" "info" "warn" "error" "fatal" ];
|
||||
default = "warn";
|
||||
description = "Logging verbosity level.";
|
||||
};
|
||||
|
||||
filterWorkers = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
description = "The quantity of filter workers to run.";
|
||||
};
|
||||
|
||||
listenAddress = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Address on which to start webserver.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.str;
|
||||
default = "9292";
|
||||
description = "Port on which to start webserver.";
|
||||
};
|
||||
|
||||
inputConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "generator { }";
|
||||
description = "Logstash input configuration.";
|
||||
example = literalExpression ''
|
||||
'''
|
||||
# Read from journal
|
||||
pipe {
|
||||
command => "''${config.systemd.package}/bin/journalctl -f -o json"
|
||||
type => "syslog" codec => json {}
|
||||
}
|
||||
'''
|
||||
'';
|
||||
};
|
||||
|
||||
filterConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "logstash filter configuration.";
|
||||
example = ''
|
||||
if [type] == "syslog" {
|
||||
# Keep only relevant systemd fields
|
||||
# http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
|
||||
prune {
|
||||
whitelist_names => [
|
||||
"type", "@timestamp", "@version",
|
||||
"MESSAGE", "PRIORITY", "SYSLOG_FACILITY"
|
||||
]
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
outputConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "stdout { codec => rubydebug }";
|
||||
description = "Logstash output configuration.";
|
||||
example = ''
|
||||
redis { host => ["localhost"] data_type => "list" key => "logstash" codec => json }
|
||||
elasticsearch { }
|
||||
'';
|
||||
};
|
||||
|
||||
extraSettings = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Extra Logstash settings in YAML format.";
|
||||
example = ''
|
||||
pipeline:
|
||||
batch:
|
||||
size: 125
|
||||
delay: 5
|
||||
'';
|
||||
};
|
||||
|
||||
extraJvmOptions = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Extra JVM options, one per line (jvm.options format).";
|
||||
example = ''
|
||||
-Xms2g
|
||||
-Xmx2g
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.logstash = {
|
||||
description = "Logstash Daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = [ pkgs.bash ];
|
||||
serviceConfig = {
|
||||
ExecStartPre = ''${pkgs.coreutils}/bin/mkdir -p "${cfg.dataDir}" ; ${pkgs.coreutils}/bin/chmod 700 "${cfg.dataDir}"'';
|
||||
ExecStart = concatStringsSep " " (filter (s: stringLength s != 0) [
|
||||
"${cfg.package}/bin/logstash"
|
||||
"-w ${toString cfg.filterWorkers}"
|
||||
(concatMapStringsSep " " (x: "--path.plugins ${x}") cfg.plugins)
|
||||
"${verbosityFlag}"
|
||||
"-f ${logstashConf}"
|
||||
"--path.settings ${logstashSettingsDir}"
|
||||
"--path.data ${cfg.dataDir}"
|
||||
]);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
91
nixos/modules/services/logging/promtail.nix
Normal file
91
nixos/modules/services/logging/promtail.nix
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
{ config, lib, pkgs, ... }: with lib;
|
||||
let
|
||||
cfg = config.services.promtail;
|
||||
|
||||
prettyJSON = conf: pkgs.runCommandLocal "promtail-config.json" {} ''
|
||||
echo '${builtins.toJSON conf}' | ${pkgs.buildPackages.jq}/bin/jq 'del(._module)' > $out
|
||||
'';
|
||||
|
||||
allowSystemdJournal = cfg.configuration ? scrape_configs && lib.any (v: v ? journal) cfg.configuration.scrape_configs;
|
||||
|
||||
allowPositionsFile = !lib.hasPrefix "/var/cache/promtail" positionsFile;
|
||||
positionsFile = cfg.configuration.positions.filename;
|
||||
in {
|
||||
options.services.promtail = with types; {
|
||||
enable = mkEnableOption "the Promtail ingresser";
|
||||
|
||||
|
||||
configuration = mkOption {
|
||||
type = (pkgs.formats.json {}).type;
|
||||
description = ''
|
||||
Specify the configuration for Promtail in Nix.
|
||||
'';
|
||||
};
|
||||
|
||||
extraFlags = mkOption {
|
||||
type = listOf str;
|
||||
default = [];
|
||||
example = [ "--server.http-listen-port=3101" ];
|
||||
description = ''
|
||||
Specify a list of additional command line flags,
|
||||
which get escaped and are then passed to Loki.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.promtail.configuration.positions.filename = mkDefault "/var/cache/promtail/positions.yaml";
|
||||
|
||||
systemd.services.promtail = {
|
||||
description = "Promtail log ingress";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
stopIfChanged = false;
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
TimeoutStopSec = 10;
|
||||
|
||||
ExecStart = "${pkgs.promtail}/bin/promtail -config.file=${prettyJSON cfg.configuration} ${escapeShellArgs cfg.extraFlags}";
|
||||
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictSUIDSGID = true;
|
||||
PrivateMounts = true;
|
||||
CacheDirectory = "promtail";
|
||||
ReadWritePaths = lib.optional allowPositionsFile (builtins.dirOf positionsFile);
|
||||
|
||||
User = "promtail";
|
||||
Group = "promtail";
|
||||
|
||||
CapabilityBoundingSet = "";
|
||||
NoNewPrivileges = true;
|
||||
|
||||
ProtectKernelModules = true;
|
||||
SystemCallArchitectures = "native";
|
||||
ProtectKernelLogs = true;
|
||||
ProtectClock = true;
|
||||
|
||||
LockPersonality = true;
|
||||
ProtectHostname = true;
|
||||
RestrictRealtime = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
PrivateUsers = true;
|
||||
|
||||
SupplementaryGroups = lib.optional (allowSystemdJournal) "systemd-journal";
|
||||
} // (optionalAttrs (!pkgs.stdenv.isAarch64) { # FIXME: figure out why this breaks on aarch64
|
||||
SystemCallFilter = "@system-service";
|
||||
});
|
||||
};
|
||||
|
||||
users.groups.promtail = {};
|
||||
users.users.promtail = {
|
||||
description = "Promtail service user";
|
||||
isSystemUser = true;
|
||||
group = "promtail";
|
||||
};
|
||||
};
|
||||
}
|
||||
105
nixos/modules/services/logging/rsyslogd.nix
Normal file
105
nixos/modules/services/logging/rsyslogd.nix
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.rsyslogd;
|
||||
|
||||
syslogConf = pkgs.writeText "syslog.conf" ''
|
||||
$ModLoad imuxsock
|
||||
$SystemLogSocketName /run/systemd/journal/syslog
|
||||
$WorkDirectory /var/spool/rsyslog
|
||||
|
||||
${cfg.defaultConfig}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
defaultConf = ''
|
||||
# "local1" is used for dhcpd messages.
|
||||
local1.* -/var/log/dhcpd
|
||||
|
||||
mail.* -/var/log/mail
|
||||
|
||||
*.=warning;*.=err -/var/log/warn
|
||||
*.crit /var/log/warn
|
||||
|
||||
*.*;mail.none;local1.none -/var/log/messages
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.rsyslogd = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable syslogd. Note that systemd also logs
|
||||
syslog messages, so you normally don't need to run syslogd.
|
||||
'';
|
||||
};
|
||||
|
||||
defaultConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = defaultConf;
|
||||
description = ''
|
||||
The default <filename>syslog.conf</filename> file configures a
|
||||
fairly standard setup of log files, which can be extended by
|
||||
means of <varname>extraConfig</varname>.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = "news.* -/var/log/news";
|
||||
description = ''
|
||||
Additional text appended to <filename>syslog.conf</filename>,
|
||||
i.e. the contents of <varname>defaultConfig</varname>.
|
||||
'';
|
||||
};
|
||||
|
||||
extraParams = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "-m 0" ];
|
||||
description = ''
|
||||
Additional parameters passed to <command>rsyslogd</command>.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.systemPackages = [ pkgs.rsyslog ];
|
||||
|
||||
systemd.services.syslog =
|
||||
{ description = "Syslog Daemon";
|
||||
|
||||
requires = [ "syslog.socket" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig =
|
||||
{ ExecStart = "${pkgs.rsyslog}/sbin/rsyslogd ${toString cfg.extraParams} -f ${syslogConf} -n";
|
||||
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/spool/rsyslog";
|
||||
# Prevent syslogd output looping back through journald.
|
||||
StandardOutput = "null";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
98
nixos/modules/services/logging/syslog-ng.nix
Normal file
98
nixos/modules/services/logging/syslog-ng.nix
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.syslog-ng;
|
||||
|
||||
syslogngConfig = pkgs.writeText "syslog-ng.conf" ''
|
||||
${cfg.configHeader}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
ctrlSocket = "/run/syslog-ng/syslog-ng.ctl";
|
||||
pidFile = "/run/syslog-ng/syslog-ng.pid";
|
||||
persistFile = "/var/syslog-ng/syslog-ng.persist";
|
||||
|
||||
syslogngOptions = [
|
||||
"--foreground"
|
||||
"--module-path=${concatStringsSep ":" (["${cfg.package}/lib/syslog-ng"] ++ cfg.extraModulePaths)}"
|
||||
"--cfgfile=${syslogngConfig}"
|
||||
"--control=${ctrlSocket}"
|
||||
"--persist-file=${persistFile}"
|
||||
"--pidfile=${pidFile}"
|
||||
];
|
||||
|
||||
in {
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "services" "syslog-ng" "serviceName" ] "")
|
||||
(mkRemovedOptionModule [ "services" "syslog-ng" "listenToJournal" ] "")
|
||||
];
|
||||
|
||||
options = {
|
||||
|
||||
services.syslog-ng = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable the syslog-ng daemon.
|
||||
'';
|
||||
};
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.syslogng;
|
||||
defaultText = literalExpression "pkgs.syslogng";
|
||||
description = ''
|
||||
The package providing syslog-ng binaries.
|
||||
'';
|
||||
};
|
||||
extraModulePaths = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of paths that should be included in syslog-ng's
|
||||
<literal>--module-path</literal> option. They should usually
|
||||
end in <literal>/lib/syslog-ng</literal>
|
||||
'';
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Configuration added to the end of <literal>syslog-ng.conf</literal>.
|
||||
'';
|
||||
};
|
||||
configHeader = mkOption {
|
||||
type = types.lines;
|
||||
default = ''
|
||||
@version: 3.6
|
||||
@include "scl.conf"
|
||||
'';
|
||||
description = ''
|
||||
The very first lines of the configuration file. Should usually contain
|
||||
the syslog-ng version header.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.syslog-ng = {
|
||||
description = "syslog-ng daemon";
|
||||
preStart = "mkdir -p /{var,run}/syslog-ng";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "multi-user.target" ]; # makes sure hostname etc is set
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
PIDFile = pidFile;
|
||||
StandardOutput = "null";
|
||||
Restart = "on-failure";
|
||||
ExecStart = "${cfg.package}/sbin/syslog-ng ${concatStringsSep " " syslogngOptions}";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
130
nixos/modules/services/logging/syslogd.nix
Normal file
130
nixos/modules/services/logging/syslogd.nix
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.syslogd;
|
||||
|
||||
syslogConf = pkgs.writeText "syslog.conf" ''
|
||||
${if (cfg.tty != "") then "kern.warning;*.err;authpriv.none /dev/${cfg.tty}" else ""}
|
||||
${cfg.defaultConfig}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
defaultConf = ''
|
||||
# Send emergency messages to all users.
|
||||
*.emerg *
|
||||
|
||||
# "local1" is used for dhcpd messages.
|
||||
local1.* -/var/log/dhcpd
|
||||
|
||||
mail.* -/var/log/mail
|
||||
|
||||
*.=warning;*.=err -/var/log/warn
|
||||
*.crit /var/log/warn
|
||||
|
||||
*.*;mail.none;local1.none -/var/log/messages
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.syslogd = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable syslogd. Note that systemd also logs
|
||||
syslog messages, so you normally don't need to run syslogd.
|
||||
'';
|
||||
};
|
||||
|
||||
tty = mkOption {
|
||||
type = types.str;
|
||||
default = "tty10";
|
||||
description = ''
|
||||
The tty device on which syslogd will print important log
|
||||
messages. Leave this option blank to disable tty logging.
|
||||
'';
|
||||
};
|
||||
|
||||
defaultConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = defaultConf;
|
||||
description = ''
|
||||
The default <filename>syslog.conf</filename> file configures a
|
||||
fairly standard setup of log files, which can be extended by
|
||||
means of <varname>extraConfig</varname>.
|
||||
'';
|
||||
};
|
||||
|
||||
enableNetworkInput = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Accept logging through UDP. Option -r of syslogd(8).
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = "news.* -/var/log/news";
|
||||
description = ''
|
||||
Additional text appended to <filename>syslog.conf</filename>,
|
||||
i.e. the contents of <varname>defaultConfig</varname>.
|
||||
'';
|
||||
};
|
||||
|
||||
extraParams = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "-m 0" ];
|
||||
description = ''
|
||||
Additional parameters passed to <command>syslogd</command>.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
assertions =
|
||||
[ { assertion = !config.services.rsyslogd.enable;
|
||||
message = "rsyslogd conflicts with syslogd";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ pkgs.sysklogd ];
|
||||
|
||||
services.syslogd.extraParams = optional cfg.enableNetworkInput "-r";
|
||||
|
||||
# FIXME: restarting syslog seems to break journal logging.
|
||||
systemd.services.syslog =
|
||||
{ description = "Syslog Daemon";
|
||||
|
||||
requires = [ "syslog.socket" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig =
|
||||
{ ExecStart = "${pkgs.sysklogd}/sbin/syslogd ${toString cfg.extraParams} -f ${syslogConf} -n";
|
||||
# Prevent syslogd output looping back through journald.
|
||||
StandardOutput = "null";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
64
nixos/modules/services/logging/vector.nix
Normal file
64
nixos/modules/services/logging/vector.nix
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let cfg = config.services.vector;
|
||||
|
||||
in
|
||||
{
|
||||
options.services.vector = {
|
||||
enable = mkEnableOption "Vector";
|
||||
|
||||
journaldAccess = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable Vector to access journald.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = (pkgs.formats.json { }).type;
|
||||
default = { };
|
||||
description = ''
|
||||
Specify the configuration for Vector in Nix.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users.groups.vector = { };
|
||||
users.users.vector = {
|
||||
description = "Vector service user";
|
||||
group = "vector";
|
||||
isSystemUser = true;
|
||||
};
|
||||
systemd.services.vector = {
|
||||
description = "Vector event and log aggregator";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
serviceConfig =
|
||||
let
|
||||
format = pkgs.formats.toml { };
|
||||
conf = format.generate "vector.toml" cfg.settings;
|
||||
validateConfig = file:
|
||||
pkgs.runCommand "validate-vector-conf" { } ''
|
||||
${pkgs.vector}/bin/vector validate --no-environment "${file}"
|
||||
ln -s "${file}" "$out"
|
||||
'';
|
||||
in
|
||||
{
|
||||
ExecStart = "${pkgs.vector}/bin/vector --config ${validateConfig conf}";
|
||||
User = "vector";
|
||||
Group = "vector";
|
||||
Restart = "no";
|
||||
StateDirectory = "vector";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||
# This group is required for accessing journald.
|
||||
SupplementaryGroups = mkIf cfg.journaldAccess "systemd-journal";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue