aboutsummaryrefslogtreecommitdiff
path: root/nixos
diff options
context:
space:
mode:
authorFrederik Rietdijk <fridh@fridh.nl>2020-11-09 14:33:52 +0100
committerFrederik Rietdijk <fridh@fridh.nl>2020-11-09 14:33:52 +0100
commit20f001c01eeddbfd46ba4c5e47a8396a6eb4b08c (patch)
tree3165b4e84ec25ad34e2e40995c3fb10fad17f1d5 /nixos
parent099bb158f91ff67a3c2fa637cb533b44cf17a7da (diff)
parentf77eb9bb4d5d9ff34c6b1c18274e50d0bdddb652 (diff)
Merge master into staging-next
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml13
-rw-r--r--nixos/doc/manual/release-notes/rl-2103.xml68
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/databases/redis.nix9
-rw-r--r--nixos/modules/services/mail/roundcube.nix5
-rw-r--r--nixos/modules/services/misc/octoprint.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix9
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/sql.nix104
-rw-r--r--nixos/modules/services/networking/nar-serve.nix55
-rw-r--r--nixos/modules/services/networking/unbound.nix141
-rw-r--r--nixos/modules/services/torrent/transmission.nix2
-rw-r--r--nixos/modules/system/boot/luksroot.nix2
-rw-r--r--nixos/modules/system/boot/pbkdf2-sha512.c2
-rw-r--r--nixos/modules/virtualisation/ec2-amis.nix36
-rw-r--r--nixos/release-combined.nix2
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/nar-serve.nix48
-rw-r--r--nixos/tests/prometheus-exporters.nix44
-rw-r--r--nixos/tests/unbound.nix278
19 files changed, 761 insertions, 62 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index 01f113198eb9..75c8adbf45ed 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -879,12 +879,23 @@ php.override {
<listitem>
<para>
Nginx web server now starting with additional sandbox/hardening options. By default, write access
- to <literal>services.nginx.stateDir</literal> is allowed. To allow writing to other folders,
+ to <literal>/var/log/nginx</literal> and <literal>/var/cache/nginx</literal> is allowed. To allow writing to other folders,
use <literal>systemd.services.nginx.serviceConfig.ReadWritePaths</literal>
<programlisting>
systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/var/www" ];
</programlisting>
</para>
+ <para>
+ Nginx is also started with the systemd option <literal>ProtectHome = mkDefault true;</literal>
+ which forbids it to read anything from <literal>/home</literal>, <literal>/root</literal>
+ and <literal>/run/user</literal> (see
+ <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=">ProtectHome docs</link>
+ for details).
+ If you require serving files from home directories, you may choose to set e.g.
+<programlisting>
+systemd.services.nginx.serviceConfig.ProtectHome = "read-only";
+</programlisting>
+ </para>
</listitem>
<listitem>
<para>
diff --git a/nixos/doc/manual/release-notes/rl-2103.xml b/nixos/doc/manual/release-notes/rl-2103.xml
index a90b49ba798c..709b2ba5228f 100644
--- a/nixos/doc/manual/release-notes/rl-2103.xml
+++ b/nixos/doc/manual/release-notes/rl-2103.xml
@@ -139,6 +139,13 @@
<package>stanchion</package> package removed along with <varname>services.stanchion</varname> module.
</para>
</listitem>
+ <listitem>
+ <para>
+ <package>mutt</package> has been updated to a new major version (2.x), which comes with
+ some backward incompatible changes that are described in the
+ <link xlink:href="http://www.mutt.org/relnotes/2.0/">release notes for Mutt 2.0</link>.
+ </para>
+ </listitem>
</itemizedlist>
</section>
@@ -164,12 +171,73 @@
</listitem>
<listitem>
<para>
+ The setting <xref linkend="opt-services.redis.bind" /> defaults to <literal>127.0.0.1</literal> now, making Redis listen on the loopback interface only, and not all public network interfaces.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
NixOS now emits a deprecation warning if systemd's <literal>StartLimitInterval</literal> setting is used in a <literal>serviceConfig</literal> section instead of in a <literal>unitConfig</literal>; that setting is deprecated and now undocumented for the service section by systemd upstream, but still effective and somewhat buggy there, which can be confusing. See <link xlink:href="https://github.com/NixOS/nixpkgs/issues/45785">#45785</link> for details.
</para>
<para>
All services should use <xref linkend="opt-systemd.services._name_.startLimitIntervalSec" /> or <literal>StartLimitIntervalSec</literal> in <xref linkend="opt-systemd.services._name_.unitConfig" /> instead.
</para>
</listitem>
+ <listitem>
+ <para>
+ The Unbound DNS resolver service (<literal>services.unbound</literal>) has been refactored to allow reloading, control sockets and to fix startup ordering issues.
+ </para>
+
+ <para>
+ It is now possible to enable a local UNIX control socket for unbound by setting the <xref linkend="opt-services.unbound.localControlSocketPath" />
+ option.
+ </para>
+
+ <para>
+ Previously we just applied a very minimal set of restrictions and
+ trusted unbound to properly drop root privs and capabilities.
+ </para>
+
+ <para>
+ As of this we are (for the most part) just using the upstream
+ example unit file for unbound. The main difference is that we start
+ unbound as <literal>unbound</literal> user with the required capabilities instead of
+ letting unbound do the chroot &amp; uid/gid changes.
+ </para>
+
+ <para>
+ The upstream unit configuration this is based on is a lot stricter with
+ all kinds of permissions then our previous variant. It also came with
+ the default of having the <literal>Type</literal> set to <literal>notify</literal>, therefore we are now also
+ using the <literal>unbound-with-systemd</literal> package here. Unbound will start up,
+ read the configuration files and start listening on the configured ports
+ before systemd will declare the unit <literal>active (running)</literal>.
+ This will likely help with startup order and the occasional race condition during system
+ activation where the DNS service is started but not yet ready to answer
+ queries. Services depending on <literal>nss-lookup.target</literal> or <literal>unbound.service</literal>
+ are now be able to use unbound when those targets have been reached.
+ </para>
+
+ <para>
+ Aditionally to the much stricter runtime environmet the
+ <literal>/dev/urandom</literal> mount lines we previously had in the code (that would
+ randomly failed during the stop-phase) have been removed as systemd will take care of those for us.
+ </para>
+
+ <para>
+ The <literal>preStart</literal> script is now only required if we enabled the trust
+ anchor updates (which are still enabled by default).
+ </para>
+
+ <para>
+ Another benefit of the refactoring is that we can now issue reloads via
+ either <literal>pkill -HUP unbound</literal> and <literal>systemctl reload unbound</literal> to reload the
+ running configuration without taking the daemon offline. A prerequisite
+ of this was that unbound configuration is available on a well known path
+ on the file system. We are using the path <literal>/etc/unbound/unbound.conf</literal> as that is the
+ default in the CLI tooling which in turn enables us to use
+ <literal>unbound-control</literal> without passing a custom configuration location.
+ </para>
+ </listitem>
</itemizedlist>
</section>
</section>
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 3fd7ebd1ca78..6ac12e4e1387 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -680,6 +680,7 @@
./services/networking/murmur.nix
./services/networking/mxisd.nix
./services/networking/namecoind.nix
+ ./services/networking/nar-serve.nix
./services/networking/nat.nix
./services/networking/ndppd.nix
./services/networking/networkmanager.nix
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index f1777854e141..6b8853ae390b 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -87,9 +87,12 @@ in
bind = mkOption {
type = with types; nullOr str;
- default = null; # All interfaces
- description = "The IP interface to bind to.";
- example = "127.0.0.1";
+ default = "127.0.0.1";
+ description = ''
+ The IP interface to bind to.
+ <literal>null</literal> means "all interfaces".
+ '';
+ example = "192.0.2.1";
};
unixSocket = mkOption {
diff --git a/nixos/modules/services/mail/roundcube.nix b/nixos/modules/services/mail/roundcube.nix
index a0bbab64985b..ee7aa7e22fb9 100644
--- a/nixos/modules/services/mail/roundcube.nix
+++ b/nixos/modules/services/mail/roundcube.nix
@@ -204,6 +204,11 @@ in
};
systemd.services.phpfpm-roundcube.after = [ "roundcube-setup.service" ];
+ # Restart on config changes.
+ systemd.services.phpfpm-roundcube.restartTriggers = [
+ config.environment.etc."roundcube/config.inc.php".source
+ ];
+
systemd.services.roundcube-setup = mkMerge [
(mkIf (cfg.database.host == "localhost") {
requires = [ "postgresql.service" ];
diff --git a/nixos/modules/services/misc/octoprint.nix b/nixos/modules/services/misc/octoprint.nix
index e2fbd3b401cc..a69e65073050 100644
--- a/nixos/modules/services/misc/octoprint.nix
+++ b/nixos/modules/services/misc/octoprint.nix
@@ -68,7 +68,7 @@ in
plugins = mkOption {
default = plugins: [];
defaultText = "plugins: []";
- example = literalExample "plugins: with plugins; [ m33-fio stlviewer ]";
+ example = literalExample "plugins: with plugins; [ themeify stlviewer ]";
description = "Additional plugins to be used. Available plugins are passed through the plugins input.";
};
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index a4aa470f5bc4..995afca96ff8 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -45,6 +45,7 @@ let
"rspamd"
"rtl_433"
"snmp"
+ "sql"
"surfboard"
"tor"
"unifi"
@@ -218,6 +219,14 @@ in
Please specify either 'services.prometheus.exporters.mail.configuration'
or 'services.prometheus.exporters.mail.configFile'.
'';
+ } {
+ assertion = cfg.sql.enable -> (
+ (cfg.sql.configFile == null) != (cfg.sql.configuration == null)
+ );
+ message = ''
+ Please specify either 'services.prometheus.exporters.sql.configuration' or
+ 'services.prometheus.exporters.sql.configFile'
+ '';
} ];
}] ++ [(mkIf config.services.minio.enable {
services.prometheus.exporters.minio.minioAddress = mkDefault "http://localhost:9000";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/sql.nix b/nixos/modules/services/monitoring/prometheus/exporters/sql.nix
new file mode 100644
index 000000000000..d9be724ebc03
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/sql.nix
@@ -0,0 +1,104 @@
+{ config, lib, pkgs, options }:
+with lib;
+let
+ cfg = config.services.prometheus.exporters.sql;
+ cfgOptions = {
+ options = with types; {
+ jobs = mkOption {
+ type = attrsOf (submodule jobOptions);
+ default = { };
+ description = "An attrset of metrics scraping jobs to run.";
+ };
+ };
+ };
+ jobOptions = {
+ options = with types; {
+ interval = mkOption {
+ type = str;
+ description = ''
+ How often to run this job, specified in
+ <link xlink:href="https://golang.org/pkg/time/#ParseDuration">Go duration</link> format.
+ '';
+ };
+ connections = mkOption {
+ type = listOf str;
+ description = "A list of connection strings of the SQL servers to scrape metrics from";
+ };
+ startupSql = mkOption {
+ type = listOf str;
+ default = [];
+ description = "A list of SQL statements to execute once after making a connection.";
+ };
+ queries = mkOption {
+ type = attrsOf (submodule queryOptions);
+ description = "SQL queries to run.";
+ };
+ };
+ };
+ queryOptions = {
+ options = with types; {
+ help = mkOption {
+ type = nullOr str;
+ default = null;
+ description = "A human-readable description of this metric.";
+ };
+ labels = mkOption {
+ type = listOf str;
+ default = [ ];
+ description = "A set of columns that will be used as Prometheus labels.";
+ };
+ query = mkOption {
+ type = str;
+ description = "The SQL query to run.";
+ };
+ values = mkOption {
+ type = listOf str;
+ description = "A set of columns that will be used as values of this metric.";
+ };
+ };
+ };
+
+ configFile =
+ if cfg.configFile != null
+ then cfg.configFile
+ else
+ let
+ nameInline = mapAttrsToList (k: v: v // { name = k; });
+ renameStartupSql = j: removeAttrs (j // { startup_sql = j.startupSql; }) [ "startupSql" ];
+ configuration = {
+ jobs = map renameStartupSql
+ (nameInline (mapAttrs (k: v: (v // { queries = nameInline v.queries; })) cfg.configuration.jobs));
+ };
+ in
+ builtins.toFile "config.yaml" (builtins.toJSON configuration);
+in
+{
+ extraOpts = {
+ configFile = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = ''
+ Path to configuration file.
+ '';
+ };
+ configuration = mkOption {
+ type = with types; nullOr (submodule cfgOptions);
+ default = null;
+ description = ''
+ Exporter configuration as nix attribute set. Mutually exclusive with 'configFile' option.
+ '';
+ };
+ };
+
+ port = 9237;
+ serviceOpts = {
+ serviceConfig = {
+ ExecStart = ''
+ ${pkgs.prometheus-sql-exporter}/bin/sql_exporter \
+ -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+ -config.file ${configFile} \
+ ${concatStringsSep " \\\n " cfg.extraFlags}
+ '';
+ };
+ };
+}
diff --git a/nixos/modules/services/networking/nar-serve.nix b/nixos/modules/services/networking/nar-serve.nix
new file mode 100644
index 000000000000..ddd42fa01073
--- /dev/null
+++ b/nixos/modules/services/networking/nar-serve.nix
@@ -0,0 +1,55 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+ cfg = config.services.nar-serve;
+in
+{
+ meta = {
+ maintainers = [ maintainers.rizary ];
+ };
+ options = {
+ services.nar-serve = {
+ enable = mkEnableOption "Serve NAR file contents via HTTP";
+
+ port = mkOption {
+ type = types.int;
+ default = 8383;
+ description = ''
+ Port number where nar-serve will listen on.
+ '';
+ };
+
+ cacheURL = mkOption {
+ type = types.str;
+ default = "https://cache.nixos.org/";
+ description = ''
+ Binary cache URL to connect to.
+
+ The URL format is compatible with the nix remote url style, such as:
+ - http://, https:// for binary caches via HTTP or HTTPS
+ - s3:// for binary caches stored in Amazon S3
+ - gs:// for binary caches stored in Google Cloud Storage
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.nar-serve = {
+ description = "NAR server";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ environment.PORT = toString cfg.port;
+ environment.NAR_CACHE_URL = cfg.cacheURL;
+
+ serviceConfig = {
+ Restart = "always";
+ RestartSec = "5s";
+ ExecStart = "${pkgs.nar-serve}/bin/nar-serve";
+ DynamicUser = true;
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index baed83591e1e..9a46fa3075fa 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -1,9 +1,7 @@
{ config, lib, pkgs, ... }:
with lib;
-
let
-
cfg = config.services.unbound;
stateDir = "/var/lib/unbound";
@@ -17,12 +15,12 @@ let
forward =
optionalString (any isLocalAddress cfg.forwardAddresses) ''
do-not-query-localhost: no
- '' +
- optionalString (cfg.forwardAddresses != []) ''
+ ''
+ + optionalString (cfg.forwardAddresses != []) ''
forward-zone:
name: .
- '' +
- concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses;
+ ''
+ + concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses;
rootTrustAnchorFile = "${stateDir}/root.key";
@@ -31,19 +29,25 @@ let
confFile = pkgs.writeText "unbound.conf" ''
server:
+ ip-freebind: yes
directory: "${stateDir}"
username: unbound
- chroot: "${stateDir}"
+ chroot: ""
pidfile: ""
+ # when running under systemd there is no need to daemonize
+ do-daemonize: no
${interfaces}
${access}
${trustAnchor}
+ ${lib.optionalString (cfg.localControlSocketPath != null) ''
+ remote-control:
+ control-enable: yes
+ control-interface: ${cfg.localControlSocketPath}
+ ''}
${cfg.extraConfig}
${forward}
'';
-
in
-
{
###### interface
@@ -55,8 +59,8 @@ in
package = mkOption {
type = types.package;
- default = pkgs.unbound;
- defaultText = "pkgs.unbound";
+ default = pkgs.unbound-with-systemd;
+ defaultText = "pkgs.unbound-with-systemd";
description = "The unbound package to use";
};
@@ -69,11 +73,14 @@ in
interfaces = mkOption {
default = [ "127.0.0.1" ] ++ optional config.networking.enableIPv6 "::1";
type = types.listOf types.str;
- description = "What addresses the server should listen on.";
+ description = ''
+ What addresses the server should listen on. This supports the interface syntax documented in
+ <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ '';
};
forwardAddresses = mkOption {
- default = [ ];
+ default = [];
type = types.listOf types.str;
description = "What servers to forward queries to.";
};
@@ -84,6 +91,28 @@ in
description = "Use and update root trust anchor for DNSSEC validation.";
};
+ localControlSocketPath = mkOption {
+ default = null;
+ # FIXME: What is the proper type here so users can specify strings,
+ # paths and null?
+ # My guess would be `types.nullOr (types.either types.str types.path)`
+ # but I haven't verified yet.
+ type = types.nullOr types.str;
+ example = "/run/unbound/unbound.ctl";
+ description = ''
+ When not set to <literal>null</literal> this option defines the path
+ at which the unbound remote control socket should be created at. The
+ socket will be owned by the unbound user (<literal>unbound</literal>)
+ and group will be <literal>nogroup</literal>.
+
+ Users that should be permitted to access the socket must be in the
+ <literal>unbound</literal> group.
+
+ If this option is <literal>null</literal> remote control will not be
+ configured at all. Unbounds default values apply.
+ '';
+ };
+
extraConfig = mkOption {
default = "";
type = types.lines;
@@ -106,43 +135,85 @@ in
users.users.unbound = {
description = "unbound daemon user";
isSystemUser = true;
+ group = lib.mkIf (cfg.localControlSocketPath != null) (lib.mkDefault "unbound");
+ };
+
+ # We need a group so that we can give users access to the configured
+ # control socket. Unbound allows access to the socket only to the unbound
+ # user and the primary group.
+ users.groups = lib.mkIf (cfg.localControlSocketPath != null) {
+ unbound = {};
};
networking.resolvconf.useLocalResolver = mkDefault true;
+
+ environment.etc."unbound/unbound.conf".source = confFile;
+
systemd.services.unbound = {
description = "Unbound recursive Domain Name Server";
after = [ "network.target" ];
before = [ "nss-lookup.target" ];
- wants = [ "nss-lookup.target" ];
- wantedBy = [ "multi-user.target" ];
-
- preStart = ''
- mkdir -m 0755 -p ${stateDir}/dev/
- cp ${confFile} ${stateDir}/unbound.conf
- ${optionalString cfg.enableRootTrustAnchor ''
- ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
- chown unbound ${stateDir} ${rootTrustAnchorFile}
- ''}
- touch ${stateDir}/dev/random
- ${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random
+ wantedBy = [ "multi-user.target" "nss-lookup.target" ];
+
+ preStart = lib.mkIf cfg.enableRootTrustAnchor ''
+ ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!"
'';
- serviceConfig = {
- ExecStart = "${cfg.package}/bin/unbound -d -c ${stateDir}/unbound.conf";
- ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random";
+ restartTriggers = [
+ confFile
+ ];
- ProtectSystem = true;
- ProtectHome = true;
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/unbound -p -d -c /etc/unbound/unbound.conf";
+ ExecReload = "+/run/current-system/sw/bin/kill -HUP $MAINPID";
+
+ NotifyAccess = "main";
+ Type = "notify";
+
+ # FIXME: Which of these do we actualy need, can we drop the chroot flag?
+ AmbientCapabilities = [
+ "CAP_NET_BIND_SERVICE"
+ "CAP_NET_RAW"
+ "CAP_SETGID"
+ "CAP_SETUID"
+ "CAP_SYS_CHROOT"
+ "CAP_SYS_RESOURCE"
+ ];
+
+ User = "unbound";
+ Group = lib.mkIf (cfg.localControlSocketPath != null) (lib.mkDefault "unbound");
+
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
PrivateDevices = true;
- Restart = "always";
- RestartSec = "5s";
+ PrivateTmp = true;
+ ProtectHome = true;
+ ProtectControlGroups = true;
+ ProtectKernelModules = true;
+ ProtectSystem = "strict";
+ RuntimeDirectory = "unbound";
+ ConfigurationDirectory = "unbound";
+ StateDirectory = "unbound";
+ RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+ RestrictRealtime = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "~@clock"
+ "@cpu-emulation"
+ "@debug"
+ "@keyring"
+ "@module"
+ "mount"
+ "@obsolete"
+ "@resources"
+ ];
+ RestrictNamespaces = true;
+ LockPersonality = true;
+ RestrictSUIDSGID = true;
};
};
-
# If networkmanager is enabled, ask it to interface with unbound.
networking.networkmanager.dns = "unbound";
-
};
-
}
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index aeb58a7194f9..717c18d367f0 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -236,6 +236,7 @@ in
# an AppArmor profile is provided to get a confinement based upon paths and rights.
builtins.storeDir
"/etc"
+ "/run"
] ++
optional (cfg.settings.script-torrent-done-enabled &&
cfg.settings.script-torrent-done-filename != "")
@@ -408,6 +409,7 @@ in
#r @{PROC}/@{pid}/environ,
r @{PROC}/@{pid}/mounts,
rwk /tmp/tr_session_id_*,
+ r /run/systemd/resolve/stub-resolv.conf,
r ${pkgs.openssl.out}/etc/**,
r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 88190e8200b1..8dd2ea20519a 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -404,7 +404,7 @@ let
echo "Please move your mouse to create needed randomness."
''}
echo "Waiting for your FIDO2 device..."
- fido2luks -i open ${device} ${name} ${fido2.credential} --await-dev ${toString fido2.gracePeriod} --salt string:$passphrase
+ fido2luks open ${device} ${name} ${fido2.credential} --await-dev ${toString fido2.gracePeriod} --salt string:$passphrase
if [ $? -ne 0 ]; then
echo "No FIDO2 key found, falling back to normal open procedure"
open_normally
diff --git a/nixos/modules/system/boot/pbkdf2-sha512.c b/nixos/modules/system/boot/pbkdf2-sha512.c
index b40c383ac023..67e989957ba6 100644
--- a/nixos/modules/system/boot/pbkdf2-sha512.c
+++ b/nixos/modules/system/boot/pbkdf2-sha512.c
@@ -35,4 +35,4 @@ int main(int argc, char** argv)
fwrite(key, 1, key_length, stdout);
return 0;
-} \ No newline at end of file
+}
diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix
index 4d9c391e0465..3da63078a214 100644
--- a/nixos/modules/virtualisation/ec2-amis.nix
+++ b/nixos/modules/virtualisation/ec2-amis.nix
@@ -329,24 +329,24 @@ let self = {
"20.03".ap-east-1.hvm-ebs = "ami-0d18fdd309cdefa86";
"20.03".sa-east-1.hvm-ebs = "ami-09859378158ae971d";
- # 20.09.1465.9a0b14b097d
- "20.09".eu-west-1.hvm-ebs = "ami-0d90f16418e3c364c";
- "20.09".eu-west-2.hvm-ebs = "ami-0635ec0780ea57cfe";
- "20.09".eu-west-3.hvm-ebs = "ami-0714e94352f2eabb9";
- "20.09".eu-central-1.hvm-ebs = "ami-0979d39762a4d2a02";
- "20.09".eu-north-1.hvm-ebs = "ami-0b14e273185c66e9b";
- "20.09".us-east-1.hvm-ebs = "ami-0f8b063ac3f2d9645";
- "20.09".us-east-2.hvm-ebs = "ami-0959202a0393fdd0c";
- "20.09".us-west-1.hvm-ebs = "ami-096d50833b785478b";
- "20.09".us-west-2.hvm-ebs = "ami-0fc31031df0df6104";
- "20.09".ca-central-1.hvm-ebs = "ami-0787786a38cde3905";
- "20.09".ap-southeast-1.hvm-ebs = "ami-0b3f693d3a2a0b9ae";
- "20.09".ap-southeast-2.hvm-ebs = "ami-02471872bc876b610";
- "20.09".ap-northeast-1.hvm-ebs = "ami-06505fd2bf44a59a7";
- "20.09".ap-northeast-2.hvm-ebs = "ami-0754b4c014eea1e8a";
- "20.09".ap-south-1.hvm-ebs = "ami-05100e32242ae65a6";
- "20.09".ap-east-1.hvm-ebs = "ami-045288859a39de009";
- "20.09".sa-east-1.hvm-ebs = "ami-0a937748db48fb00d";
+ # 20.09.1632.a6a3a368dda
+ "20.09".eu-west-1.hvm-ebs = "ami-01a79d5ce435f4db3";
+ "20.09".eu-west-2.hvm-ebs = "ami-0cbe14f32904e6331";
+ "20.09".eu-west-3.hvm-ebs = "ami-07f493412d6213de6";
+ "20.09".eu-central-1.hvm-ebs = "ami-01d4a0c2248cbfe38";
+ "20.09".eu-north-1.hvm-ebs = "ami-0003f54dd99d68e0f";
+ "20.09".us-east-1.hvm-ebs = "ami-068a62d478710462d";
+ "20.09".us-east-2.hvm-ebs = "ami-01ac677ff61399caa";
+ "20.09".us-west-1.hvm-ebs = "ami-04befdb203b4b17f6";
+ "20.09".us-west-2.hvm-ebs = "ami-0fb7bd4a43261c6b2";
+ "20.09".ca-central-1.hvm-ebs = "ami-06d5ee429f153f856";
+ "20.09".ap-southeast-1.hvm-ebs = "ami-0db0304e23c535b2a";
+ "20.09".ap-southeast-2.hvm-ebs = "ami-045983c4db7e36447";
+ "20.09".ap-northeast-1.hvm-ebs = "ami-0beb18d632cf64e5a";
+ "20.09".ap-northeast-2.hvm-ebs = "ami-0dd0316af578862db";
+ "20.09".ap-south-1.hvm-ebs = "ami-008d15ced81c88aed";
+ "20.09".ap-east-1.hvm-ebs = "ami-071f49713f86ea965";
+ "20.09".sa-east-1.hvm-ebs = "ami-05ded1ae35209b5a8";
latest = self."20.09";
}; in self
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index 945ba90e3454..384ae5765b8c 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -71,7 +71,6 @@ in rec {
(onFullSupported "nixos.tests.fontconfig-default-fonts")
(onFullSupported "nixos.tests.gnome3")
(onFullSupported "nixos.tests.gnome3-xorg")
- (onFullSupported "nixos.tests.hardened")
(onSystems ["x86_64-linux"] "nixos.tests.hibernate")
(onFullSupported "nixos.tests.i3wm")
(onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSimple")
@@ -93,7 +92,6 @@ in rec {
(onFullSupported "nixos.tests.keymap.dvp")
(onFullSupported "nixos.tests.keymap.neo")
(onFullSupported "nixos.tests.keymap.qwertz")
- (onFullSupported "nixos.tests.latestKernel.hardened")
(onFullSupported "nixos.tests.latestKernel.login")
(onFullSupported "nixos.tests.lightdm")
(onFullSupported "nixos.tests.login")
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index f36b70bae7fc..d4aff486225d 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -225,6 +225,7 @@ in
mysql-backup = handleTest ./mysql/mysql-backup.nix {};
mysql-replication = handleTest ./mysql/mysql-replication.nix {};
nagios = handleTest ./nagios.nix {};
+ nar-serve = handleTest ./nar-serve.nix {};
nat.firewall = handleTest ./nat.nix { withFirewall = true; };
nat.firewall-conntrack = handleTest ./nat.nix { withFirewall = true; withConntrackHelpers = true; };
nat.standalone = handleTest ./nat.nix { withFirewall = false; };
@@ -368,6 +369,7 @@ in
trezord = handleTest ./trezord.nix {};
trickster = handleTest ./trickster.nix {};
tuptime = handleTest ./tuptime.nix {};
+ unbound = handleTest ./unbound.nix {};
udisks2 = handleTest ./udisks2.nix {};
unit-php = handleTest ./web-servers/unit-php.nix {};
upnp = handleTest ./upnp.nix {};
diff --git a/nixos/tests/nar-serve.nix b/nixos/tests/nar-serve.nix
new file mode 100644
index 000000000000..9ee738ffb170
--- /dev/null
+++ b/nixos/tests/nar-serve.nix
@@ -0,0 +1,48 @@
+import ./make-test-python.nix (
+ { pkgs, lib, ... }:
+ {
+ name = "nar-serve";
+ meta.maintainers = [ lib.maintainers.rizary ];
+ nodes =
+ {
+ server = { pkgs, ... }: {
+ services.nginx = {
+ enable = true;
+ virtualHosts.default.root = "/var/www";
+ };
+ services.nar-serve = {
+ enable = true;
+ # Connect to the localhost nginx instead of the default
+ # https://cache.nixos.org
+ cacheURL = "http://localhost/";
+ };
+ environment.systemPackages = [
+ pkgs.hello
+ pkgs.curl
+ ];
+
+ networking.firewall.allowedTCPPorts = [ 8383 ];
+
+ # virtualisation.diskSize = 2 * 1024;
+ };
+ };
+ testScript = ''
+ start_all()
+
+ # Create a fake cache with Nginx service the static files
+ server.succeed(
+ "nix copy --to file:///var/www ${pkgs.hello}"
+ )
+ server.wait_for_unit("nginx.service")
+ server.wait_for_open_port(80)
+
+ # Check that nar-serve can return the content of the derivation
+ drvName = os.path.basename("${pkgs.hello}")
+ drvHash = drvName.split("-")[0]
+ server.wait_for_unit("nar-serve.service")
+ server.succeed(
+ "curl -o hello -f http://localhost:8383/nix/store/{}/bin/hello".format(drvHash)
+ )
+ '';
+ }
+)
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 2553f5dcf747..0b9957404f3b 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -609,6 +609,50 @@ let
'';
};
+ sql = {
+ exporterConfig = {
+ configuration.jobs.points = {
+ interval = "1m";
+ connections = [
+ "postgres://prometheus-sql-exporter@/data?host=/run/postgresql&sslmode=disable"
+ ];
+ queries = {
+ points = {
+ labels = [ "name" ];
+ help = "Amount of points accumulated per person";
+ values = [ "amount" ];
+ query = "SELECT SUM(amount) as amount, name FROM points GROUP BY name";
+ };
+ };
+ };
+ enable = true;
+ user = "prometheus-sql-exporter";
+ };
+ metricProvider = {
+ services.postgresql = {
+ enable = true;
+ initialScript = builtins.toFile "init.sql" ''
+ CREATE DATABASE data;
+ \c data;
+ CREATE TABLE points (amount INT, name TEXT);
+ INSERT INTO points(amount, name) VALUES (1, 'jack');
+ INSERT INTO points(amount, name) VALUES (2, 'jill');
+ INSERT INTO points(amount, name) VALUES (3, 'jack');
+
+ CREATE USER "prometheus-sql-exporter";
+ GRANT ALL PRIVILEGES ON DATABASE data TO "prometheus-sql-exporter";
+ GRANT SELECT ON points TO "prometheus-sql-exporter";
+ '';
+ };
+ systemd.services.prometheus-sql-exporter.after = [ "postgresql.service" ];
+ };
+ exporterTest = ''
+ wait_for_unit("prometheus-sql-exporter.service")
+ wait_for_open_port(9237)
+ succeed("curl http://localhost:9237/metrics | grep -c 'sql_points{' | grep -q 2")
+ '';
+ };
+
surfboard = {
exporterConfig = {
enable = true;
diff --git a/nixos/tests/unbound.nix b/nixos/tests/unbound.nix
new file mode 100644
index 000000000000..dc8e5a9d3ed8
--- /dev/null
+++ b/nixos/tests/unbound.nix
@@ -0,0 +1,278 @@
+/*
+ Test that our unbound module indeed works as most users would expect.
+ There are a few settings that we must consider when modifying the test. The
+ ususal use-cases for unbound are
+ * running a recursive DNS resolver on the local machine
+ * running a recursive DNS resolver on the local machine, forwarding to a local DNS server via UDP/53 & TCP/53
+ * running a recursive DNS resolver on the local machine, forwarding to a local DNS server via TCP/853 (DoT)
+ * running a recursive DNS resolver on a machine in the network awaiting input from clients over TCP/53 & UDP/53
+ * running a recursive DNS resolver on a machine in the network awaiting input from clients over TCP/853 (DoT)
+
+ In the below test setup we are trying to implement all of those use cases.
+
+ Another aspect that we cover is access to the local control UNIX socket. It
+ can optionally be enabled and users can optionally be in a group to gain
+ access. Users that are not in the group (except for root) should not have
+ access to that socket. Also, when there is no socket configured, users
+ shouldn't be able to access the control socket at all. Not even root.
+*/
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+ let
+ # common client configuration that we can just use for the multitude of
+ # clients we are constructing
+ common = { lib, pkgs, ... }: {
+ config = {
+ environment.systemPackages = [ pkgs.knot-dns ];
+
+ # disable the root anchor update as we do not have internet access during
+ # the test execution
+ services.unbound.enableRootTrustAnchor = false;
+ };
+ };
+
+ cert = pkgs.runCommandNoCC "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=dns.example.local'
+ mkdir -p $out
+ cp key.pem cert.pem $out
+ '';
+ in
+ {
+ name = "unbound";
+ meta = with pkgs.stdenv.lib.maintainers; {
+ maintainers = [ andir ];
+ };
+
+ nodes = {
+
+ # The server that actually serves our zones, this tests unbounds authoriative mode
+ authoritative = { lib, pkgs, config, ... }: {
+ imports = [ common ];
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ { address = "192.168.0.1"; prefixLength = 24; }
+ ];
+ networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+ { address = "fd21::1"; prefixLength = 64; }
+ ];
+ networking.firewall.allowedTCPPorts = [ 53 ];
+ networking.firewall.allowedUDPPorts = [ 53 ];
+
+ services.unbound = {
+ enable = true;
+ interfaces = [ "192.168.0.1" "fd21::1" "::1" "127.0.0.1" ];
+ allowedAccess = [ "192.168.0.0/24" "fd21::/64" "::1" "127.0.0.0/8" ];
+ extraConfig = ''
+ server:
+ local-data: "example.local. IN A 1.2.3.4"
+ local-data: "example.local. IN AAAA abcd::eeff"
+ '';
+ };
+ };
+
+ # The resolver that knows that fowards (only) to the authoritative server
+ # and listens on UDP/53, TCP/53 & TCP/853.
+ resolver = { lib, nodes, ... }: {
+ imports = [ common ];
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ { address = "192.168.0.2"; prefixLength = 24; }
+ ];
+ networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+ { address = "fd21::2"; prefixLength = 64; }
+ ];
+ networking.firewall.allowedTCPPorts = [
+ 53 # regular DNS
+ 853 # DNS over TLS
+ ];
+ networking.firewall.allowedUDPPorts = [ 53 ];
+
+ services.unbound = {
+ enable = true;
+ allowedAccess = [ "192.168.0.0/24" "fd21::/64" "::1" "127.0.0.0/8" ];
+ interfaces = [ "::1" "127.0.0.1" "192.168.0.2" "fd21::2" "192.168.0.2@853" "fd21::2@853" "::1@853" "127.0.0.1@853" ];
+ forwardAddresses = [
+ (lib.head nodes.authoritative.config.networking.interfaces.eth1.ipv6.addresses).address
+ (lib.head nodes.authoritative.config.networking.interfaces.eth1.ipv4.addresses).address
+ ];
+ extraConfig = ''
+ server:
+ tls-service-pem: ${cert}/cert.pem
+ tls-service-key: ${cert}/key.pem
+ '';
+ };
+ };
+
+ # machine that runs a local unbound that will be reconfigured during test execution
+ local_resolver = { lib, nodes, config, ... }: {
+ imports = [ common ];
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ { address = "192.168.0.3"; prefixLength = 24; }
+ ];
+ networking.interfaces.eth1.ipv6.addresses = lib.mkForce [
+ { address = "fd21::3"; prefixLength = 64; }
+ ];
+ networking.firewall.allowedTCPPorts = [
+ 53 # regular DNS
+ ];
+ networking.firewall.allowedUDPPorts = [ 53 ];
+
+ services.unbound = {
+ enable = true;
+ allowedAccess = [ "::1" "127.0.0.0/8" ];
+ interfaces = [ "::1" "127.0.0.1" ];
+ localControlSocketPath = "/run/unbound/unbound.ctl";
+ extraConfig = ''
+ include: "/etc/unbound/extra*.conf"
+ '';
+ };
+
+ users.users = {
+ # user that is permitted to access the unix socket
+ someuser.extraGroups = [
+ config.users.users.unbound.group
+ ];
+
+ # user that is not permitted to access the unix socket
+ unauthorizeduser = {};
+ };
+
+ environment.etc = {
+ "unbound-extra1.conf".text = ''
+ forward-zone:
+ name: "example.local."
+ forward-addr: ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address}
+ forward-addr: ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address}
+ '';
+ "unbound-extra2.conf".text = ''
+ auth-zone:
+ name: something.local.
+ zonefile: ${pkgs.writeText "zone" ''
+ something.local. IN A 3.4.5.6
+ ''}
+ '';
+ };
+ };
+
+
+ # plain node that only has network access and doesn't run any part of the
+ # resolver software locally
+ client = { lib, nodes, ... }: {
+ imports = [ common ];
+ networking.nameservers = [
+ (lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address
+ (lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address
+ ];
+ networking.interfaces.eth1.ipv4.addresses = [
+ { address = "192.168.0.10"; prefixLength = 24; }
+ ];
+ networking.interfaces.eth1.ipv6.addresses = [
+ { address = "fd21::10"; prefixLength = 64; }
+ ];
+ };
+ };
+
+ testScript = { nodes, ... }: ''
+ import typing
+ import json
+
+ zone = "example.local."
+ records = [("AAAA", "abcd::eeff"), ("A", "1.2.3.4")]
+
+
+ def query(
+ machine,
+ host: str,
+ query_type: str,
+ query: str,
+ expected: typing.Optional[str] = None,
+ args: typing.Optional[typing.List[str]] = None,
+ ):
+ """
+ Execute a single query and compare the result with expectation
+ """
+ text_args = ""
+ if args:
+ text_args = " ".join(args)
+
+ out = machine.succeed(
+ f"kdig {text_args} {query} {query_type} @{host} +short"
+ ).strip()
+ machine.log(f"{host} replied with {out}")
+ if expected:
+ assert expected == out, f"Expected `{expected}` but got `{out}`"
+
+
+ def test(machine, remotes, /, doh=False, zone=zone, records=records, args=[]):
+ """
+ Run queries for the given remotes on the given machine.
+ """
+ for query_type, expected in records:
+ for remote in remotes:
+ query(machine, remote, query_type, zone, expected, args)
+ query(machine, remote, query_type, zone, expected, ["+tcp"] + args)
+ if doh:
+ query(
+ machine,
+ remote,
+ query_type,
+ zone,
+ expected,
+ ["+tcp", "+tls"] + args,
+ )
+
+
+ client.start()
+ authoritative.wait_for_unit("unbound.service")
+
+ # verify that we can resolve locally
+ with subtest("test the authoritative servers local responses"):
+ test(authoritative, ["::1", "127.0.0.1"])
+
+ resolver.wait_for_unit("unbound.service")
+
+ with subtest("root is unable to use unbounc-control when the socket is not configured"):
+ resolver.succeed("which unbound-control") # the binary must exist
+ resolver.fail("unbound-control list_forwards") # the invocation must fail
+
+ # verify that the resolver is able to resolve on all the local protocols
+ with subtest("test that the resolver resolves on all protocols and transports"):
+ test(resolver, ["::1", "127.0.0.1"], doh=True)
+
+ resolver.wait_for_unit("multi-user.target")
+
+ with subtest("client should be able to query the resolver"):
+ test(client, ["${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address}", "${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address}"], doh=True)
+
+ # discard the client we do not need anymore
+ client.shutdown()
+
+ local_resolver.wait_for_unit("multi-user.target")
+
+ # link a new config file to /etc/unbound/extra.conf
+ local_resolver.succeed("ln -s /etc/unbound-extra1.conf /etc/unbound/extra1.conf")
+
+ # reload the server & ensure the forwarding works
+ with subtest("test that the local resolver resolves on all protocols and transports"):
+ local_resolver.succeed("systemctl reload unbound")
+ print(local_resolver.succeed("journalctl -u unbound -n 1000"))
+ test(local_resolver, ["::1", "127.0.0.1"], args=["+timeout=60"])
+
+ with subtest("test that we can use the unbound control socket"):
+ out = local_resolver.succeed(
+ "sudo -u someuser -- unbound-control list_forwards"
+ ).strip()
+
+ # Thank you black! Can't really break this line into a readable version.
+ expected = "example.local. IN forward ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv6.addresses).address} ${(lib.head nodes.resolver.config.networking.interfaces.eth1.ipv4.addresses).address}"
+ assert out == expected, f"Expected `{expected}` but got `{out}` instead."
+ local_resolver.fail("sudo -u unauthorizeduser -- unbound-control list_forwards")
+
+
+ # link a new config file to /etc/unbound/extra.conf
+ local_resolver.succeed("ln -sf /etc/unbound-extra2.conf /etc/unbound/extra2.conf")
+
+ # reload the server & ensure the new local zone works
+ with subtest("test that we can query the new local zone"):
+ local_resolver.succeed("unbound-control reload")
+ r = [("A", "3.4.5.6")]
+ test(local_resolver, ["::1", "127.0.0.1"], zone="something.local.", records=r)
+ '';
+ })