diff options
Diffstat (limited to 'nixos')
41 files changed, 832 insertions, 354 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml index 8bf9c16e6f8..848e5e63a9c 100644 --- a/nixos/doc/manual/release-notes/rl-2009.xml +++ b/nixos/doc/manual/release-notes/rl-2009.xml @@ -178,6 +178,12 @@ services.mysql.initialScript = pkgs.writeText "mariadb-init.sql" '' </listitem> <listitem> <para> + <literal>vmware</literal> has been removed from the <literal>services.x11.videoDrivers</literal> defaults. + For VMWare guests set <literal>virtualisation.vmware.guest.enable</literal> to <literal>true</literal> which will include the appropriate drivers. + </para> + </listitem> + <listitem> + <para> The initrd SSH support now uses OpenSSH rather than Dropbear to allow the use of Ed25519 keys and other OpenSSH-specific functionality. Host keys must now be in the OpenSSH format, and at @@ -455,6 +461,14 @@ systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/var/www" ]; initrd file has not been deleted. </para> </listitem> + <listitem> + <para> + The <link xlink:href="https://github.com/okTurtles/dnschain">DNSChain</link> + package and NixOS module have been removed from Nixpkgs as the software is + unmaintained and can't be built. For more information see issue + <link xlink:href="https://github.com/NixOS/nixpkgs/issues/89205">#89205</link>. + </para> + </listitem> </itemizedlist> </section> @@ -492,6 +506,17 @@ systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/var/www" ]; </listitem> <listitem> <para> + The cc- and binutils-wrapper's "infix salt" and <literal>_BUILD_</literal> and <literal>_TARGET_</literal> user infixes have been replaced with with a "suffix salt" and suffixes and <literal>_FOR_BUILD</literal> and <literal>_FOR_TARGET</literal>. + This matches the autotools convention for env vars which standard for these things, making interfacing with other tools easier. + </para> + </listitem> + <listitem> + <para> + Additional Git documentation (HTML and text files) is now available via the <literal>git-doc</literal> package. + </para> + </listitem> + <listitem> + <para> Default algorithm for ZRAM swap was changed to <literal>zstd</literal>. </para> </listitem> diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix index 5e86ea479d5..9364aab3277 100644 --- a/nixos/lib/make-disk-image.nix +++ b/nixos/lib/make-disk-image.nix @@ -39,7 +39,7 @@ , name ? "nixos-disk-image" -, # Disk image format, one of qcow2, qcow2-compressed, vpc, raw. +, # Disk image format, one of qcow2, qcow2-compressed, vdi, vpc, raw. format ? "raw" }: @@ -57,6 +57,7 @@ let format' = format; in let filename = "nixos." + { qcow2 = "qcow2"; + vdi = "vdi"; vpc = "vhd"; raw = "img"; }.${format}; diff --git a/nixos/lib/make-iso9660-image.nix b/nixos/lib/make-iso9660-image.nix index be8ad9912db..12a6cf64996 100644 --- a/nixos/lib/make-iso9660-image.nix +++ b/nixos/lib/make-iso9660-image.nix @@ -35,7 +35,7 @@ isohybridMbrImage ? "" , # Whether to compress the resulting ISO image with zstd. - compressImage ? false + compressImage ? false, zstd , # The volume ID. volumeID ? "" diff --git a/nixos/lib/make-iso9660-image.sh b/nixos/lib/make-iso9660-image.sh index eb9edcd7026..d64fe9a365e 100644 --- a/nixos/lib/make-iso9660-image.sh +++ b/nixos/lib/make-iso9660-image.sh @@ -129,7 +129,7 @@ fi if test -n "$compressImage"; then echo "Compressing image..." - zstd -T$NIX_BUILD_CORES $out/iso/$isoName + zstd -T$NIX_BUILD_CORES --rm $out/iso/$isoName fi mkdir -p $out/nix-support diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix index cc2ddda9d32..feb76581a72 100644 --- a/nixos/modules/config/i18n.nix +++ b/nixos/modules/config/i18n.nix @@ -68,7 +68,8 @@ with lib; config = { environment.systemPackages = - optional (config.i18n.supportedLocales != []) config.i18n.glibcLocales; + # We increase the priority a little, so that plain glibc in systemPackages can't win. + optional (config.i18n.supportedLocales != []) (lib.setPrio (-1) config.i18n.glibcLocales); environment.sessionVariables = { LANG = config.i18n.defaultLocale; diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix index 589bf74263b..98c12e30633 100644 --- a/nixos/modules/installer/cd-dvd/sd-image.nix +++ b/nixos/modules/installer/cd-dvd/sd-image.nix @@ -190,7 +190,7 @@ in fsck.vfat -vn firmware_part.img dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS if test -n "$compressImage"; then - zstd -T$NIX_BUILD_CORES $img + zstd -T$NIX_BUILD_CORES --rm $img fi ''; }) {}; diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index 85e5534e906..3409e7ba22e 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -239,7 +239,6 @@ in shout = 206; gateone = 207; namecoin = 208; - dnschain = 209; #lxd = 210; # unused kibana = 211; xtreemfs = 212; @@ -549,7 +548,6 @@ in #shout = 206; #unused gateone = 207; namecoin = 208; - #dnschain = 209; #unused lxd = 210; # unused #kibana = 211; xtreemfs = 212; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 978d33e7585..321fec5fb51 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -334,6 +334,7 @@ ./services/games/minecraft-server.nix ./services/games/minetest-server.nix ./services/games/openarena.nix + ./services/games/teeworlds.nix ./services/games/terraria.nix ./services/hardware/acpid.nix ./services/hardware/actkbd.nix @@ -601,7 +602,6 @@ ./services/networking/dhcpcd.nix ./services/networking/dhcpd.nix ./services/networking/dnscache.nix - ./services/networking/dnschain.nix ./services/networking/dnscrypt-proxy2.nix ./services/networking/dnscrypt-wrapper.nix ./services/networking/dnsdist.nix diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix index 44e65ee8a9a..a983ffa4b89 100644 --- a/nixos/modules/programs/ssh.nix +++ b/nixos/modules/programs/ssh.nix @@ -194,6 +194,33 @@ in ''; }; + kexAlgorithms = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "curve25519-sha256@libssh.org" "diffie-hellman-group-exchange-sha256" ]; + description = '' + Specifies the available KEX (Key Exchange) algorithms. + ''; + }; + + ciphers = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "chacha20-poly1305@openssh.com" "aes256-gcm@openssh.com" ]; + description = '' + Specifies the ciphers allowed and their order of preference. + ''; + }; + + macs = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + example = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha1" ]; + description = '' + Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used + for data integrity protection. + ''; + }; }; }; @@ -232,6 +259,9 @@ in ${optionalString (cfg.pubkeyAcceptedKeyTypes != []) "PubkeyAcceptedKeyTypes ${concatStringsSep "," cfg.pubkeyAcceptedKeyTypes}"} ${optionalString (cfg.hostKeyAlgorithms != []) "HostKeyAlgorithms ${concatStringsSep "," cfg.hostKeyAlgorithms}"} + ${optionalString (cfg.kexAlgorithms != null) "KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}"} + ${optionalString (cfg.ciphers != null) "Ciphers ${concatStringsSep "," cfg.ciphers}"} + ${optionalString (cfg.macs != null) "MACs ${concatStringsSep "," cfg.macs}"} ''; environment.etc."ssh/ssh_known_hosts".text = knownHostsText; diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix index c7a94739349..15d2750c193 100644 --- a/nixos/modules/programs/ssmtp.nix +++ b/nixos/modules/programs/ssmtp.nix @@ -21,9 +21,11 @@ in (mkRenamedOptionModule [ "networking" "defaultMailServer" "useTLS" ] [ "services" "ssmtp" "useTLS" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "useSTARTTLS" ] [ "services" "ssmtp" "useSTARTTLS" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "authUser" ] [ "services" "ssmtp" "authUser" ]) - (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPass" ] [ "services" "ssmtp" "authPass" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPassFile" ] [ "services" "ssmtp" "authPassFile" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "setSendmail" ] [ "services" "ssmtp" "setSendmail" ]) + + (mkRemovedOptionModule [ "networking" "defaultMailServer" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path") + (mkRemovedOptionModule [ "services" "ssmtp" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path") ]; options = { @@ -45,6 +47,21 @@ in ''; }; + settings = mkOption { + type = with types; attrsOf (oneOf [ bool str ]); + default = {}; + description = '' + <citerefentry><refentrytitle>ssmtp</refentrytitle><manvolnum>5</manvolnum></citerefentry> configuration. Refer + to <link xlink:href="https://linux.die.net/man/5/ssmtp.conf"/> for details on supported values. + ''; + example = literalExample '' + { + Debug = true; + FromLineOverride = false; + } + ''; + }; + hostName = mkOption { type = types.str; example = "mail.example.org"; @@ -101,18 +118,6 @@ in ''; }; - authPass = mkOption { - type = types.str; - default = ""; - example = "correctHorseBatteryStaple"; - description = '' - Password used for SMTP auth. (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE) - - It's recommended to use <option>authPassFile</option> - which takes precedence over <option>authPass</option>. - ''; - }; - authPassFile = mkOption { type = types.nullOr types.str; default = null; @@ -121,11 +126,6 @@ in Path to a file that contains the password used for SMTP auth. The file should not contain a trailing newline, if the password does not contain one. This file should be readable by the users that need to execute ssmtp. - - <option>authPassFile</option> takes precedence over <option>authPass</option>. - - Warning: when <option>authPass</option> is non-empty <option>authPassFile</option> - defaults to a file in the WORLD-READABLE Nix store containing that password. ''; }; @@ -142,25 +142,28 @@ in config = mkIf cfg.enable { - services.ssmtp.authPassFile = mkIf (cfg.authPass != "") - (mkDefault (toString (pkgs.writeTextFile { - name = "ssmtp-authpass"; - text = cfg.authPass; - }))); - - environment.etc."ssmtp/ssmtp.conf".text = - let yesNo = yes : if yes then "YES" else "NO"; in - '' - MailHub=${cfg.hostName} - FromLineOverride=YES - ${optionalString (cfg.root != "") "root=${cfg.root}"} - ${optionalString (cfg.domain != "") "rewriteDomain=${cfg.domain}"} - UseTLS=${yesNo cfg.useTLS} - UseSTARTTLS=${yesNo cfg.useSTARTTLS} - #Debug=YES - ${optionalString (cfg.authUser != "") "AuthUser=${cfg.authUser}"} - ${optionalString (cfg.authPassFile != null) "AuthPassFile=${cfg.authPassFile}"} - ''; + services.ssmtp.settings = mkMerge [ + ({ + MailHub = cfg.hostName; + FromLineOverride = mkDefault true; + UseTLS = cfg.useTLS; + UseSTARTTLS = cfg.useSTARTTLS; + }) + (mkIf (cfg.root != "") { root = cfg.root; }) + (mkIf (cfg.domain != "") { rewriteDomain = cfg.domain; }) + (mkIf (cfg.authUser != "") { AuthUser = cfg.authUser; }) + (mkIf (cfg.authPassFile != null) { AuthPassFile = cfg.authPassFile; }) + ]; + + environment.etc."ssmtp/ssmtp.conf".source = + let + toStr = value: + if value == true then "YES" + else if value == false then "NO" + else builtins.toString value + ; + in + pkgs.writeText "ssmtp.conf" (concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${toStr value}") cfg.settings)); environment.systemPackages = [pkgs.ssmtp]; diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index e1a94b0121a..688344852ae 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -436,6 +436,8 @@ let "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} ${optionalString config.security.pam.enableEcryptfs "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} + ${optionalString cfg.pamMount + "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} ${optionalString use_ldap "session optional ${pam_ldap}/lib/security/pam_ldap.so"} ${optionalString config.services.sssd.enable @@ -452,8 +454,6 @@ let "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"} ${optionalString (cfg.showMotd && config.users.motd != null) "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} - ${optionalString cfg.pamMount - "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable) "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"} ${optionalString (cfg.enableKwallet) diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix index 799c3db6216..f1777854e14 100644 --- a/nixos/modules/services/databases/redis.nix +++ b/nixos/modules/services/databases/redis.nix @@ -218,6 +218,7 @@ in description = "Redis database user"; isSystemUser = true; }; + users.groups.redis = {}; environment.systemPackages = [ cfg.package ]; @@ -240,6 +241,7 @@ in StateDirectory = "redis"; Type = "notify"; User = "redis"; + Group = "redis"; }; }; }; diff --git a/nixos/modules/services/games/teeworlds.nix b/nixos/modules/services/games/teeworlds.nix new file mode 100644 index 00000000000..babf989c98c --- /dev/null +++ b/nixos/modules/services/games/teeworlds.nix @@ -0,0 +1,119 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.teeworlds; + register = cfg.register; + + teeworldsConf = pkgs.writeText "teeworlds.cfg" '' + sv_port ${toString cfg.port} + sv_register ${if cfg.register then "1" else "0"} + ${optionalString (cfg.name != null) "sv_name ${cfg.name}"} + ${optionalString (cfg.motd != null) "sv_motd ${cfg.motd}"} + ${optionalString (cfg.password != null) "password ${cfg.password}"} + ${optionalString (cfg.rconPassword != null) "sv_rcon_password ${cfg.rconPassword}"} + ${concatStringsSep "\n" cfg.extraOptions} + ''; + +in +{ + options = { + services.teeworlds = { + enable = mkEnableOption "Teeworlds Server"; + + openPorts = mkOption { + type = types.bool; + default = false; + description = "Whether to open firewall ports for Teeworlds"; + }; + + name = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Name of the server. Defaults to 'unnamed server'. + ''; + }; + + register = mkOption { + type = types.bool; + example = true; + default = false; + description = '' + Whether the server registers as public server in the global server list. This is disabled by default because of privacy. + ''; + }; + + motd = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Set the server message of the day text. + ''; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password to connect to the server. + ''; + }; + + rconPassword = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password to access the remote console. If not set, a randomly generated one is displayed in the server log. + ''; + }; + + port = mkOption { + type = types.int; + default = 8303; + description = '' + Port the server will listen on. + ''; + }; + + extraOptions = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra configuration lines for the <filename>teeworlds.cfg</filename>. See <link xlink:href="https://www.teeworlds.com/?page=docs&wiki=server_settings">Teeworlds Documentation</link>. + ''; + example = [ "sv_map dm1" "sv_gametype dm" ]; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall = mkIf cfg.openPorts { + allowedUDPPorts = [ cfg.port ]; + }; + + systemd.services.teeworlds = { + description = "Teeworlds Server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.teeworlds}/bin/teeworlds_srv -f ${teeworldsConf}"; + + # Hardening + CapabilityBoundingSet = false; + PrivateDevices = true; + PrivateUsers = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + SystemCallArchitectures = "native"; + }; + }; + }; +} diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix index 1fabe2da45c..c838d3b949d 100644 --- a/nixos/modules/services/mail/opensmtpd.nix +++ b/nixos/modules/services/mail/opensmtpd.nix @@ -17,6 +17,10 @@ in { ###### interface + imports = [ + (mkRenamedOptionModule [ "services" "opensmtpd" "addSendmailToSystemPath" ] [ "services" "opensmtpd" "setSendmail" ]) + ]; + options = { services.opensmtpd = { @@ -34,13 +38,10 @@ in { description = "The OpenSMTPD package to use."; }; - addSendmailToSystemPath = mkOption { + setSendmail = mkOption { type = types.bool; default = true; - description = '' - Whether to add OpenSMTPD's sendmail binary to the - system path or not. - ''; + description = "Whether to set the system sendmail to OpenSMTPD's."; }; extraServerArgs = mkOption { @@ -82,7 +83,7 @@ in { ###### implementation - config = mkIf cfg.enable { + config = mkIf cfg.enable rec { users.groups = { smtpd.gid = config.ids.gids.smtpd; smtpq.gid = config.ids.gids.smtpq; @@ -101,6 +102,14 @@ in { }; }; + security.wrappers.smtpctl = { + group = "smtpq"; + setgid = true; + source = "${cfg.package}/bin/smtpctl"; + }; + + services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail security.wrappers.smtpctl; + systemd.tmpfiles.rules = [ "d /var/spool/smtpd 711 root - - -" "d /var/spool/smtpd/offline 770 root smtpq - -" @@ -119,7 +128,5 @@ in { serviceConfig.ExecStart = "${cfg.package}/sbin/smtpd -d -f ${conf} ${args}"; environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/opensmtpd"; }; - - environment.systemPackages = mkIf cfg.addSendmailToSystemPath [ sendmail ]; }; } diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix index 703bc9416f8..8ed4bbdd031 100644 --- a/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -9,6 +9,9 @@ let logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig; mkResource = r: ''{names: ${builtins.toJSON r.names}, compress: ${boolToString r.compress}}''; mkListener = l: ''{port: ${toString l.port}, bind_address: "${l.bind_address}", type: ${l.type}, tls: ${boolToString l.tls}, x_forwarded: ${boolToString l.x_forwarded}, resources: [${concatStringsSep "," (map mkResource l.resources)}]}''; + pluginsEnv = cfg.package.python.buildEnv.override { + extraLibs = cfg.plugins; + }; configFile = pkgs.writeText "homeserver.yaml" '' ${optionalString (cfg.tls_certificate_path != null) '' tls_certificate_path: "${cfg.tls_certificate_path}" @@ -125,6 +128,14 @@ in { Overridable attribute of the matrix synapse server package to use. ''; }; + plugins = mkOption { + type = types.listOf types.package; + default = [ ]; + defaultText = "with config.services.matrix-synapse.package.plugins [ matrix-synapse-ldap3 matrix-synapse-pam ]"; + description = '' + List of additional Matrix plugins to make available. + ''; + }; no_tls = mkOption { type = types.bool; default = false; @@ -686,6 +697,7 @@ in { --keys-directory ${cfg.dataDir} \ --generate-keys ''; + environment.PYTHONPATH = makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ]; serviceConfig = { Type = "notify"; User = "matrix-synapse"; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index f9ad1457fc8..0318acae50f 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -21,6 +21,7 @@ let # `serviceOpts.script` or `serviceOpts.serviceConfig.ExecStart` exporterOpts = genAttrs [ + "apcupsd" "bind" "blackbox" "collectd" @@ -28,6 +29,8 @@ let "dovecot" "fritzbox" "json" + "keylight" + "lnd" "mail" "mikrotik" "minio" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix b/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix new file mode 100644 index 00000000000..57c35a742c5 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.apcupsd; +in +{ + port = 9162; + extraOpts = { + apcupsdAddress = mkOption { + type = types.str; + default = ":3551"; + description = '' + Address of the apcupsd Network Information Server (NIS). + ''; + }; + + apcupsdNetwork = mkOption { + type = types.enum ["tcp" "tcp4" "tcp6"]; + default = "tcp"; + description = '' + Network of the apcupsd Network Information Server (NIS): one of "tcp", "tcp4", or "tcp6". + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-apcupsd-exporter}/bin/apcupsd_exporter \ + -telemetry.addr ${cfg.listenAddress}:${toString cfg.port} \ + -apcupsd.addr ${cfg.apcupsdAddress} \ + -apcupsd.network ${cfg.apcupsdNetwork} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix b/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix new file mode 100644 index 00000000000..dfa56343b87 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.keylight; +in +{ + port = 9288; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-keylight-exporter}/bin/keylight_exporter \ + -metrics.addr ${cfg.listenAddress}:${toString cfg.port} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + }; + }; +} diff --git a/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix b/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix new file mode 100644 index 00000000000..35f97202057 --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.lnd; +in +{ + port = 9092; + extraOpts = { + lndHost = mkOption { + type = types.str; + default = "localhost:10009"; + description = '' + lnd instance gRPC address:port. + ''; + }; + + lndTlsPath = mkOption { + type = types.path; + description = '' + Path to lnd TLS certificate. + ''; + }; + + lndMacaroonDir = mkOption { + type = types.path; + description = '' + Path to lnd macaroons. + ''; + }; + }; + serviceOpts.serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-lnd-exporter}/bin/lndmon \ + --prometheus.listenaddr=${cfg.listenAddress}:${toString cfg.port} \ + --prometheus.logdir=/var/log/prometheus-lnd-exporter \ + --lnd.host=${cfg.lndHost} \ + --lnd.tlspath=${cfg.lndTlsPath} \ + --lnd.macaroondir=${cfg.lndMacaroonDir} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + LogsDirectory = "prometheus-lnd-exporter"; + ReadOnlyPaths = [ cfg.lndTlsPath cfg.lndMacaroonDir ]; + }; +} diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix index 1f5c14d777d..a3bd40135d1 100644 --- a/nixos/modules/services/network-filesystems/ipfs.nix +++ b/nixos/modules/services/network-filesystems/ipfs.nix @@ -1,69 +1,17 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, options, ... }: with lib; let - inherit (pkgs) ipfs runCommand makeWrapper; - cfg = config.services.ipfs; + opt = options.services.ipfs; ipfsFlags = toString ([ (optionalString cfg.autoMount "--mount") - #(optionalString cfg.autoMigrate "--migrate") (optionalString cfg.enableGC "--enable-gc") (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false") (optionalString (cfg.defaultMode == "offline") "--offline") (optionalString (cfg.defaultMode == "norouting") "--routing=none") ] ++ cfg.extraFlags); - defaultDataDir = if versionAtLeast config.system.stateVersion "17.09" then - "/var/lib/ipfs" else - "/var/lib/ipfs/.ipfs"; - - # Wrapping the ipfs binary with the environment variable IPFS_PATH set to dataDir because we can't set it in the user environment - wrapped = runCommand "ipfs" { buildInputs = [ makeWrapper ]; preferLocalBuild = true; } '' - mkdir -p "$out/bin" - makeWrapper "${ipfs}/bin/ipfs" "$out/bin/ipfs" \ - --set IPFS_PATH ${cfg.dataDir} \ - --prefix PATH : /run/wrappers/bin - ''; - - - commonEnv = { - environment.IPFS_PATH = cfg.dataDir; - path = [ wrapped ]; - serviceConfig.User = cfg.user; - serviceConfig.Group = cfg.group; - }; - - baseService = recursiveUpdate commonEnv { - wants = [ "ipfs-init.service" ]; - # NB: migration must be performed prior to pre-start, else we get the failure message! - preStart = optionalString cfg.autoMount '' - ipfs --local config Mounts.FuseAllowOther --json true - ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir} - ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir} - '' + concatStringsSep "\n" (collect - isString - (mapAttrsRecursive - (path: value: - # Using heredoc below so that the value is never improperly quoted - '' - read value <<EOF - ${builtins.toJSON value} - EOF - ipfs --local config --json "${concatStringsSep "." path}" "$value" - '') - ({ Addresses.API = cfg.apiAddress; - Addresses.Gateway = cfg.gatewayAddress; - Addresses.Swarm = cfg.swarmAddress; - } // - cfg.extraConfig)) - ); - serviceConfig = { - ExecStart = "${wrapped}/bin/ipfs daemon ${ipfsFlags}"; - Restart = "on-failure"; - RestartSec = 1; - } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; }; - }; in { ###### interface @@ -88,7 +36,9 @@ in { dataDir = mkOption { type = types.str; - default = defaultDataDir; + default = if versionAtLeast config.system.stateVersion "17.09" + then "/var/lib/ipfs" + else "/var/lib/ipfs/.ipfs"; description = "The data dir for IPFS"; }; @@ -98,18 +48,6 @@ in { description = "systemd service that is enabled by default"; }; - /* - autoMigrate = mkOption { - type = types.bool; - default = false; - description = '' - Whether IPFS should try to migrate the file system automatically. - - The daemon will need to be able to download a binary from https://ipfs.io to perform the migration. - ''; - }; - */ - autoMount = mkOption { type = types.bool; default = false; @@ -199,13 +137,21 @@ in { example = 64*1024; }; + startWhenNeeded = mkOption { + type = types.bool; + default = false; + description = "Whether to use socket activation to start IPFS when needed."; + }; + }; }; ###### implementation config = mkIf cfg.enable { - environment.systemPackages = [ wrapped ]; + environment.systemPackages = [ pkgs.ipfs ]; + environment.variables.IPFS_PATH = cfg.dataDir; + programs.fuse = mkIf cfg.autoMount { userAllowOther = true; }; @@ -234,10 +180,14 @@ in { "d '${cfg.ipnsMountDir}' - ${cfg.user} ${cfg.group} - -" ]; - systemd.services.ipfs-init = recursiveUpdate commonEnv { + systemd.packages = [ pkgs.ipfs ]; + + systemd.services.ipfs-init = { description = "IPFS Initializer"; - before = [ "ipfs.service" "ipfs-offline.service" "ipfs-norouting.service" ]; + environment.IPFS_PATH = cfg.dataDir; + + path = [ pkgs.ipfs ]; script = '' if [[ ! -f ${cfg.dataDir}/config ]]; then @@ -251,34 +201,63 @@ in { fi ''; + wantedBy = [ "default.target" ]; + serviceConfig = { Type = "oneshot"; RemainAfterExit = true; + User = cfg.user; + Group = cfg.group; }; }; - # TODO These 3 definitions possibly be further abstracted through use of a function - # like: mutexServices "ipfs" [ "", "offline", "norouting" ] { ... shared conf here ... } + systemd.services.ipfs = { + path = [ "/run/wrappers" pkgs.ipfs ]; + environment.IPFS_PATH = cfg.dataDir; + + wants = [ "ipfs-init.service" ]; + after = [ "ipfs-init.service" ]; - systemd.services.ipfs = recursiveUpdate baseService { - description = "IPFS Daemon"; - wantedBy = mkIf (cfg.defaultMode == "online") [ "multi-user.target" ]; - after = [ "network.target" "ipfs-init.service" ]; - conflicts = [ "ipfs-offline.service" "ipfs-norouting.service"]; + preStart = optionalString cfg.autoMount '' + ipfs --local config Mounts.FuseAllowOther --json true + ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir} + ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir} + '' + concatStringsSep "\n" (collect + isString + (mapAttrsRecursive + (path: value: + # Using heredoc below so that the value is never improperly quoted + '' + read value <<EOF + ${builtins.toJSON value} + EOF + ipfs --local config --json "${concatStringsSep "." path}" "$value" + '') + ({ Addresses.API = cfg.apiAddress; + Addresses.Gateway = cfg.gatewayAddress; + Addresses.Swarm = cfg.swarmAddress; + } // + cfg.extraConfig)) + ); + serviceConfig = { + ExecStart = ["" "${pkgs.ipfs}/bin/ipfs daemon ${ipfsFlags}"]; + User = cfg.user; + Group = cfg.group; + } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; }; + } // optionalAttrs (!cfg.startWhenNeeded) { + wantedBy = [ "default.target" ]; }; - systemd.services.ipfs-offline = recursiveUpdate baseService { - description = "IPFS Daemon (offline mode)"; - wantedBy = mkIf (cfg.defaultMode == "offline") [ "multi-user.target" ]; - after = [ "ipfs-init.service" ]; - conflicts = [ "ipfs.service" "ipfs-norouting.service"]; + systemd.sockets.ipfs-gateway = { + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = [ "" ] + ++ lib.optional (cfg.gatewayAddress == opt.gatewayAddress.default) [ "127.0.0.1:8080" "[::1]:8080" ]; }; - systemd.services.ipfs-norouting = recursiveUpdate baseService { - description = "IPFS Daemon (no routing mode)"; - wantedBy = mkIf (cfg.defaultMode == "norouting") [ "multi-user.target" ]; - after = [ "ipfs-init.service" ]; - conflicts = [ "ipfs.service" "ipfs-offline.service"]; + systemd.sockets.ipfs-api = { + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = [ "" "%t/ipfs.sock" ] + ++ lib.optional (cfg.apiAddress == opt.apiAddress.default) [ "127.0.0.1:5001" "[::1]:5001" ]; }; }; diff --git a/nixos/modules/services/networking/dnschain.nix b/nixos/modules/services/networking/dnschain.nix deleted file mode 100644 index 003609ea705..00000000000 --- a/nixos/modules/services/networking/dnschain.nix +++ /dev/null @@ -1,184 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfgs = config.services; - cfg = cfgs.dnschain; - - dataDir = "/var/lib/dnschain"; - username = "dnschain"; - - configFile = pkgs.writeText "dnschain.conf" '' - [log] - level = info - - [dns] - host = ${cfg.dns.address} - port = ${toString cfg.dns.port} - oldDNSMethod = NO_OLD_DNS - externalIP = ${cfg.dns.externalAddress} - - [http] - host = ${cfg.api.hostname} - port = ${toString cfg.api.port} - tlsPort = ${toString cfg.api.tlsPort} - - ${cfg.extraConfig} - ''; - -in - -{ - - ###### interface - - options = { - - services.dnschain = { - - enable = mkEnableOption '' - DNSChain, a blockchain based DNS + HTTP server. - To resolve .bit domains set <literal>services.namecoind.enable = true;</literal> - and an RPC username/password. - ''; - - dns.address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = '' - The IP address the DNSChain resolver will bind to. - Leave this unchanged if you do not wish to directly expose the resolver. - ''; - }; - - dns.externalAddress = mkOption { - type = types.str; - default = cfg.dns.address; - description = '' - The IP address used by clients to reach the resolver and the value of - the <literal>namecoin.dns</literal> record. Set this in case the bind address - is not the actual IP address (e.g. the machine is behind a NAT). - ''; - }; - - dns.port = mkOption { - type = types.int; - default = 5333; - description = '' - The port the DNSChain resolver will bind to. - ''; - }; - - api.hostname = mkOption { - type = types.str; - default = "0.0.0.0"; - description = '' - The hostname (or IP address) the DNSChain API server will bind to. - ''; - }; - - api.port = mkOption { - type = types.int; - default = 8080; - description = '' - The port the DNSChain API server (HTTP) will bind to. - ''; - }; - - api.tlsPort = mkOption { - type = types.int; - default = 4433; - description = '' - The port the DNSChain API server (HTTPS) will bind to. - ''; - }; - - extraConfig = mkOption { - type = types.lines; - default = ""; - example = '' - [log] - level = debug - ''; - description = '' - Additional options that will be appended to the configuration file. - ''; - }; - - }; - - services.dnsmasq.resolveDNSChainQueries = mkOption { - type = types.bool; - default = false; - description = '' - Resolve <literal>.bit</literal> top-level domains using DNSChain and namecoin. - ''; - }; - - services.pdns-recursor.resolveDNSChainQueries = mkOption { - type = types.bool; - default = false; - description = '' - Resolve <literal>.bit</literal> top-level domains using DNSChain and namecoin. - ''; - }; - - }; - - - ###### implementation - - config = mkIf cfg.enable { - - services.dnsmasq.servers = optionals cfgs.dnsmasq.resolveDNSChainQueries - [ "/.bit/127.0.0.1#${toString cfg.dns.port}" - "/.dns/127.0.0.1#${toString cfg.dns.port}" - ]; - - services.pdns-recursor = mkIf cfgs.pdns-recursor.resolveDNSChainQueries { - forwardZonesRecurse = - { bit = "127.0.0.1:${toString cfg.dns.port}"; - dns = "127.0.0.1:${toString cfg.dns.port}"; - }; - luaConfig ='' - addNTA("bit", "namecoin doesn't support DNSSEC") - addNTA("dns", "namecoin doesn't support DNSSEC") - ''; - }; - - users.users.${username} = { - description = "DNSChain daemon user"; - home = dataDir; - createHome = true; - uid = config.ids.uids.dnschain; - extraGroups = optional cfgs.namecoind.enable "namecoin"; - }; - - systemd.services.dnschain = { - description = "DNSChain daemon"; - after = optional cfgs.namecoind.enable "namecoind.target"; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - User = "dnschain"; - Restart = "on-failure"; - ExecStart = "${pkgs.nodePackages.dnschain}/bin/dnschain"; - }; - - preStart = '' - # Link configuration file into dnschain home directory - configPath=${dataDir}/.dnschain/dnschain.conf - mkdir -p ${dataDir}/.dnschain - if [ "$(realpath $configPath)" != "${configFile}" ]; then - rm -f $configPath - ln -s ${configFile} $configPath - fi - ''; - }; - - }; - - meta.maintainers = with lib.maintainers; [ rnhmjoj ]; - -} diff --git a/nixos/modules/services/networking/namecoind.nix b/nixos/modules/services/networking/namecoind.nix index ead7f085943..6ca99e1321b 100644 --- a/nixos/modules/services/networking/namecoind.nix +++ b/nixos/modules/services/networking/namecoind.nix @@ -149,11 +149,6 @@ in config = mkIf cfg.enable { - services.dnschain.extraConfig = '' - [namecoin] - config = ${configFile} - ''; - users.users.namecoin = { uid = config.ids.uids.namecoin; description = "Namecoin daemon user"; diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix index e7f5aaed844..1bfcf2de82f 100644 --- a/nixos/modules/services/torrent/transmission.nix +++ b/nixos/modules/services/torrent/transmission.nix @@ -179,6 +179,8 @@ in ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so.* mr, ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so.* mr, ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so.* mr, + ${getLib pkgs.gcc.cc.lib}/lib/libstdc++.so.* mr, + ${getLib pkgs.gcc.cc.lib}/lib/libgcc_s.so.* mr, @{PROC}/sys/kernel/random/uuid r, @{PROC}/sys/vm/overcommit_memory r, diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index f826096bf60..5b9065dec3c 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -34,7 +34,7 @@ let cd ${cfg.package} sudo=exec if [[ "$USER" != nextcloud ]]; then - sudo='exec /run/wrappers/bin/sudo -u nextcloud --preserve-env=NEXTCLOUD_CONFIG_DIR' + sudo='exec /run/wrappers/bin/sudo -u nextcloud --preserve-env=NEXTCLOUD_CONFIG_DIR --preserve-env=OC_PASS' fi export NEXTCLOUD_CONFIG_DIR="${cfg.home}/config" $sudo \ diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index 803419f5d6c..69cf9832172 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -320,8 +320,8 @@ in gnome-shell gnome-shell-extensions gnome-themes-extra - nixos-artwork.wallpapers.simple-dark-gray - nixos-artwork.wallpapers.simple-dark-gray-bottom + pkgs.nixos-artwork.wallpapers.simple-dark-gray + pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom pkgs.gnome-user-docs pkgs.orca pkgs.glib # for gsettings diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 6aec1c0753a..400173745d3 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -246,7 +246,7 @@ in videoDrivers = mkOption { type = types.listOf types.str; # !!! We'd like "nv" here, but it segfaults the X server. - default = [ "radeon" "cirrus" "vesa" "vmware" "modesetting" ]; + default = [ "radeon" "cirrus" "vesa" "modesetting" ]; example = [ "ati_unfree" "amdgpu" "amdgpu-pro" "nv" "nvidia" "nvidiaLegacy390" "nvidiaLegacy340" "nvidiaLegacy304" diff --git a/nixos/modules/system/boot/kernel_config.nix b/nixos/modules/system/boot/kernel_config.nix index a316782dfc5..85bd420e304 100644 --- a/nixos/modules/system/boot/kernel_config.nix +++ b/nixos/modules/system/boot/kernel_config.nix @@ -22,7 +22,7 @@ let mergeFalseByDefault = locs: defs: if defs == [] then abort "This case should never happen." - else if any (x: x == false) defs then false + else if any (x: x == false) (getValues defs) then false else true; kernelItem = types.submodule { @@ -55,6 +55,7 @@ let default = false; description = '' Wether option should generate a failure when unused. + Upon merging values, mandatory wins over optional. ''; }; }; @@ -121,7 +122,7 @@ in type = types.attrsOf kernelItem; example = literalExample '' with lib.kernel; { "9P_NET" = yes; - USB = optional yes; + USB = option yes; MMC_BLOCK_MINORS = freeform "32"; }''; description = '' diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 9b34b12e73a..b0545363b33 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -1178,14 +1178,22 @@ in users.users.systemd-network.group = "systemd-network"; systemd.additionalUpstreamSystemUnits = [ - "systemd-networkd.service" "systemd-networkd-wait-online.service" + "systemd-networkd-wait-online.service" + "systemd-networkd.service" + "systemd-networkd.socket" ]; systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks; + # systemd-networkd is socket-activated by kernel netlink route change + # messages. It is important to have systemd buffer those on behalf of + # networkd. + systemd.sockets.systemd-networkd.wantedBy = [ "sockets.target" ]; + systemd.services.systemd-networkd = { wantedBy = [ "multi-user.target" ]; + aliases = [ "dbus-org.freedesktop.network1.service" ]; restartTriggers = map (x: x.source) (attrValues unitFiles); # prevent race condition with interface renaming (#39069) requires = [ "systemd-udev-settle.service" ]; diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix index b7aaef575ac..b024f9cf5ee 100644 --- a/nixos/modules/system/boot/resolved.nix +++ b/nixos/modules/system/boot/resolved.nix @@ -148,6 +148,7 @@ in systemd.services.systemd-resolved = { wantedBy = [ "multi-user.target" ]; + aliases = [ "dbus-org.freedesktop.resolve1.service" ]; restartTriggers = [ config.environment.etc."systemd/resolved.conf".source ]; }; diff --git a/nixos/modules/system/boot/timesyncd.nix b/nixos/modules/system/boot/timesyncd.nix index 9e2f36ca01f..35fb5578b07 100644 --- a/nixos/modules/system/boot/timesyncd.nix +++ b/nixos/modules/system/boot/timesyncd.nix @@ -41,6 +41,7 @@ with lib; systemd.services.systemd-timesyncd = { wantedBy = [ "sysinit.target" ]; + aliases = [ "dbus-org.freedesktop.timesync1.service" ]; restartTriggers = [ config.environment.etc."systemd/timesyncd.conf".source ]; }; diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index d895c58bab0..2e87197176b 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -232,18 +232,22 @@ let ''; preStop = '' state="/run/nixos/network/routes/${i.name}" - while read cidr; do - echo -n "deleting route $cidr... " - ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" - done < "$state" - rm -f "$state" + if [ -e "$state" ]; then + while read cidr; do + echo -n "deleting route $cidr... " + ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" + done < "$state" + rm -f "$state" + fi state="/run/nixos/network/addresses/${i.name}" - while read cidr; do - echo -n "deleting address $cidr... " - ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" - done < "$state" - rm -f "$state" + if [ -e "$state" ]; then + while read cidr; do + echo -n "deleting address $cidr... " + ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" + done < "$state" + rm -f "$state" + fi ''; }; diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index 53b89a9f55b..3958fc2c1d7 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -15,7 +15,6 @@ in ###### interface options = { - virtualisation.lxd = { enable = mkOption { type = types.bool; @@ -25,12 +24,18 @@ in containers. Users in the "lxd" group can interact with the daemon (e.g. to start or stop containers) using the <command>lxc</command> command line tool, among others. + + Most of the time, you'll also want to start lxcfs, so + that containers can "see" the limits: + <code> + virtualisation.lxc.lxcfs.enable = true; + </code> ''; }; package = mkOption { type = types.package; - default = pkgs.lxd; + default = pkgs.lxd.override { nftablesSupport = config.networking.nftables.enable; }; defaultText = "pkgs.lxd"; description = '' The LXD package to use. @@ -65,6 +70,7 @@ in with nixos. ''; }; + recommendedSysctlSettings = mkOption { type = types.bool; default = false; @@ -83,7 +89,6 @@ in ###### implementation config = mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; security.apparmor = { @@ -115,6 +120,12 @@ in LimitNOFILE = "1048576"; LimitNPROC = "infinity"; TasksMax = "infinity"; + + # By default, `lxd` loads configuration files from hard-coded + # `/usr/share/lxc/config` - since this is a no-go for us, we have to + # explicitly tell it where the actual configuration files are + Environment = mkIf (config.virtualisation.lxc.lxcfs.enable) + "LXD_LXC_TEMPLATE_CONFIG=${pkgs.lxcfs}/share/lxc/config"; }; }; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 4592ffcfe4d..b8d0606be7c 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -441,6 +441,18 @@ in ''; }; + virtualisation.bios = + mkOption { + default = null; + type = types.nullOr types.package; + description = + '' + An alternate BIOS (such as <package>qboot</package>) with which to start the VM. + Should containin a file named <literal>bios.bin</literal>. + If <literal>null</literal>, QEMU's builtin SeaBIOS will be used. + ''; + }; + }; config = { @@ -521,6 +533,9 @@ in (mkIf cfg.useEFIBoot [ "-pflash $TMPDIR/bios.bin" ]) + (mkIf (cfg.bios != null) [ + "-bios ${cfg.bios}/bios.bin" + ]) (mkIf (!cfg.graphics) [ "-nographic" ]) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 796c626f3dd..1bf091b361c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -178,6 +178,8 @@ in limesurvey = handleTest ./limesurvey.nix {}; login = handleTest ./login.nix {}; loki = handleTest ./loki.nix {}; + lxd = handleTest ./lxd.nix {}; + lxd-nftables = handleTest ./lxd-nftables.nix {}; #logstash = handleTest ./logstash.nix {}; lorri = handleTest ./lorri/default.nix {}; magnetico = handleTest ./magnetico.nix {}; @@ -273,6 +275,7 @@ in prosody = handleTest ./xmpp/prosody.nix {}; prosodyMysql = handleTest ./xmpp/prosody-mysql.nix {}; proxy = handleTest ./proxy.nix {}; + qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {}; quagga = handleTest ./quagga.nix {}; quorum = handleTest ./quorum.nix {}; rabbitmq = handleTest ./rabbitmq.nix {}; diff --git a/nixos/tests/hydra/db-migration.nix b/nixos/tests/hydra/db-migration.nix index cf74acfd67a..ca65e2e66aa 100644 --- a/nixos/tests/hydra/db-migration.nix +++ b/nixos/tests/hydra/db-migration.nix @@ -61,7 +61,7 @@ with pkgs.lib; 'curl -L -s http://localhost:3000/build/1 -H "Accept: application/json" | jq .buildstatus | xargs test 0 -eq' ) - out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'") + out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'") assert "jobset_id" not in out original.succeed( @@ -69,7 +69,7 @@ with pkgs.lib; ) original.wait_for_unit("hydra-init.service") - out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'") + out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'") assert "jobset_id|integer|||" in out original.succeed("hydra-backfill-ids") @@ -79,7 +79,7 @@ with pkgs.lib; ) original.wait_for_unit("hydra-init.service") - out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ jobs\" -A'") + out = original.succeed("su -l postgres -c 'psql -d hydra <<< \"\\d+ builds\" -A'") assert "jobset_id|integer||not null|" in out original.wait_until_succeeds( diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix index 4d721aec0c7..82234f96922 100644 --- a/nixos/tests/ipfs.nix +++ b/nixos/tests/ipfs.nix @@ -21,5 +21,12 @@ import ./make-test-python.nix ({ pkgs, ...} : { ) machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord") + + ipfs_hash = machine.succeed( + "echo fnord2 | ipfs --api /unix/run/ipfs.sock add | awk '{ print $2 }'" + ) + machine.succeed( + f"ipfs --api /unix/run/ipfs.sock cat /ipfs/{ipfs_hash.strip()} | grep fnord2" + ) ''; }) diff --git a/nixos/tests/lxd-nftables.nix b/nixos/tests/lxd-nftables.nix new file mode 100644 index 00000000000..25517914db8 --- /dev/null +++ b/nixos/tests/lxd-nftables.nix @@ -0,0 +1,50 @@ +# This test makes sure that lxd stops implicitly depending on iptables when +# user enabled nftables. +# +# It has been extracted from `lxd.nix` for clarity, and because switching from +# iptables to nftables requires a full reboot, which is a bit hard inside NixOS +# tests. + +import ./make-test-python.nix ({ pkgs, ...} : { + name = "lxd-nftables"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ patryk27 ]; + }; + + machine = { lib, ... }: { + virtualisation = { + lxd.enable = true; + }; + + networking = { + firewall.enable = false; + nftables.enable = true; + nftables.ruleset = '' + table inet filter { + chain incoming { + type filter hook input priority 0; + policy accept; + } + + chain forward { + type filter hook forward priority 0; + policy accept; + } + + chain output { + type filter hook output priority 0; + policy accept; + } + } + ''; + }; + }; + + testScript = '' + machine.wait_for_unit("network.target") + + with subtest("When nftables are enabled, lxd doesn't depend on iptables anymore"): + machine.succeed("lsmod | grep nf_tables") + machine.fail("lsmod | grep ip_tables") + ''; +}) diff --git a/nixos/tests/lxd.nix b/nixos/tests/lxd.nix new file mode 100644 index 00000000000..db2d44dff55 --- /dev/null +++ b/nixos/tests/lxd.nix @@ -0,0 +1,135 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +let + # Since we don't have access to the internet during the tests, we have to + # pre-fetch lxd containers beforehand. + # + # I've chosen to import Alpine Linux, because its image is turbo-tiny and, + # generally, sufficient for our tests. + + alpine-meta = pkgs.fetchurl { + url = "https://uk.images.linuxcontainers.org/images/alpine/3.11/i386/default/20200608_13:00/lxd.tar.xz"; + sha256 = "1hkvaj3rr333zmx1759njy435lps33gl4ks8zfm7m4nqvipm26a0"; + }; + + alpine-rootfs = pkgs.fetchurl { + url = "https://uk.images.linuxcontainers.org/images/alpine/3.11/i386/default/20200608_13:00/rootfs.tar.xz"; + sha256 = "1v82zdra4j5xwsff09qlp7h5vbsg54s0j7rdg4rynichfid3r347"; + }; + + lxd-config = pkgs.writeText "config.yaml" '' + storage_pools: + - name: default + driver: dir + config: + source: /var/lxd-pool + + networks: + - name: lxdbr0 + type: bridge + config: + ipv4.address: auto + ipv6.address: none + + profiles: + - name: default + devices: + eth0: + name: eth0 + network: lxdbr0 + type: nic + root: + path: / + pool: default + type: disk + ''; + +in { + name = "lxd"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ patryk27 ]; + }; + + machine = { lib, ... }: { + virtualisation = { + # Since we're testing `limits.cpu`, we've gotta have a known number of + # cores to lay on + cores = 2; + + # Ditto, for `limits.memory` + memorySize = 512; + + lxc.lxcfs.enable = true; + lxd.enable = true; + }; + }; + + testScript = '' + machine.wait_for_unit("sockets.target") + machine.wait_for_unit("lxd.service") + + # It takes additional second for lxd to settle + machine.sleep(1) + + # lxd expects the pool's directory to already exist + machine.succeed("mkdir /var/lxd-pool") + + machine.succeed( + "cat ${lxd-config} | lxd init --preseed" + ) + + machine.succeed( + "lxc image import ${alpine-meta} ${alpine-rootfs} --alias alpine" + ) + + with subtest("Containers can be launched and destroyed"): + machine.succeed("lxc launch alpine test") + machine.succeed("lxc exec test true") + machine.succeed("lxc delete -f test") + + with subtest("Containers are being mounted with lxcfs inside"): + machine.succeed("lxc launch alpine test") + + ## ---------- ## + ## limits.cpu ## + + machine.succeed("lxc config set test limits.cpu 1") + + # Since Alpine doesn't have `nproc` pre-installed, we've gotta resort + # to the primal methods + assert ( + "1" + == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip() + ) + + machine.succeed("lxc config set test limits.cpu 2") + + assert ( + "2" + == machine.succeed("lxc exec test grep -- -c ^processor /proc/cpuinfo").strip() + ) + + ## ------------- ## + ## limits.memory ## + + machine.succeed("lxc config set test limits.memory 64MB") + + assert ( + "MemTotal: 62500 kB" + == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip() + ) + + machine.succeed("lxc config set test limits.memory 128MB") + + assert ( + "MemTotal: 125000 kB" + == machine.succeed("lxc exec test grep -- MemTotal /proc/meminfo").strip() + ) + + machine.succeed("lxc delete -f test") + + with subtest("Unless explicitly changed, lxd leans on iptables"): + machine.succeed("lsmod | grep ip_tables") + machine.fail("lsmod | grep nf_tables") + ''; +}) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index 4fc3668cfaf..4dbd6431222 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -56,6 +56,21 @@ let */ exporterTests = { + apcupsd = { + exporterConfig = { + enable = true; + }; + metricProvider = { + services.apcupsd.enable = true; + }; + exporterTest = '' + wait_for_unit("apcupsd.service") + wait_for_open_port(3551) + wait_for_unit("prometheus-apcupsd-exporter.service") + wait_for_open_port(9162) + succeed("curl -sSf http://localhost:9162/metrics | grep -q 'apcupsd_info'") + ''; + }; bind = { exporterConfig = { @@ -202,6 +217,69 @@ let ''; }; + keylight = { + # A hardware device is required to properly test this exporter, so just + # perform a couple of basic sanity checks that the exporter is running + # and requires a target, but cannot reach a specified target. + exporterConfig = { + enable = true; + }; + exporterTest = '' + wait_for_unit("prometheus-keylight-exporter.service") + wait_for_open_port(9288) + succeed( + "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics | grep -q '400'" + ) + succeed( + "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics?target=nosuchdevice | grep -q '500'" + ) + ''; + }; + + lnd = { + exporterConfig = { + enable = true; + lndTlsPath = "/var/lib/lnd/tls.cert"; + lndMacaroonDir = "/var/lib/lnd"; + }; + metricProvider = { + systemd.services.prometheus-lnd-exporter.serviceConfig.DynamicUser = false; + services.bitcoind.enable = true; + services.bitcoind.extraConfig = '' + rpcauth=bitcoinrpc:e8fe33f797e698ac258c16c8d7aadfbe$872bdb8f4d787367c26bcfd75e6c23c4f19d44a69f5d1ad329e5adf3f82710f7 + bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 + bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 + ''; + systemd.services.lnd = { + serviceConfig.ExecStart = '' + ${pkgs.lnd}/bin/lnd \ + --datadir=/var/lib/lnd \ + --tlscertpath=/var/lib/lnd/tls.cert \ + --tlskeypath=/var/lib/lnd/tls.key \ + --logdir=/var/log/lnd \ + --bitcoin.active \ + --bitcoin.mainnet \ + --bitcoin.node=bitcoind \ + --bitcoind.rpcuser=bitcoinrpc \ + --bitcoind.rpcpass=hunter2 \ + --bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \ + --bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \ + --readonlymacaroonpath=/var/lib/lnd/readonly.macaroon + ''; + serviceConfig.StateDirectory = "lnd"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + }; + }; + exporterTest = '' + wait_for_unit("lnd.service") + wait_for_open_port(10009) + wait_for_unit("prometheus-lnd-exporter.service") + wait_for_open_port(9092) + succeed("curl -sSf localhost:9092/metrics | grep -q '^promhttp_metric_handler'") + ''; + }; + mail = { exporterConfig = { enable = true; diff --git a/nixos/tests/qboot.nix b/nixos/tests/qboot.nix new file mode 100644 index 00000000000..12aef6decfa --- /dev/null +++ b/nixos/tests/qboot.nix @@ -0,0 +1,13 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "qboot"; + + machine = { ... }: { + virtualisation.bios = pkgs.qboot; + }; + + testScript = + '' + start_all() + machine.wait_for_unit("multi-user.target") + ''; +}) diff --git a/nixos/tests/teeworlds.nix b/nixos/tests/teeworlds.nix new file mode 100644 index 00000000000..edf58896878 --- /dev/null +++ b/nixos/tests/teeworlds.nix @@ -0,0 +1,55 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + client = + { pkgs, ... }: + + { imports = [ ./common/x11.nix ]; + environment.systemPackages = [ pkgs.teeworlds ]; + }; + +in { + name = "teeworlds"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ hax404 ]; + }; + + nodes = + { server = + { services.teeworlds = { + enable = true; + openPorts = true; + }; + }; + + client1 = client; + client2 = client; + }; + + testScript = + '' + start_all() + + server.wait_for_unit("teeworlds.service") + server.wait_until_succeeds("ss --numeric --udp --listening | grep -q 8303") + + client1.wait_for_x() + client2.wait_for_x() + + client1.execute("teeworlds 'player_name Alice;connect server'&") + server.wait_until_succeeds( + 'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Alice"' + ) + + client2.execute("teeworlds 'player_name Bob;connect server'&") + server.wait_until_succeeds( + 'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Bob"' + ) + + server.sleep(10) # wait for a while to get a nice screenshot + + client1.screenshot("screen_client1") + client2.screenshot("screen_client2") + ''; + +}) |