diff options
Diffstat (limited to 'nixos/modules/services/logging')
-rw-r--r-- | nixos/modules/services/logging/klogd.nix | 42 | ||||
-rw-r--r-- | nixos/modules/services/logging/logcheck.nix | 231 | ||||
-rw-r--r-- | nixos/modules/services/logging/logrotate.nix | 38 | ||||
-rw-r--r-- | nixos/modules/services/logging/logstash.nix | 161 | ||||
-rw-r--r-- | nixos/modules/services/logging/rsyslogd.nix | 105 | ||||
-rw-r--r-- | nixos/modules/services/logging/syslogd.nix | 124 |
6 files changed, 701 insertions, 0 deletions
diff --git a/nixos/modules/services/logging/klogd.nix b/nixos/modules/services/logging/klogd.nix new file mode 100644 index 0000000000000..d7d0bbf89a546 --- /dev/null +++ b/nixos/modules/services/logging/klogd.nix @@ -0,0 +1,42 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +{ + ###### interface + + options = { + + services.klogd.enable = mkOption { + type = types.bool; + default = versionOlder (getVersion config.boot.kernelPackages.kernel) "3.5"; + description = '' + Whether to enable klogd, the kernel log message processing + daemon. Since systemd handles logging of kernel messages on + Linux 3.5 and later, this is only useful if you're running an + older kernel. + ''; + }; + + }; + + + ###### implementation + + config = mkIf config.services.klogd.enable { + + jobs.klogd = + { description = "Kernel Log Daemon"; + + wantedBy = [ "multi-user.target" ]; + + path = [ pkgs.sysklogd ]; + + exec = + "klogd -c 1 -2 -n " + + "-k $(dirname $(readlink -f /run/booted-system/kernel))/System.map"; + }; + + }; + +} diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix new file mode 100644 index 0000000000000..23f21b6a754d3 --- /dev/null +++ b/nixos/modules/services/logging/logcheck.nix @@ -0,0 +1,231 @@ +{config, pkgs, ...}: + +with pkgs.lib; + +let + cfg = config.services.logcheck; + + defaultRules = pkgs.runCommand "logcheck-default-rules" {} '' + cp -prd ${pkgs.logcheck}/etc/logcheck $out + chmod u+w $out + rm $out/logcheck.* + ''; + + rulesDir = pkgs.symlinkJoin "logcheck-rules-dir" ([ 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=/var/setuid-wrappers:$PATH nice -n10 ${pkgs.logcheck}/sbin/logcheck -R ${flags} + 2 ${cfg.timeOfDay} * * * logcheck env PATH=/var/setuid-wrappers:$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.uniq types.string; + description = '' + Set the logcheck level. Either "workstation", "server", or "paranoid". + ''; + }; + + ignoreOptions = { + level = levelOption; + + regex = mkOption { + default = ""; + type = types.uniq types.string; + description = '' + Regex specifying which log lines to ignore. + ''; + }; + }; + + ignoreCronOptions = { + user = mkOption { + default = "root"; + type = types.uniq types.string; + description = '' + User that runs the cronjob. + ''; + }; + + cmdline = mkOption { + default = ""; + type = types.uniq types.string; + 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.uniq types.string); + 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.uniq types.string; + description = '' + Username for the logcheck user. + ''; + }; + + timeOfDay = mkOption { + default = "*"; + example = "6"; + type = types.uniq types.string; + 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.uniq types.string; + description = '' + Email address to send reports to. + ''; + }; + + level = mkOption { + default = "server"; + type = types.uniq types.string; + description = '' + Set the logcheck level. Either "workstation", "server", or "paranoid". + ''; + }; + + config = mkOption { + default = "FQDN=1"; + type = types.string; + 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 = types.loaOf types.optionSet; + options = [ ignoreOptions ]; + }; + + ignoreCron = mkOption { + default = {}; + description = '' + This option defines extra ignore rules for cronjobs. + ''; + type = types.loaOf types.optionSet; + options = [ ignoreOptions ignoreCronOptions ]; + }; + + extraGroups = mkOption { + default = []; + type = types.listOf types.string; + 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.extraUsers = singleton + { name = cfg.user; + shell = "/bin/sh"; + description = "Logcheck user account"; + extraGroups = cfg.extraGroups; + }; + + 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, ...}: ! (builtins.isNull timeArgs); + mkCron = name: {user, cmdline, timeArgs, ...}: '' + ${timeArgs} ${user} ${cmdline} + ''; + in mapAttrsToList mkCron (filterAttrs withTime cfg.ignoreCron) + ++ [ cronJob ]; + }; +} diff --git a/nixos/modules/services/logging/logrotate.nix b/nixos/modules/services/logging/logrotate.nix new file mode 100644 index 0000000000000..c6c0d2ea23824 --- /dev/null +++ b/nixos/modules/services/logging/logrotate.nix @@ -0,0 +1,38 @@ +{config, pkgs, ...}: + +with pkgs.lib; + +let + cfg = config.services.logrotate; + + configFile = pkgs.writeText "logrotate.conf" + cfg.config; + + cronJob = '' + 5 * * * * root ${pkgs.logrotate}/sbin/logrotate ${configFile} + ''; + +in +{ + options = { + services.logrotate = { + enable = mkOption { + default = false; + description = '' + Enable the logrotate cron job + ''; + }; + + config = mkOption { + default = ""; + description = '' + The contents of the logrotate config file + ''; + }; + }; + }; + + config = mkIf cfg.enable { + services.cron.systemCronJobs = [ cronJob ]; + }; +} diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix new file mode 100644 index 0000000000000..2f0eea505267f --- /dev/null +++ b/nixos/modules/services/logging/logstash.nix @@ -0,0 +1,161 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.logstash; + + listToConfig = list: "[ " + (concatStringsSep ", " (map exprToConfig list)) + " ]"; + + hashToConfig = attrs: + let + attrNameToConfigList = name: + [ (exprToConfig name) (exprToConfig (getAttr name attrs)) ]; + in + "[ " + + (concatStringsSep ", " (map attrNameToConfigList (attrNames attrs))) + + " ]"; + + valueToConfig = nvpair: let name = nvpair.name; value = nvpair.value; in + if (isAttrs value) && ((!(value ? __type)) || value.__type == "repeated") + then '' + ${name} { + ${exprToConfig value} + } + '' + else "${name} => ${exprToConfig value}"; + + repeatedAttrsToConfig = values: + concatStringsSep "\n" (map valueToConfig values); + + attrsToConfig = attrs: + let + attrToConfig = name: valueToConfig { + inherit name; + value = (getAttr name attrs); + }; + in + concatStringsSep "\n" (map attrToConfig (attrNames attrs)); + + exprToConfig = expr: + let + isCustomType = expr: (isAttrs expr) && (expr ? __type); + + isFloat = expr: (isCustomType expr) && (expr.__type == "float"); + + isHash = expr: (isCustomType expr) && (expr.__type == "hash"); + + isRepeatedAttrs = expr: (isCustomType expr) && (expr.__type == "repeated"); + in + if builtins.isBool expr then (if expr then "true" else "false") else + if builtins.isString expr then ''"${expr}"'' else + if builtins.isInt expr then toString expr else + if isFloat expr then expr.value else + if isList expr then listToConfig expr else + if isHash expr then hashToConfig expr.value else + if isRepeatedAttrs expr then repeatedAttrsToConfig expr.values + else attrsToConfig expr; + + mergeConfigs = configs: + let + op = attrs: newAttrs: + let + isRepeated = newAttrs ? __type && newAttrs.__type == "repeated"; + in { + values = attrs.values ++ (if isRepeated then newAttrs.values else + map (name: { inherit name; value = getAttr name newAttrs; }) + (attrNames newAttrs)); + }; + in (foldl op { values = []; } configs) // { __type = "repeated"; }; + +in + +{ + ###### interface + + options = { + services.logstash = { + enable = mkOption { + default = false; + description = '' + Enable logstash. + ''; + }; + + inputConfig = mkOption { + default = {}; + description = '' + An attribute set (or an expression generated by mkNameValuePairs) + representing a logstash configuration's input section. + Logstash configs are name-value pairs, where values can be bools, + strings, numbers, arrays, hashes, or other name-value pairs, + and names are strings that can be repeated. Name-value pairs with no + repeats are represented by attr sets. Bools, strings, ints, and + arrays are mapped directly. Name-value pairs with repeats can be + generated by the config.lib.logstash.mkNameValuePairs function, which + takes a list of attrsets and combines them while preserving attribute + name duplicates if they occur. Similarly, there are the mkFloat and + mkHash functions, which take a string representation of a float and an + attrset, respectively. + ''; + merge = mergeConfigs; + }; + + filterConfig = mkOption { + default = {}; + description = '' + An attribute set (or an expression generated by mkNameValuePairs) + representing a logstash configuration's filter section. + See inputConfig description for details. + ''; + merge = mergeConfigs; + }; + + outputConfig = mkOption { + default = {}; + description = '' + An attribute set (or an expression generated by mkNameValuePairs) + representing a logstash configuration's output section. + See inputConfig description for details. + ''; + merge = mergeConfigs; + }; + }; + }; + + + ###### implementation + + config = mkMerge [ { + lib.logstash = { + mkFloat = stringRep: { __type = "float"; value = stringRep; }; + + mkHash = attrs: { __type = "hash"; value = attrs; }; + + mkNameValuePairs = mergeConfigs; + }; + } ( mkIf cfg.enable { + systemd.services.logstash = with pkgs; { + description = "Logstash daemon"; + + wantedBy = [ "multi-user.target" ]; + + path = [ jre ]; + + script = "cd /tmp && exec java -jar ${logstash} agent -f ${writeText "logstash.conf" '' + input { + ${exprToConfig cfg.inputConfig} + } + + filter { + ${exprToConfig cfg.filterConfig} + } + + output { + ${exprToConfig cfg.outputConfig} + } + ''} &> /var/log/logstash.log"; + }; + })]; +} diff --git a/nixos/modules/services/logging/rsyslogd.nix b/nixos/modules/services/logging/rsyslogd.nix new file mode 100644 index 0000000000000..680c7a912c1f3 --- /dev/null +++ b/nixos/modules/services/logging/rsyslogd.nix @@ -0,0 +1,105 @@ +{ config, pkgs, ... }: + +with pkgs.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.string; + 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.string; + 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.string; + 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"; + }; + }; + + }; + +} diff --git a/nixos/modules/services/logging/syslogd.nix b/nixos/modules/services/logging/syslogd.nix new file mode 100644 index 0000000000000..24cd5c1567e7c --- /dev/null +++ b/nixos/modules/services/logging/syslogd.nix @@ -0,0 +1,124 @@ +{ config, pkgs, ... }: + +with pkgs.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.uniq types.string; + 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.string; + 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.string; + 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.string; + default = [ ]; + example = [ "-m 0" ]; + description = '' + Additional parameters passed to <command>syslogd</command>. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + 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"; + }; + }; + + }; + +} |