summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--maintainers/maintainer-list.nix2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2111.section.xml11
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md5
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/monitoring/parsedmarc.md113
-rw-r--r--nixos/modules/services/monitoring/parsedmarc.nix537
-rw-r--r--nixos/modules/services/monitoring/parsedmarc.xml125
-rw-r--r--nixos/modules/services/search/elasticsearch.nix7
-rw-r--r--nixos/modules/system/activation/top-level.nix4
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/parsedmarc/default.nix224
-rw-r--r--pkgs/applications/misc/mkgmap/default.nix4
-rw-r--r--pkgs/applications/misc/mkgmap/splitter/default.nix4
-rw-r--r--pkgs/applications/networking/instant-messengers/element/element-desktop-package.json2
-rw-r--r--pkgs/applications/networking/instant-messengers/element/element-desktop.nix13
-rw-r--r--pkgs/applications/networking/instant-messengers/element/element-web.nix4
-rw-r--r--pkgs/applications/networking/mailreaders/evolution/evolution-ews/default.nix4
-rw-r--r--pkgs/applications/science/biology/sambamba/default.nix10
-rw-r--r--pkgs/development/compilers/dmd/default.nix202
-rw-r--r--pkgs/development/compilers/ldc/default.nix4
-rw-r--r--pkgs/development/interpreters/php/7.4.nix4
-rw-r--r--pkgs/development/interpreters/php/8.0.nix4
-rw-r--r--pkgs/development/libraries/gtkd/default.nix5
-rw-r--r--pkgs/development/libraries/pangomm/2.48.nix4
-rw-r--r--pkgs/development/python-modules/azure-mgmt-batchai/default.nix6
-rw-r--r--pkgs/development/python-modules/azure-mgmt-iothubprovisioningservices/default.nix7
-rw-r--r--pkgs/development/python-modules/azure-mgmt-sqlvirtualmachine/default.nix8
-rw-r--r--pkgs/development/python-modules/azure-synapse-managedprivateendpoints/default.nix33
-rw-r--r--pkgs/development/python-modules/google-cloud-audit-log/default.nix4
-rw-r--r--pkgs/development/python-modules/mailsuite/default.nix39
-rw-r--r--pkgs/development/python-modules/parsedmarc/default.nix74
-rw-r--r--pkgs/development/r-modules/default.nix4
-rw-r--r--pkgs/development/tools/pscale/default.nix6
-rw-r--r--pkgs/misc/emulators/punes/default.nix6
-rw-r--r--pkgs/servers/http/nginx/mainline.nix4
-rw-r--r--pkgs/servers/irc/inspircd/default.nix4
-rw-r--r--pkgs/servers/plex/raw.nix6
-rw-r--r--pkgs/shells/zsh/oh-my-zsh/default.nix6
-rw-r--r--pkgs/tools/admin/azure-cli/default.nix7
-rw-r--r--pkgs/tools/admin/azure-cli/python-packages.nix120
-rw-r--r--pkgs/tools/misc/ipxe/default.nix2
-rw-r--r--pkgs/tools/misc/pv/default.nix18
-rw-r--r--pkgs/tools/networking/mosh/default.nix8
-rw-r--r--pkgs/top-level/all-packages.nix4
-rw-r--r--pkgs/top-level/python-packages.nix6
45 files changed, 1458 insertions, 208 deletions
diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
index 628a42ce1f00..aeca70d7fe43 100644
--- a/maintainers/maintainer-list.nix
+++ b/maintainers/maintainer-list.nix
@@ -8582,7 +8582,7 @@
githubId = 1719781;
name = "Pablo Ovelleiro Corral";
keys = [{
- longkeyid = "sa4096/0x823A6154426408D3";
+ longkeyid = "rsa4096/0x823A6154426408D3";
fingerprint = "D03B 218C AE77 1F77 D7F9 20D9 823A 6154 4264 08D3";
}];
};
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index 57cd98db5894..6eaba9111a2b 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -239,6 +239,17 @@
<link xlink:href="options.html#opt-programs.git.enable">programs.git</link>.
</para>
</listitem>
+ <listitem>
+ <para>
+ <link xlink:href="https://domainaware.github.io/parsedmarc/">parsedmarc</link>,
+ a service which parses incoming
+ <link xlink:href="https://dmarc.org/">DMARC</link> reports and
+ stores or sends them to a downstream service for further
+ analysis. Documented in
+ <link linkend="module-services-parsedmarc">its manual
+ entry</link>.
+ </para>
+ </listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-21.11-incompatibilities">
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 440d063abf28..b77bd30ea17c 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -73,6 +73,11 @@ subsonic-compatible api. Available as [navidrome](#opt-services.navidrome.enable
- [git](https://git-scm.com), a distributed version control system. Available as [programs.git](options.html#opt-programs.git.enable).
+- [parsedmarc](https://domainaware.github.io/parsedmarc/), a service
+ which parses incoming [DMARC](https://dmarc.org/) reports and stores
+ or sends them to a downstream service for further analysis.
+ Documented in [its manual entry](#module-services-parsedmarc).
+
## Backward Incompatibilities {#sec-release-21.11-incompatibilities}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index fed212c0332c..c01c55b26700 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -621,6 +621,7 @@
./services/monitoring/munin.nix
./services/monitoring/nagios.nix
./services/monitoring/netdata.nix
+ ./services/monitoring/parsedmarc.nix
./services/monitoring/prometheus/default.nix
./services/monitoring/prometheus/alertmanager.nix
./services/monitoring/prometheus/exporters.nix
diff --git a/nixos/modules/services/monitoring/parsedmarc.md b/nixos/modules/services/monitoring/parsedmarc.md
new file mode 100644
index 000000000000..d93134a4cc76
--- /dev/null
+++ b/nixos/modules/services/monitoring/parsedmarc.md
@@ -0,0 +1,113 @@
+# parsedmarc {#module-services-parsedmarc}
+[parsedmarc](https://domainaware.github.io/parsedmarc/) is a service
+which parses incoming [DMARC](https://dmarc.org/) reports and stores
+or sends them to a downstream service for further analysis. In
+combination with Elasticsearch, Grafana and the included Grafana
+dashboard, it provides a handy overview of DMARC reports over time.
+
+## Basic usage {#module-services-parsedmarc-basic-usage}
+A very minimal setup which reads incoming reports from an external
+email address and saves them to a local Elasticsearch instance looks
+like this:
+
+```nix
+services.parsedmarc = {
+ enable = true;
+ settings.imap = {
+ host = "imap.example.com";
+ user = "alice@example.com";
+ password = "/path/to/imap_password_file";
+ watch = true;
+ };
+ provision.geoIp = false; # Not recommended!
+};
+```
+
+Note that GeoIP provisioning is disabled in the example for
+simplicity, but should be turned on for fully functional reports.
+
+## Local mail
+Instead of watching an external inbox, a local inbox can be
+automatically provisioned. The recipient's name is by default set to
+`dmarc`, but can be configured in
+[services.parsedmarc.provision.localMail.recipientName](options.html#opt-services.parsedmarc.provision.localMail.recipientName). You
+need to add an MX record pointing to the host. More concretely: for
+the example to work, an MX record needs to be set up for
+`monitoring.example.com` and the complete email address that should be
+configured in the domain's dmarc policy is
+`dmarc@monitoring.example.com`.
+
+```nix
+services.parsedmarc = {
+ enable = true;
+ provision = {
+ localMail = {
+ enable = true;
+ hostname = monitoring.example.com;
+ };
+ geoIp = false; # Not recommended!
+ };
+};
+```
+
+## Grafana and GeoIP
+The reports can be visualized and summarized with parsedmarc's
+official Grafana dashboard. For all views to work, and for the data to
+be complete, GeoIP databases are also required. The following example
+shows a basic deployment where the provisioned Elasticsearch instance
+is automatically added as a Grafana datasource, and the dashboard is
+added to Grafana as well.
+
+```nix
+services.parsedmarc = {
+ enable = true;
+ provision = {
+ localMail = {
+ enable = true;
+ hostname = url;
+ };
+ grafana = {
+ datasource = true;
+ dashboard = true;
+ };
+ };
+};
+
+# Not required, but recommended for full functionality
+services.geoipupdate = {
+ settings = {
+ AccountID = 000000;
+ LicenseKey = "/path/to/license_key_file";
+ };
+};
+
+services.grafana = {
+ enable = true;
+ addr = "0.0.0.0";
+ domain = url;
+ rootUrl = "https://" + url;
+ protocol = "socket";
+ security = {
+ adminUser = "admin";
+ adminPasswordFile = "/path/to/admin_password_file";
+ secretKeyFile = "/path/to/secret_key_file";
+ };
+};
+
+services.nginx = {
+ enable = true;
+ recommendedTlsSettings = true;
+ recommendedOptimisation = true;
+ recommendedGzipSettings = true;
+ recommendedProxySettings = true;
+ upstreams.grafana.servers."unix:/${config.services.grafana.socket}" = {};
+ virtualHosts.${url} = {
+ root = config.services.grafana.staticRootPath;
+ enableACME = true;
+ forceSSL = true;
+ locations."/".tryFiles = "$uri @grafana";
+ locations."@grafana".proxyPass = "http://grafana";
+ };
+};
+users.users.nginx.extraGroups = [ "grafana" ];
+```
diff --git a/nixos/modules/services/monitoring/parsedmarc.nix b/nixos/modules/services/monitoring/parsedmarc.nix
new file mode 100644
index 000000000000..e6a72dea0260
--- /dev/null
+++ b/nixos/modules/services/monitoring/parsedmarc.nix
@@ -0,0 +1,537 @@
+{ config, lib, pkgs, ... }:
+
+let
+ cfg = config.services.parsedmarc;
+ ini = pkgs.formats.ini {};
+in
+{
+ options.services.parsedmarc = {
+
+ enable = lib.mkEnableOption ''
+ parsedmarc, a DMARC report monitoring service
+ '';
+
+ provision = {
+ localMail = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether Postfix and Dovecot should be set up to receive
+ mail locally. parsedmarc will be configured to watch the
+ local inbox as the automatically created user specified in
+ <xref linkend="opt-services.parsedmarc.provision.localMail.recipientName" />
+ '';
+ };
+
+ recipientName = lib.mkOption {
+ type = lib.types.str;
+ default = "dmarc";
+ description = ''
+ The DMARC mail recipient name, i.e. the name part of the
+ email address which receives DMARC reports.
+
+ A local user with this name will be set up and assigned a
+ randomized password on service start.
+ '';
+ };
+
+ hostname = lib.mkOption {
+ type = lib.types.str;
+ default = config.networking.fqdn;
+ defaultText = "config.networking.fqdn";
+ example = "monitoring.example.com";
+ description = ''
+ The hostname to use when configuring Postfix.
+
+ Should correspond to the host's fully qualified domain
+ name and the domain part of the email address which
+ receives DMARC reports. You also have to set up an MX record
+ pointing to this domain name.
+ '';
+ };
+ };
+
+ geoIp = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether to enable and configure the <link
+ linkend="opt-services.geoipupdate.enable">geoipupdate</link>
+ service to automatically fetch GeoIP databases. Not crucial,
+ but recommended for full functionality.
+
+ To finish the setup, you need to manually set the <xref
+ linkend="opt-services.geoipupdate.settings.AccountID" /> and
+ <xref linkend="opt-services.geoipupdate.settings.LicenseKey" />
+ options.
+ '';
+ };
+
+ elasticsearch = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether to set up and use a local instance of Elasticsearch.
+ '';
+ };
+
+ grafana = {
+ datasource = lib.mkOption {
+ type = lib.types.bool;
+ default = cfg.provision.elasticsearch && config.services.grafana.enable;
+ apply = x: x && cfg.provision.elasticsearch;
+ description = ''
+ Whether the automatically provisioned Elasticsearch
+ instance should be added as a grafana datasource. Has no
+ effect unless
+ <xref linkend="opt-services.parsedmarc.provision.elasticsearch" />
+ is also enabled.
+ '';
+ };
+
+ dashboard = lib.mkOption {
+ type = lib.types.bool;
+ default = config.services.grafana.enable;
+ description = ''
+ Whether the official parsedmarc grafana dashboard should
+ be provisioned to the local grafana instance.
+ '';
+ };
+ };
+ };
+
+ settings = lib.mkOption {
+ description = ''
+ Configuration parameters to set in
+ <filename>parsedmarc.ini</filename>. For a full list of
+ available parameters, see
+ <link xlink:href="https://domainaware.github.io/parsedmarc/#configuration-file" />.
+ '';
+
+ type = lib.types.submodule {
+ freeformType = ini.type;
+
+ options = {
+ general = {
+ save_aggregate = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Save aggregate report data to Elasticsearch and/or Splunk.
+ '';
+ };
+
+ save_forensic = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Save forensic report data to Elasticsearch and/or Splunk.
+ '';
+ };
+ };
+
+ imap = {
+ host = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = ''
+ The IMAP server hostname or IP address.
+ '';
+ };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ default = 993;
+ description = ''
+ The IMAP server port.
+ '';
+ };
+
+ ssl = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Use an encrypted SSL/TLS connection.
+ '';
+ };
+
+ user = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ The IMAP server username.
+ '';
+ };
+
+ password = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ description = ''
+ The path to a file containing the IMAP server password.
+ '';
+ };
+
+ watch = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Use the IMAP IDLE command to process messages as they arrive.
+ '';
+ };
+
+ delete = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Delete messages after processing them, instead of archiving them.
+ '';
+ };
+ };
+
+ smtp = {
+ host = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ The SMTP server hostname or IP address.
+ '';
+ };
+
+ port = lib.mkOption {
+ type = with lib.types; nullOr port;
+ default = null;
+ description = ''
+ The SMTP server port.
+ '';
+ };
+
+ ssl = lib.mkOption {
+ type = with lib.types; nullOr bool;
+ default = null;
+ description = ''
+ Use an encrypted SSL/TLS connection.
+ '';
+ };
+
+ user = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ The SMTP server username.
+ '';
+ };
+
+ password = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ description = ''
+ The path to a file containing the SMTP server password.
+ '';
+ };
+
+ from = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ The <literal>From</literal> address to use for the
+ outgoing mail.
+ '';
+ };
+
+ to = lib.mkOption {
+ type = with lib.types; nullOr (listOf str);
+ default = null;
+ description = ''
+ The addresses to send outgoing mail to.
+ '';
+ };
+ };
+
+ elasticsearch = {
+ hosts = lib.mkOption {
+ default = [];
+ type = with lib.types; listOf str;
+ apply = x: if x == [] then null else lib.concatStringsSep "," x;
+ description = ''
+ A list of Elasticsearch hosts to push parsed reports
+ to.
+ '';
+ };
+
+ user = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ Username to use when connecting to Elasticsearch, if
+ required.
+ '';
+ };
+
+ password = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ description = ''
+ The path to a file containing the password to use when
+ connecting to Elasticsearch, if required.
+ '';
+ };
+
+ ssl = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to use an encrypted SSL/TLS connection.
+ '';
+ };
+
+ cert_path = lib.mkOption {
+ type = lib.types.path;
+ default = "/etc/ssl/certs/ca-certificates.crt";
+ description = ''
+ The path to a TLS certificate bundle used to verify
+ the server's certificate.
+ '';
+ };
+ };
+
+ kafka = {
+ hosts = lib.mkOption {
+ default = [];
+ type = with lib.types; listOf str;
+ apply = x: if x == [] then null else lib.concatStringsSep "," x;
+ description = ''
+ A list of Apache Kafka hosts to publish parsed reports
+ to.
+ '';
+ };
+
+ user = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ description = ''
+ Username to use when connecting to Kafka, if
+ required.
+ '';
+ };
+
+ password = lib.mkOption {
+ type = with lib.types; nullOr path;
+ default = null;
+ description = ''
+ The path to a file containing the password to use when
+ connecting to Kafka, if required.
+ '';
+ };
+
+ ssl = lib.mkOption {
+ type = with lib.types; nullOr bool;
+ default = null;
+ description = ''
+ Whether to use an encrypted SSL/TLS connection.
+ '';
+ };
+
+ aggregate_topic = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ example = "aggregate";
+ description = ''
+ The Kafka topic to publish aggregate reports on.
+ '';
+ };
+
+ forensic_topic = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ example = "forensic";
+ description = ''
+ The Kafka topic to publish forensic reports on.
+ '';
+ };
+ };
+
+ };
+
+ };
+ };
+
+ };
+
+ config = lib.mkIf cfg.enable {
+
+ services.elasticsearch.enable = lib.mkDefault cfg.provision.elasticsearch;
+
+ services.geoipupdate = lib.mkIf cfg.provision.geoIp {
+ enable = true;
+ settings = {
+ EditionIDs = [
+ "GeoLite2-ASN"
+ "GeoLite2-City"
+ "GeoLite2-Country"
+ ];
+ DatabaseDirectory = "/var/lib/GeoIP";
+ };
+ };
+
+ services.dovecot2 = lib.mkIf cfg.provision.localMail.enable {
+ enable = true;
+ protocols = [ "imap" ];
+ };
+
+ services.postfix = lib.mkIf cfg.provision.localMail.enable {
+ enable = true;
+ origin = cfg.provision.localMail.hostname;
+ config = {
+ myhostname = cfg.provision.localMail.hostname;
+ mydestination = cfg.provision.localMail.hostname;
+ };
+ };
+
+ services.grafana = {
+ declarativePlugins = with pkgs.grafanaPlugins;
+ lib.mkIf cfg.provision.grafana.dashboard [
+ grafana-worldmap-panel
+ grafana-piechart-panel
+ ];
+
+ provision = {
+ enable = cfg.provision.grafana.datasource || cfg.provision.grafana.dashboard;
+ datasources =
+ let
+ pkgVer = lib.getVersion config.services.elasticsearch.package;
+ esVersion =
+ if lib.versionOlder pkgVer "7" then
+ "60"
+ else if lib.versionOlder pkgVer "8" then
+ "70"
+ else
+ throw "When provisioning parsedmarc grafana datasources: unknown Elasticsearch version.";
+ in
+ lib.mkIf cfg.provision.grafana.datasource [
+ {
+ name = "dmarc-ag";
+ type = "elasticsearch";
+ access = "proxy";
+ url = "localhost:9200";
+ jsonData = {
+ timeField = "date_range";
+ inherit esVersion;
+ };
+ }
+ {
+ name = "dmarc-fo";
+ type = "elasticsearch";
+ access = "proxy";
+ url = "localhost:9200";
+ jsonData = {
+ timeField = "date_range";
+ inherit esVersion;
+ };
+ }
+ ];
+ dashboards = lib.mkIf cfg.provision.grafana.dashboard [{
+ name = "parsedmarc";
+ options.path = "${pkgs.python3Packages.parsedmarc.dashboard}";
+ }];
+ };
+ };
+
+ services.parsedmarc.settings = lib.mkMerge [
+ (lib.mkIf cfg.provision.elasticsearch {
+ elasticsearch = {
+ hosts = [ "localhost