diff options
Diffstat (limited to 'nixos')
37 files changed, 1196 insertions, 241 deletions
diff --git a/nixos/doc/manual/release-notes/rl-1803.xml b/nixos/doc/manual/release-notes/rl-1803.xml index d2d4a0d32bb2..1a146473e23c 100644 --- a/nixos/doc/manual/release-notes/rl-1803.xml +++ b/nixos/doc/manual/release-notes/rl-1803.xml @@ -113,7 +113,7 @@ following incompatible changes:</para> </listitem> <listitem> <para> - <literal>cc-wrapper</literal>has been split in two; there is now also a <literal>bintools-wrapper</literal>. + <literal>cc-wrapper</literal> has been split in two; there is now also a <literal>bintools-wrapper</literal>. The most commonly used files in <filename>nix-support</filename> are now split between the two wrappers. Some commonly used ones, like <filename>nix-support/dynamic-linker</filename>, are duplicated for backwards compatability, even though they rightly belong only in <literal>bintools-wrapper</literal>. Other more obscure ones are just moved. @@ -133,6 +133,11 @@ following incompatible changes:</para> </listitem> <listitem> <para> + <literal>lib.addPassthru</literal> is removed. Use <literal>lib.extendDerivation true</literal> instead. <emphasis role="strong">TODO: actually remove it before branching 18.03 off.</emphasis> + </para> + </listitem> + <listitem> + <para> The <literal>memcached</literal> service no longer accept dynamic socket paths via <option>services.memcached.socket</option>. Unix sockets can be still enabled by <option>services.memcached.enableUnixSocket</option> and @@ -141,8 +146,7 @@ following incompatible changes:</para> </listitem> <listitem> <para> - The DNSCrypt proxy module has been removed, the upstream project - is no longer maintained. + The <varname>hardware.amdHybridGraphics.disable</varname> option was removed for lack of a maintainer. If you still need this module, you may wish to include a copy of it from an older version of nixos in your imports. </para> </listitem> </itemizedlist> diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix index f06649e1991a..21c69ed560a3 100644 --- a/nixos/lib/make-ext4-fs.nix +++ b/nixos/lib/make-ext4-fs.nix @@ -10,7 +10,7 @@ pkgs.stdenv.mkDerivation { name = "ext4-fs.img"; - buildInputs = with pkgs; [e2fsprogs libfaketime perl]; + nativeBuildInputs = with pkgs; [e2fsprogs libfaketime perl]; # For obtaining the closure of `storePaths'. exportReferencesGraph = diff --git a/nixos/lib/make-squashfs.nix b/nixos/lib/make-squashfs.nix index e66c0ae8f661..9d47a3222cc2 100644 --- a/nixos/lib/make-squashfs.nix +++ b/nixos/lib/make-squashfs.nix @@ -8,7 +8,7 @@ stdenv.mkDerivation { name = "squashfs.img"; - buildInputs = [perl squashfsTools]; + nativeBuildInputs = [perl squashfsTools]; # For obtaining the closure of `storeContents'. exportReferencesGraph = diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index c6440dd906fd..415be580e974 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -301,6 +301,7 @@ pykms = 282; kodi = 283; restya-board = 284; + mighttpd2 = 285; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -570,6 +571,7 @@ pykms = 282; kodi = 283; restya-board = 284; + mighttpd2 = 285; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 8846bf9e8b12..bb3abc256fc1 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -200,6 +200,7 @@ ./services/desktops/dleyna-server.nix ./services/desktops/geoclue2.nix ./services/desktops/gnome3/at-spi2-core.nix + ./services/desktops/gnome3/chrome-gnome-shell.nix ./services/desktops/gnome3/evolution-data-server.nix ./services/desktops/gnome3/gnome-disks.nix ./services/desktops/gnome3/gnome-documents.nix @@ -225,7 +226,6 @@ ./services/games/terraria.nix ./services/hardware/acpid.nix ./services/hardware/actkbd.nix - ./services/hardware/amd-hybrid-graphics.nix ./services/hardware/bluetooth.nix ./services/hardware/brltty.nix ./services/hardware/freefall.nix @@ -446,6 +446,7 @@ ./services/networking/dhcpd.nix ./services/networking/dnscache.nix ./services/networking/dnschain.nix + ./services/networking/dnscrypt-proxy.nix ./services/networking/dnscrypt-wrapper.nix ./services/networking/dnsmasq.nix ./services/networking/ejabberd.nix @@ -633,6 +634,7 @@ ./services/web-servers/lighttpd/default.nix ./services/web-servers/lighttpd/gitweb.nix ./services/web-servers/lighttpd/inginious.nix + ./services/web-servers/mighttpd2.nix ./services/web-servers/minio.nix ./services/web-servers/nginx/default.nix ./services/web-servers/phpfpm/default.nix diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix index 9077643c4440..b995d390b279 100644 --- a/nixos/modules/programs/zsh/oh-my-zsh.nix +++ b/nixos/modules/programs/zsh/oh-my-zsh.nix @@ -48,6 +48,15 @@ in Name of the theme to be used by oh-my-zsh. ''; }; + + cacheDir = mkOption { + default = "$HOME/.cache/oh-my-zsh"; + type = types.str; + description = '' + Cache directory to be used by `oh-my-zsh`. + Without this option it would default to the read-only nix store. + ''; + }; }; }; @@ -74,6 +83,13 @@ in "ZSH_THEME=\"${cfg.theme}\"" } + ${optionalString (cfg.cacheDir != null) '' + if [[ ! -d "${cfg.cacheDir}" ]]; then + mkdir -p "${cfg.cacheDir}" + fi + ZSH_CACHE_DIR=${cfg.cacheDir} + ''} + source $ZSH/oh-my-zsh.sh ''; }; diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index cd6ae35c0d9f..562be13a3f64 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -89,9 +89,6 @@ with lib; # Tarsnap (mkRenamedOptionModule [ "services" "tarsnap" "config" ] [ "services" "tarsnap" "archives" ]) - # dnscrypt-proxy - (mkRemovedOptionModule [ "services" "dnscrypt-proxy" "enable" ] "") - # ibus (mkRenamedOptionModule [ "programs" "ibus" "plugins" ] [ "i18n" "inputMethod" "ibus" "engines" ]) diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index fb011019f7f5..5940f471883c 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -139,6 +139,14 @@ in ''; }; + tosHash = mkOption { + type = types.string; + default = "cc88d8d9517f490191401e7b54e9ffd12a2b9082ec7a1d4cec6101f9f1647e7b"; + description = '' + SHA256 of the Terms of Services document. This changes once in a while. + ''; + }; + production = mkOption { type = types.bool; default = true; @@ -188,7 +196,7 @@ in domain = if data.domain != null then data.domain else cert; cpath = "${cfg.directory}/${cert}"; rights = if data.allowKeysForGroup then "750" else "700"; - cmdline = [ "-v" "-d" domain "--default_root" data.webroot "--valid_min" cfg.validMin ] + cmdline = [ "-v" "-d" domain "--default_root" data.webroot "--valid_min" cfg.validMin "--tos_sha256" cfg.tosHash ] ++ optionals (data.email != null) [ "--email" data.email ] ++ concatMap (p: [ "-f" p ]) data.plugins ++ concatLists (mapAttrsToList (name: root: [ "-d" (if root == null then name else "${name}:${root}")]) data.extraDomains) diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix index cfd0595e63b7..a57f14bb5ae1 100644 --- a/nixos/modules/security/sudo.nix +++ b/nixos/modules/security/sudo.nix @@ -8,6 +8,22 @@ let inherit (pkgs) sudo; + toUserString = user: if (isInt user) then "#${toString user}" else "${user}"; + toGroupString = group: if (isInt group) then "%#${toString group}" else "%${group}"; + + toCommandOptionsString = options: + "${concatStringsSep ":" options}${optionalString (length options != 0) ":"} "; + + toCommandsString = commands: + concatStringsSep ", " ( + map (command: + if (isString command) then + command + else + "${toCommandOptionsString command.options}${command.command}" + ) commands + ); + in { @@ -47,6 +63,97 @@ in ''; }; + security.sudo.extraRules = mkOption { + description = '' + Define specific rules to be in the <filename>sudoers</filename> file. + ''; + default = []; + example = [ + # Allow execution of any command by all users in group sudo, + # requiring a password. + { groups = [ "sudo" ]; commands = [ "ALL" ]; } + + # Allow execution of "/home/root/secret.sh" by user `backup`, `database` + # and the group with GID `1006` without a password. + { users = [ "backup" ]; groups = [ 1006 ]; + commands = [ { command = "/home/root/secret.sh"; options = [ "SETENV" "NOPASSWD" ]; } ]; } + + # Allow all users of group `bar` to run two executables as user `foo` + # with arguments being pre-set. + { groups = [ "bar" ]; runAs = "foo"; + commands = + [ "/home/baz/cmd1.sh hello-sudo" + { command = ''/home/baz/cmd2.sh ""''; options = [ "SETENV" ]; } ]; } + ]; + type = with types; listOf (submodule { + options = { + users = mkOption { + type = with types; listOf (either string int); + description = '' + The usernames / UIDs this rule should apply for. + ''; + default = []; + }; + + groups = mkOption { + type = with types; listOf (either string int); + description = '' + The groups / GIDs this rule should apply for. + ''; + default = []; + }; + + host = mkOption { + type = types.string; + default = "ALL"; + description = '' + For what host this rule should apply. + ''; + }; + + runAs = mkOption { + type = with types; string; + default = "ALL:ALL"; + description = '' + Under which user/group the specified command is allowed to run. + + A user can be specified using just the username: <code>"foo"</code>. + It is also possible to specify a user/group combination using <code>"foo:bar"</code> + or to only allow running as a specific group with <code>":bar"</code>. + ''; + }; + + commands = mkOption { + description = '' + The commands for which the rule should apply. + ''; + type = with types; listOf (either string (submodule { + + options = { + command = mkOption { + type = with types; string; + description = '' + A command being either just a path to a binary to allow any arguments, + the full command with arguments pre-set or with <code>""</code> used as the argument, + not allowing arguments to the command at all. + ''; + }; + + options = mkOption { + type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" ]); + description = '' + Options for running the command. Refer to the <a href="https://www.sudo.ws/man/1.7.10/sudoers.man.html">sudo manual</a>. + ''; + default = []; + }; + }; + + })); + }; + }; + }); + }; + security.sudo.extraConfig = mkOption { type = types.lines; default = ""; @@ -61,10 +168,16 @@ in config = mkIf cfg.enable { + security.sudo.extraRules = [ + { groups = [ "wheel" ]; + commands = [ { command = "ALL"; options = (if cfg.wheelNeedsPassword then [ "SETENV" ] else [ "NOPASSWD" "SETENV" ]); } ]; + } + ]; + security.sudo.configFile = '' # Don't edit this file. Set the NixOS options ‘security.sudo.configFile’ - # or ‘security.sudo.extraConfig’ instead. + # or ‘security.sudo.extraRules’ instead. # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic. Defaults env_keep+=SSH_AUTH_SOCK @@ -72,8 +185,18 @@ in # "root" is allowed to do anything. root ALL=(ALL:ALL) SETENV: ALL - # Users in the "wheel" group can do anything. - %wheel ALL=(ALL:ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL + # extraRules + ${concatStringsSep "\n" ( + lists.flatten ( + map ( + rule: if (length rule.commands != 0) then [ + (map (user: "${toUserString user} ${rule.host}=(${rule.runAs}) ${toCommandsString rule.commands}") rule.users) + (map (group: "${toGroupString group} ${rule.host}=(${rule.runAs}) ${toCommandsString rule.commands}") rule.groups) + ] else [] + ) cfg.extraRules + ) + )} + ${cfg.extraConfig} ''; diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix index 1f64213accd4..77e4b2a616d8 100644 --- a/nixos/modules/security/wrappers/default.nix +++ b/nixos/modules/security/wrappers/default.nix @@ -17,7 +17,7 @@ let hardeningEnable = [ "pie" ]; installPhase = '' mkdir -p $out/bin - gcc -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \ + $CC -Wall -O2 -DWRAPPER_DIR=\"${parentWrapperDir}\" \ -lcap-ng -lcap ${./wrapper.c} -o $out/bin/security-wrapper ''; }; @@ -79,7 +79,7 @@ let ({ owner = "root"; group = "root"; } // s) - else if + else if (s ? "setuid" && s.setuid) || (s ? "setgid" && s.setgid) || (s ? "permissions") diff --git a/nixos/modules/services/audio/mopidy.nix b/nixos/modules/services/audio/mopidy.nix index c0a0f0374294..52613d450b51 100644 --- a/nixos/modules/services/audio/mopidy.nix +++ b/nixos/modules/services/audio/mopidy.nix @@ -4,17 +4,22 @@ with pkgs; with lib; let - uid = config.ids.uids.mopidy; gid = config.ids.gids.mopidy; cfg = config.services.mopidy; mopidyConf = writeText "mopidy.conf" cfg.configuration; - mopidyEnv = python.buildEnv.override { - extraLibs = [ mopidy ] ++ cfg.extensionPackages; + mopidyEnv = buildEnv { + name = "mopidy-with-extensions-${mopidy.version}"; + paths = closePropagation cfg.extensionPackages; + pathsToLink = [ "/${python.sitePackages}" ]; + buildInputs = [ makeWrapper ]; + postBuild = '' + makeWrapper ${mopidy}/bin/mopidy $out/bin/mopidy \ + --prefix PYTHONPATH : $out/${python.sitePackages} + ''; }; - in { options = { @@ -61,7 +66,6 @@ in { }; - ###### implementation config = mkIf cfg.enable { diff --git a/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix new file mode 100644 index 000000000000..2740a22c7ca0 --- /dev/null +++ b/nixos/modules/services/desktops/gnome3/chrome-gnome-shell.nix @@ -0,0 +1,27 @@ +# Chrome GNOME Shell native host connector. +{ config, lib, pkgs, ... }: + +with lib; + +{ + ###### interface + options = { + services.gnome3.chrome-gnome-shell.enable = mkEnableOption '' + Chrome GNOME Shell native host connector, a DBus service + allowing to install GNOME Shell extensions from a web browser. + ''; + }; + + + ###### implementation + config = mkIf config.services.gnome3.chrome-gnome-shell.enable { + environment.etc = { + "chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/chromium/native-messaging-hosts/org.gnome.chrome_gnome_shell.json"; + "opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json".source = "${pkgs.chrome-gnome-shell}/etc/opt/chrome/native-messaging-hosts/org.gnome.chrome_gnome_shell.json"; + }; + + environment.systemPackages = [ pkgs.chrome-gnome-shell ]; + + services.dbus.packages = [ pkgs.chrome-gnome-shell ]; + }; +} diff --git a/nixos/modules/services/hardware/amd-hybrid-graphics.nix b/nixos/modules/services/hardware/amd-hybrid-graphics.nix deleted file mode 100644 index b0f9ff56d1b2..000000000000 --- a/nixos/modules/services/hardware/amd-hybrid-graphics.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ config, pkgs, lib, ... }: - -{ - - ###### interface - - options = { - - hardware.amdHybridGraphics.disable = lib.mkOption { - default = false; - type = lib.types.bool; - description = '' - Completely disable the AMD graphics card and use the - integrated graphics processor instead. - ''; - }; - - }; - - - ###### implementation - - config = lib.mkIf config.hardware.amdHybridGraphics.disable { - systemd.services."amd-hybrid-graphics" = { - path = [ pkgs.bash ]; - description = "Disable AMD Card"; - after = [ "sys-kernel-debug.mount" ]; - before = [ "systemd-vconsole-setup.service" "display-manager.service" ]; - requires = [ "sys-kernel-debug.mount" "vgaswitcheroo.path" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${pkgs.bash}/bin/sh -c 'echo -e \"IGD\\nOFF\" > /sys/kernel/debug/vgaswitcheroo/switch'"; - ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch'"; - }; - }; - systemd.paths."vgaswitcheroo" = { - pathConfig = { - PathExists = "/sys/kernel/debug/vgaswitcheroo/switch"; - Unit = "amd-hybrid-graphics.service"; - }; - wantedBy = ["multi-user.target"]; - }; - }; - -} diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix index 11463cf4500a..80979547d339 100644 --- a/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -578,6 +578,18 @@ in { Extra config options for matrix-synapse. ''; }; + extraConfigFiles = mkOption { + type = types.listOf types.path; + default = []; + description = '' + Extra config files to include. + + The configuration files will be included based on the command line + argument --config-path. This allows to configure secrets without + having to go through the Nix store, e.g. based on deployment keys if + NixOPS is in use. + ''; + }; logConfig = mkOption { type = types.lines; default = readFile ./matrix-synapse-log_config.yaml; @@ -627,7 +639,11 @@ in { Group = "matrix-synapse"; WorkingDirectory = cfg.dataDir; PermissionsStartOnly = true; - ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile} --keys-directory ${cfg.dataDir}"; + ExecStart = '' + ${cfg.package}/bin/homeserver \ + ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) } + --keys-directory ${cfg.dataDir} + ''; Restart = "on-failure"; }; }; diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix index e1fde4fc9500..d23b329eeb25 100644 --- a/nixos/modules/services/monitoring/netdata.nix +++ b/nixos/modules/services/monitoring/netdata.nix @@ -5,18 +5,25 @@ with lib; let cfg = config.services.netdata; - configFile = pkgs.writeText "netdata.conf" cfg.configText; + wrappedPlugins = pkgs.runCommand "wrapped-plugins" {} '' + mkdir -p $out/libexec/netdata/plugins.d + ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin + ''; + + localConfig = { + global = { + "plugins directory" = "${wrappedPlugins}/libexec/netdata/plugins.d ${pkgs.netdata}/libexec/netdata/plugins.d"; + }; + }; + mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config); + configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig); defaultUser = "netdata"; in { options = { services.netdata = { - enable = mkOption { - default = false; - type = types.bool; - description = "Whether to enable netdata monitoring."; - }; + enable = mkEnableOption "netdata"; user = mkOption { type = types.str; @@ -31,9 +38,9 @@ in { }; configText = mkOption { - type = types.lines; - default = ""; - description = "netdata.conf configuration."; + type = types.nullOr types.lines; + description = "Verbatim netdata.conf, cannot be combined with config."; + default = null; example = '' [global] debug log = syslog @@ -42,11 +49,29 @@ in { ''; }; + config = mkOption { + type = types.attrsOf types.attrs; + default = {}; + description = "netdata.conf configuration as nix attributes. cannot be combined with configText."; + example = literalExample '' + global = { + "debug log" = "syslog"; + "access log" = "syslog"; + "error log" = "syslog"; + }; + ''; + }; + }; }; - }; config = mkIf cfg.enable { + assertions = + [ { assertion = cfg.config != {} -> cfg.configText == null ; + message = "Cannot specify both config and configText"; + } + ]; systemd.services.netdata = { + path = with pkgs; [ gawk curl ]; description = "Real time performance monitoring"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; @@ -66,6 +91,15 @@ in { }; }; + security.wrappers."apps.plugin" = { + source = "${pkgs.netdata}/libexec/netdata/plugins.d/apps.plugin"; + capabilities = "cap_dac_read_search,cap_sys_ptrace+ep"; + owner = cfg.user; + group = cfg.group; + permissions = "u+rx,g+rx,o-rwx"; + }; + + users.extraUsers = optional (cfg.user == defaultUser) { name = defaultUser; }; diff --git a/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixos/modules/services/networking/dnscrypt-proxy.nix new file mode 100644 index 000000000000..857657eea4db --- /dev/null +++ b/nixos/modules/services/networking/dnscrypt-proxy.nix @@ -0,0 +1,321 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.dnscrypt-proxy; + + stateDirectory = "/var/lib/dnscrypt-proxy"; + + # The minisign public key used to sign the upstream resolver list. + # This is somewhat more flexible than preloading the key as an + # embedded string. + upstreamResolverListPubKey = pkgs.fetchurl { + url = https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/minisign.pub; + sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh"; + }; + + # Internal flag indicating whether the upstream resolver list is used. + useUpstreamResolverList = cfg.customResolver == null; + + # The final local address. + localAddress = "${cfg.localAddress}:${toString cfg.localPort}"; + + # The final resolvers list path. + resolverList = "${stateDirectory}/dnscrypt-resolvers.csv"; + + # Build daemon command line + + resolverArgs = + if (cfg.customResolver == null) + then + [ "-L ${resolverList}" + "-R ${cfg.resolverName}" + ] + else with cfg.customResolver; + [ "-N ${name}" + "-k ${key}" + "-r ${address}:${toString port}" + ]; + + daemonArgs = + [ "-a ${localAddress}" ] + ++ resolverArgs + ++ cfg.extraArgs; +in + +{ + meta = { + maintainers = with maintainers; [ joachifm ]; + doc = ./dnscrypt-proxy.xml; + }; + + options = { + # Before adding another option, consider whether it could + # equally well be passed via extraArgs. + + services.dnscrypt-proxy = { + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to enable the DNSCrypt client proxy"; + }; + + localAddress = mkOption { + default = "127.0.0.1"; + type = types.str; + description = '' + Listen for DNS queries to relay on this address. The only reason to + change this from its default value is to proxy queries on behalf + of other machines (typically on the local network). + ''; + }; + + localPort = mkOption { + default = 53; + type = types.int; + description = '' + Listen for DNS queries to relay on this port. The default value + assumes that the DNSCrypt proxy should relay DNS queries directly. + When running as a forwarder for another DNS client, set this option + to a different value; otherwise leave the default. + ''; + }; + + resolverName = mkOption { + default = "random"; + example = "dnscrypt.eu-nl"; + type = types.nullOr types.str; + description = '' + The name of the DNSCrypt resolver to use, taken from + <filename>${resolverList}</filename>. The default is to + pick a random non-logging resolver that supports DNSSEC. + ''; + }; + + customResolver = mkOption { + default = null; + description = '' + Use an unlisted resolver (e.g., a private DNSCrypt provider). For + advanced users only. If specified, this option takes precedence. + ''; + type = types.nullOr (types.submodule ({ ... }: { options = { + address = mkOption { + type = types.str; + description = "IP address"; + example = "208.67.220.220"; + }; + + port = mkOption { + type = types.int; + description = "Port"; + default = 443; + }; + + name = mkOption { + type = types.str; + description = "Fully qualified domain name"; + example = "2.dnscrypt-cert.example.com"; + }; + + key = mkOption { + type = types.str; + description = "Public key"; + example = "B735:1140:206F:225D:3E2B:D822:D7FD:691E:A1C3:3CC8:D666:8D0C:BE04:BFAB:CA43:FB79"; + }; + }; })); + }; + + extraArgs = mkOption { + default = []; + type = types.listOf types.str; + description = '' + Additional command-line arguments passed verbatim to the daemon. + See <citerefentry><refentrytitle>dnscrypt-proxy</refentrytitle> + <manvolnum>8</manvolnum></citerefentry> for details. + ''; + example = [ "-X libdcplugin_example_cache.so,--min-ttl=60" ]; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [{ + assertions = [ + { assertion = (cfg.customResolver != null) || (cfg.resolverName != null); + message = "please configure upstream DNSCrypt resolver"; + } + ]; + + users.users.dnscrypt-proxy = { + description = "dnscrypt-proxy daemon user"; + isSystemUser = true; + group = "dnscrypt-proxy"; + }; + users.groups.dnscrypt-proxy = {}; + + systemd.sockets.dnscrypt-proxy = { + description = "dnscrypt-proxy listening socket"; + documentation = [ "man:dnscrypt-proxy(8)" ]; + + wantedBy = [ "sockets.target" ]; + + socketConfig = { + ListenStream = localAddress; + ListenDatagram = localAddress; + }; + }; + + systemd.services.dnscrypt-proxy = { + description = "dnscrypt-proxy daemon"; + documentation = [ "man:dnscrypt-proxy(8)" ]; + + before = [ "nss-lookup.target" ]; + after = [ "network.target" ]; + requires = [ "dnscrypt-proxy.socket "]; + + serviceConfig = { + NonBlocking = "true"; + ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + + User = "dnscrypt-proxy"; + + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + }; + }; + } + + (mkIf config.security.apparmor.enable { + systemd.services.dnscrypt-proxy.after = [ "apparmor.service" ]; + + security.apparmor.profiles = singleton (pkgs.writeText "apparmor-dnscrypt-proxy" '' + ${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy { + /dev/null rw, + /dev/urandom r, + + /etc/passwd r, + /etc/group r, + ${config.environment.etc."nsswitch.conf".source} r, + + ${getLib pkgs.glibc}/lib/*.so mr, + ${pkgs.tzdata}/share/zoneinfo/** r, + + network inet stream, + network inet6 stream, + network inet dgram, + network inet6 dgram, + + ${getLib pkgs.dnscrypt-proxy}/lib/dnscrypt-proxy/libdcplugin*.so mr, + + ${getLib pkgs.gcc.cc}/lib/libssp.so.* mr, + ${getLib pkgs.libsodium}/lib/libsodium.so.* mr, + ${getLib pkgs.systemd}/lib/libsystemd.so.* mr, + ${getLib pkgs.xz}/lib/liblzma.so.* mr, + ${getLib pkgs.libgcrypt}/lib/libgcrypt.so.* mr, + ${getLib pkgs.libgpgerror}/lib/libgpg-error.so.* mr, + ${getLib pkgs.libcap}/lib/libcap.so.* mr, + ${getLib pkgs.lz4}/lib/liblz4.so.* mr, + ${getLib pkgs.attr}/lib/libattr.so.* mr, # */ + + ${resolverList} r, + + /run/systemd/notify rw, + } + ''); + }) + + (mkIf useUpstreamResolverList { + systemd.services.init-dnscrypt-proxy-statedir = { + description = "Initialize dnscrypt-proxy state directory"; + + wantedBy = [ "dnscrypt-proxy.service" ]; + before = [ "dnscrypt-proxy.service" ]; + + script = '' + mkdir -pv ${stateDirectory} + chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory} + cp -uv \ + ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \ + ${stateDirectory} + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + systemd.services.update-dnscrypt-resolvers = { + description = "Update list of DNSCrypt resolvers"; + + requires = [ "init-dnscrypt-proxy-statedir.service" ]; + after = [ "init-dnscrypt-proxy-statedir.service" ]; + + path = with pkgs; [ curl diffutils dnscrypt-proxy minisign ]; + script = '' + cd ${stateDirectory} + domain=raw.githubusercontent.com + get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)" + $get -o dnscrypt-resolvers.csv.tmp \ + https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv + $get -o dnscrypt-resolvers.csv.minisig.tmp \ + https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig + mv dnscrypt-resolvers.csv.minisig{.tmp,} + if ! minisign -q -V -p ${upstreamResolverListPubKey} \ + -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then + echo "failed to verify resolver list!" >&2 + exit 1 + fi + [[ -f dnscrypt-resolvers.csv ]] && mv dnscrypt-resolvers.csv{,.old} + mv dnscrypt-resolvers.csv{.tmp,} + if cmp dnscrypt-resolvers.csv{,.old} ; then + echo "no change" + else + echo "resolver list updated" + fi + ''; + + serviceConfig = { + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + ProtectSystem = "strict"; + ReadWritePaths = "${dirOf stateDirectory} ${stateDirectory}"; + SystemCallFilter = "~@mount"; + }; + }; + + systemd.timers.update-dnscrypt-resolvers = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "5min"; + OnUnitActiveSec = "6h"; + }; + }; + }) + ]); + + imports = [ + (mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ]) + + (mkChangedOptionModule + [ "services" "dnscrypt-proxy" "tcpOnly" ] + [ "services" "dnscrypt-proxy" "extraArgs" ] + (config: + let val = getAttrFromPath [ "services" "dnscrypt-proxy" "tcpOnly" ] config; in + optional val "-T")) + + (mkChangedOptionModule + [ "services" "dnscrypt-proxy" "ephemeralKeys" ] + [ "services" "dnscrypt-proxy" "extraArgs" ] + (config: + let val = getAttrFromPath [ "services" "dnscrypt-proxy" "ephemeralKeys" ] config; in + optional val "-E")) + + (mkRemovedOptionModule [ "services" "dnscrypt-proxy" "resolverList" ] '' + The current resolver listing from upstream is always used + unless a custom resolver is specified. + '') + ]; +} diff --git a/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixos/modules/services/networking/dnscrypt-proxy.xml new file mode 100644 index 000000000000..555c6df4d551 --- /dev/null +++ b/nixos/modules/services/networking/dnscrypt-proxy.xml @@ -0,0 +1,69 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="sec-dnscrypt-proxy"> + + <title>DNSCrypt client proxy</title> + + <para> + The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled + upstream resolver. The traffic between the client and the upstream + resolver is encrypted and authenticated, mitigating the risk of MITM + attacks, DNS poisoning attacks, and third-party snooping (assuming the + upstream is trustworthy). + </para> + + <sect1><title>Basic configuration</title> + + <para> + To enable the client proxy, set + <programlisting> + services.dnscrypt-proxy.enable = true; + </programlisting> + </para> + + <para> + Enabling the client proxy does not alter the system nameserver; to + relay local queries, prepend <literal>127.0.0.1</literal> to + <option>networking.nameservers</option>. + </para> + + </sect1> + + <sect1><title>As a forwarder for another DNS client</title> + + <para> + To run the DNSCrypt proxy client as a forwarder for another + DNS client, change the default proxy listening port to a + non-standard value and point the other client to it: + <programlisting> + services.dnscrypt-proxy.localPort = 43; + </programlisting> + </para> + + <sect2><title>dnsmasq</title> + <para> + <programlisting> + { + services.dnsmasq.enable = true; + services.dnsmasq.servers = [ "127.0.0.1#43" ]; + } + </programlisting> + </para> + </sect2> + + <sect2><title>unbound</title> + <para> + <programlisting> + { + services.unbound.enable = true; + services.unbound.forwardAddresses = [ "127.0.0.1@43" ]; + } + </programlisting> + </para> + </sect2> + + </sect1> + +</chapter> diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix index 23cc92946e41..bf13d5c6f5fe 100644 --- a/nixos/modules/services/networking/dnscrypt-wrapper.nix +++ b/nixos/modules/services/networking/dnscrypt-wrapper.nix @@ -145,6 +145,16 @@ in { }; users.groups.dnscrypt-wrapper = { }; + security.polkit.extraConfig = '' + // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service + polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.systemd1.manage-units" && + action.lookup("unit") == "dnscrypt-wrapper.service" && + subject.user == "dnscrypt-wrapper") { + return polkit.Result.YES; + } + }); + ''; systemd.services.dnscrypt-wrapper = { description = "dnscrypt-wrapper daemon"; diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix index 18e2ab9aebf1..011a9b2f58ea 100644 --- a/nixos/modules/services/networking/kresd.nix +++ b/nixos/modules/services/networking/kresd.nix @@ -72,6 +72,7 @@ in (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") cfg.interfaces; socketConfig.ListenDatagram = listenStreams; + socketConfig.FreeBind = true; }; systemd.sockets.kresd-control = rec { @@ -82,20 +83,11 @@ in socketConfig = { FileDescriptorName = "control"; Service = "kresd.service"; - SocketMode = "0660"; # only root user/group may connect + SocketMode = "0660"; # only root user/group may connect and control kresd }; }; - # Create the cacheDir; tmpfiles don't work on nixos-rebuild switch. - systemd.services.kresd-cachedir = { - serviceConfig.Type = "oneshot"; - script = '' - if [ ! -d '${cfg.cacheDir}' ]; then - mkdir -p '${cfg.cacheDir}' - chown kresd:kresd '${cfg.cacheDir}' - fi - ''; - }; + systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ]; systemd.services.kresd = { description = "Knot-resolver daemon"; @@ -104,16 +96,15 @@ in User = "kresd"; Type = "notify"; WorkingDirectory = cfg.cacheDir; + Restart = "on-failure"; }; script = '' exec '${package}/bin/kresd' --config '${configFile}' \ - -k '${cfg.cacheDir}/root.key' + -k '${pkgs.dns-root-data}/root.key' ''; - after = [ "kresd-cachedir.service" ]; - requires = [ "kresd.socket" "kresd-cachedir.service" ]; - wantedBy = [ "sockets.target" ]; + requires = [ "kresd.socket" ]; }; }; } diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index 81915b5a2ef8..273ca797b98d 100644 --- a/nixos/modules/services/networking/mosquitto.nix +++ b/nixos/modules/services/networking/mosquitto.nix @@ -12,6 +12,10 @@ let keyfile ${cfg.ssl.keyfile} ''; + passwordConf = optionalString cfg.checkPasswords '' + password_file ${cfg.dataDir}/passwd + ''; + mosquittoConf = pkgs.writeText "mosquitto.conf" '' pid_file /run/mosquitto/pid acl_file ${aclFile} @@ -19,6 +23,7 @@ let allow_anonymous ${boolToString cfg.allowAnonymous} bind_address ${cfg.host} port ${toString cfg.port} + ${passwordConf} ${listenerConf} ${cfg.extraConf} ''; @@ -153,6 +158,15 @@ in ''; }; + checkPasswords = mkOption { + default = false; + example = true; + type = types.bool; + description = '' + Refuse connection when clients provide incorrect passwords. + ''; + }; + extraConf = mkOption { default = ""; type = types.lines; diff --git a/nixos/modules/services/networking/openvpn.nix b/nixos/modules/services/networking/openvpn.nix index 3fbf5a9f0227..7a96b673c51e 100644 --- a/nixos/modules/services/networking/openvpn.nix +++ b/nixos/modules/services/networking/openvpn.nix @@ -50,6 +50,11 @@ let "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"} ${optionalString (cfg.down != "" || cfg.updateResolvConf) "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"} + ${optionalString (cfg.authUserPass != null) + "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" '' + ${cfg.authUserPass.username} + ${cfg.authUserPass.password} + ''}"} ''; in { @@ -161,6 +166,29 @@ in ''; }; + authUserPass = mkOption { + default = null; + description = '' + This option can be used to store the username / password credentials + with the "auth-user-pass" authentication method. + + WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store! + ''; + type = types.nullOr (types.submodule { + + options = { + username = mkOption { + description = "The username to store inside the credentials file."; + type = types.string; + }; + + password = mkOption { + description = "The password to store inside the credentials file."; + type = types.string; + }; + }; + }); + }; }; }); diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index aa9c0fa1c09f..d9b12d278160 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -21,7 +21,7 @@ let daemon reads in addition to the the user's authorized_keys file. You can combine the <literal>keys</literal> and <literal>keyFiles</literal> options. - Warning: If you are using <literal>NixOps</literal> then don't use this + Warning: If you are using <literal>NixOps</literal> then don't use this option since it will replace the key required for deployment via ssh. ''; }; @@ -137,6 +137,14 @@ in ''; }; + openFirewall = mkOption { + type = types.bool; + default = true; + description = '' + Whether to automatically open the specified ports in the firewall. + ''; + }; + listenAddresses = mkOption { type = with types; listOf (submodule { options = { @@ -302,7 +310,7 @@ in }; - networking.firewall.allowedTCPPorts = cfg.ports; + networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else []; security.pam.services.sshd = { startSession = true; diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix index c51dd5d94655..adef500b7b5c 100644 --- a/nixos/modules/services/search/elasticsearch.nix +++ b/nixos/modules/services/search/elasticsearch.nix @@ -6,6 +6,7 @@ let cfg = config.services.elasticsearch; es5 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "5" >= 0; + es6 = builtins.compareVersions (builtins.parseDrvName cfg.package.name).version "6" >= 0; esConfig = '' network.host: ${cfg.listenAddress} @@ -92,8 +93,6 @@ in { node.name: "elasticsearch" node.master: true node.data: false - index.number_of_shards: 5 - index.number_of_replicas: 1 ''; }; @@ -165,7 +164,10 @@ in { path = [ pkgs.inetutils ]; environment = { ES_HOME = cfg.dataDir; - ES_JAVA_OPTS = toString ([ "-Des.path.conf=${configDir}" ] ++ cfg.extraJavaOptions); + ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ] + ++ cfg.extraJavaOptions); + } // optionalAttrs es6 { + ES_PATH_CONF = configDir; }; serviceConfig = { ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}"; diff --git a/nixos/modules/services/web-servers/mighttpd2.nix b/nixos/modules/services/web-servers/mighttpd2.nix new file mode 100644 index 000000000000..a888f623616e --- /dev/null +++ b/nixos/modules/services/web-servers/mighttpd2.nix @@ -0,0 +1,132 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.mighttpd2; + configFile = pkgs.writeText "mighty-config" cfg.config; + routingFile = pkgs.writeText "mighty-routing" cfg.routing; +in { + options.services.mighttpd2 = { + enable = mkEnableOption "Mighttpd2 web server"; + + config = mkOption { + default = ""; + example = '' + # Example configuration for Mighttpd 2 + Port: 80 + # IP address or "*" + Host: * + Debug_Mode: Yes # Yes or No + # If available, "nobody" is much more secure for User:. + User: root + # If available, "nobody" is much more secure for Group:. + Group: root + Pid_File: /var/run/mighty.pid + Logging: Yes # Yes or No + Log_File: /var/log/mighty # The directory must be writable by User: + Log_File_Size: 16777216 # bytes + Log_Backup_Number: 10 + Index_File: index.html + Index_Cgi: index.cgi + Status_File_Dir: /usr/local/share/mighty/status + Connection_Timeout: 30 # seconds + Fd_Cache_Duration: 10 # seconds + # Server_Name: Mighttpd/3.x.y + Tls_Port: 443 + Tls_Cert_File: cert.pem # should change this with an absolute path + # should change this with comma-separated absolute paths + Tls_Chain_Files: chain.pem + # Currently, Tls_Key_File must not be encrypted. + Tls_Key_File: privkey.pem # should change this with an absolute path + Service: 0 # 0 is HTTP only, 1 is HTTPS only, 2 is both + ''; + type = types.lines; + description = '' + Verbatim config file to use + (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html) + ''; + }; + + routing = mkOption { + default = ""; + example = '' + # Example routing for Mighttpd 2 + + # Domain lists + [localhost www.example.com] + + # Entries are looked up in the specified order + # All paths must end with "/" + + # A path to CGI scripts should be specified with "=>" + /~alice/cgi-bin/ => /home/alice/public_html/cgi-bin/ + + # A path to static files should be specified with "->" + /~alice/ -> /home/alice/public_html/ + /cgi-bin/ => /export/cgi-bin/ + + # Reverse proxy rules should be specified with ">>" + # /path >> host:port/path2 + # Either "host" or ":port" can be committed, but not both. + /app/cal/ >> example.net/calendar/ + # Yesod app in the same server + /app/wiki/ >> 127.0.0.1:3000/ + + / -> /export/www/ + ''; + type = types.lines; + description = '' + Verbatim routing file to use + (see http://www.mew.org/~kazu/proj/mighttpd/en/config.html) + ''; + }; + + cores = mkOption { + default = null; + type = types.nullOr types.int; + description = '' + How many cores to use. + If null it will be determined automatically + ''; + }; + + }; + + config = mkIf cfg.enable { + assertions = + [ { assertion = cfg.routing != ""; + message = "You need at least one rule in mighttpd2.routing"; + } + ]; + systemd.services.mighttpd2 = { + description = "Mighttpd2 web server"; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = '' + ${pkgs.haskellPackages.mighttpd2}/bin/mighty \ + ${configFile} \ + ${routingFile} \ + +RTS -N${optionalString (cfg.cores != null) "${cfg.cores}"} + ''; + Type = "simple"; + User = "mighttpd2"; + Group = "mighttpd2"; + Restart = "on-failure"; + AmbientCapabilities = "cap_net_bind_service"; + CapabilityBoundingSet = "cap_net_bind_service"; + }; + }; + + users.extraUsers.mighttpd2 = { + group = "mighttpd2"; + uid = config.ids.uids.mighttpd2; + isSystemUser = true; + }; + + users.extraGroups.mighttpd2.gid = config.ids.gids.mighttpd2; + }; + + meta.maintainers = with lib.maintainers; [ fgaz ]; +} diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix index 943415e08c64..0b2e5c0b69d9 100644 --- a/nixos/modules/services/web-servers/tomcat.nix +++ b/nixos/modules/services/web-servers/tomcat.nix @@ -29,7 +29,7 @@ in type = types.package; default = pkgs.tomcat85; defaultText = "pkgs.tomcat85"; - example = lib.literalExample "pkgs.tomcatUnstable"; + example = lib.literalExample "pkgs.tomcat9"; description = '' Which tomcat package to use. ''; diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix index 6ec56f2b1535..814503ab0bc4 100644 --- a/nixos/modules/services/x11/desktop-managers/mate.nix +++ b/nixos/modules/services/x11/desktop-managers/mate.nix @@ -12,6 +12,17 @@ let in filter (x: !(builtins.elem (pkgName x) ysNames)) xs; + addToXDGDirs = p: '' + if [ -d "${p}/share/gsettings-schemas/${p.name}" ]; then + export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}${p}/share/gsettings-schemas/${p.name} + fi + + if [ -d "${p}/lib/girepository-1.0" ]; then + export GI_TYPELIB_PATH=$GI_TYPELIB_PATH''${GI_TYPELIB_PATH:+:}${p}/lib/girepository-1.0 + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH''${LD_LIBRARY_PATH:+:}${p}/lib + fi + ''; + xcfg = config.services.xserver; cfg = xcfg.desktopManager.mate; @@ -20,10 +31,14 @@ in { options = { - services.xserver.desktopManager.mate.enable = mkOption { - type = types.bool; - default = false; - description = "Enable the MATE desktop environment"; + services.xserver.desktopManager.mate = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the MATE desktop environment"; + }; + + debug = mkEnableOption "mate-session debug messages"; }; environment.mate.excludePackages = mkOption { @@ -52,10 +67,29 @@ in # Find the mouse export XCURSOR_PATH=~/.icons:${config.system.path}/share/icons + # Let caja find extensions + export CAJA_EXTENSION_DIRS=$CAJA_EXTENSION_DIRS''${CAJA_EXTENSION_DIRS:+:}${config.system.path}/lib/caja/extensions-2.0 + + # Let caja extensions find gsettings schemas + ${concatMapStrings (p: '' + if [ -d "${p}/lib/caja/extensions-2.0" ]; then + ${addToXDGDirs p} + fi + '') + config.environment.systemPackages + } + + # Let mate-panel find applets + export MATE_PANEL_APPLETS_DIR=$MATE_PANEL_APPLETS_DIR''${MATE_PANEL_APPLETS_DIR:+:}${config.system.path}/share/mate-panel/applets + export MATE_PANEL_EXTRA_MODULES=$MATE_PANEL_EXTRA_MODULES''${MATE_PANEL_EXTRA_MODULES:+:}${config.system.path}/lib/mate-panel/applets + + # Add mate-control-center paths to some XDG variables because its schemas are needed by mate-settings-daemon, and mate-settings-daemon is a dependency for mate-control-center (that is, they are mutually recursive) + ${addToXDGDirs pkgs.mate.mate-control-center} + # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/ ${pkgs.xdg-user-dirs}/bin/xdg-user-dirs-update - ${pkgs.mate.mate-session-manager}/bin/mate-session & + ${pkgs.mate.mate-session-manager}/bin/mate-session ${optionalString cfg.debug "--debug"} & waitPID=$! ''; }; diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 7acd621f53ab..f96d3c5afbac 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -548,7 +548,7 @@ in knownVideoDrivers; in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver)); - nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; }; + nixpkgs.config = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { xorg.abiCompat = "1.18"; }; assertions = [ { assertion = config.security.polkit.enable; diff --git a/nixos/modules/virtualisation/ec2-amis.nix b/nixos/modules/virtualisation/ec2-amis.nix index 44f00d7730be..01512911a057 100644 --- a/nixos/modules/virtualisation/ec2-amis.nix +++ b/nixos/modules/virtualisation/ec2-amis.nix @@ -223,22 +223,22 @@ let self = { "17.03".us-west-2.hvm-ebs = "ami-a93daac9"; "17.03".us-west-2.hvm-s3 = "ami-5139ae31"; - # 17.09.2356.cb751f9b1c3 - "17.09".eu-west-1.hvm-ebs = "ami-d40185ad"; - "17.09".eu-west-2.hvm-ebs = "ami-c5445da1"; - "17.09".eu-west-3.hvm-ebs = "ami-2636815b"; - "17.09".eu-central-1.hvm-ebs = "ami-e758d388"; - "17.09".us-east-1.hvm-ebs = "ami-865327fc"; - "17.09".us-east-2.hvm-ebs = "ami-074d6562"; - "17.09".us-west-1.hvm-ebs = "ami-992c28f9"; - "17.09".us-west-2.hvm-ebs = "ami-2bd87953"; - "17.09".ca-central-1.hvm-ebs = "ami-c4bb01a0"; - "17.09".ap-southeast-1.hvm-ebs = "ami-5ff79723"; - "17.09".ap-southeast-2.hvm-ebs = "ami-57e71135"; - "17.09".ap-northeast-1.hvm-ebs = "ami-5249c434"; - "17.09".ap-northeast-2.hvm-ebs = "ami-f1288e9f"; - "17.09".sa-east-1.hvm-ebs = "ami-5492d438"; - "17.09".ap-south-1.hvm-ebs = "ami-c4fab2ab"; + # 17.09.2681.59661f21be6 + "17.09".eu-west-1.hvm-ebs = "ami-a30192da"; + "17.09".eu-west-2.hvm-ebs = "ami-295a414d"; + "17.09".eu-west-3.hvm-ebs = "ami-8c0eb9f1"; + "17.09".eu-central-1.hvm-ebs = "ami-266cfe49"; + "17.09".us-east-1.hvm-ebs = "ami-40bee63a"; + "17.09".us-east-2.hvm-ebs = "ami-9d84aff8"; + "17.09".us-west-1.hvm-ebs = "ami-d14142b1"; + "17.09".us-west-2.hvm-ebs = "ami-3eb40346"; + "17.09".ca-central-1.hvm-ebs = "ami-ca8207ae"; + "17.09".ap-southeast-1.hvm-ebs = "ami-84bccff8"; + "17.09".ap-southeast-2.hvm-ebs = "ami-0dc5386f"; + "17.09".ap-northeast-1.hvm-ebs = "ami-89b921ef"; + "17.09".ap-northeast-2.hvm-ebs = "ami-179b3b79"; + "17.09".sa-east-1.hvm-ebs = "ami-4762202b"; + "17.09".ap-south-1.hvm-ebs = "ami-4e376021"; latest = self."17.09"; }; in self diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix index 75717e08ab2a..2fb38059b261 100644 --- a/nixos/modules/virtualisation/google-compute-image.nix +++ b/nixos/modules/virtualisation/google-compute-image.nix @@ -212,7 +212,7 @@ in echo "Obtaining SSH keys..." mkdir -m 0700 -p /root/.ssh AUTH_KEYS=$(${mktemp}) - ${wget} -O $AUTH_KEYS http://metadata.google.internal/computeMetadata/v1/project/attributes/sshKeys + ${wget} -O $AUTH_KEYS --header="Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/attributes/sshKeys if [ -s $AUTH_KEYS ]; then # Read in key one by one, split in case Google decided diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index 34a413f1ac3e..6583b13b844e 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -80,7 +80,7 @@ in rec { (all nixos.tests.boot.uefiUsb) (all nixos.tests.boot-stage1) (all nixos.tests.hibernate) - nixos.tests.docker + nixos.tests.docker.x86_64-linux (all nixos.tests.ecryptfs) (all nixos.tests.env) (all nixos.tests.ipv6) diff --git a/nixos/release.nix b/nixos/release.nix index 5ae32928240a..846d87b18d6a 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -3,6 +3,7 @@ , supportedSystems ? [ "x86_64-linux" "aarch64-linux" ] }: +with import ../pkgs/top-level/release-lib.nix { inherit supportedSystems; }; with import ../lib; let @@ -11,13 +12,12 @@ let versionSuffix = (if stableBranch then "." else "pre") + "${toString nixpkgs.revCount}.${nixpkgs.shortRev}"; - forAllSystems = genAttrs supportedSystems; - importTest = fn: args: system: import fn ({ inherit system; } // args); - callTest = fn: args: forAllSystems (system: hydraJob (importTest fn args system)); + callTestOnTheseSystems = systems: fn: args: forTheseSystems systems (system: hydraJob (importTest fn args system)); + callTest = callTestOnTheseSystems supportedSystems; callSubTests = fn: args: let discover = attrs: let @@ -91,13 +91,13 @@ let makeNetboot = config: let - config_evaled = import lib/eval-config.nix config; - build = config_evaled.config.system.build; - kernelTarget = config_evaled.pkgs.stdenv.platform.kernelTarget; + configEvaled = import lib/eval-config.nix config; + build = configEvaled.config.system.build; + kernelTarget = configEvaled.pkgs.stdenv.platform.kernelTarget; in pkgs.symlinkJoin { - name="netboot"; - paths=[ + name = "netboot"; + paths = [ build.netbootRamdisk build.kernel build.netbootIpxeScript @@ -108,6 +108,7 @@ let echo "file initrd $out/initrd" >> $out/nix-support/hydra-build-products echo "file ipxe $out/netboot.ipxe" >> $out/nix-support/hydra-build-products ''; + preferLocalBuild = true; }; @@ -124,22 +125,13 @@ in rec { # Build the initial ramdisk so Hydra can keep track of its size over time. initialRamdisk = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.initialRamdisk); - netboot = { - x86_64-linux = makeNetboot { - system = "x86_64-linux"; - modules = [ - ./modules/installer/netboot/netboot-minimal.nix - versionModule - ]; - }; - } // (optionalAttrs (elem "aarch64-linux" supportedSystems) { - aarch64-linux = makeNetboot { - system = "aarch64-linux"; - modules = [ - ./modules/installer/netboot/netboot-minimal.nix - versionModule - ]; - };}); + netboot = forTheseSystems [ "x86_64-linux" "aarch64-linux" ] (system: makeNetboot { + inherit system; + modules = [ + ./modules/installer/netboot/netboot-minimal.nix + versionModule + ]; + }); iso_minimal = forAllSystems (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-minimal.nix; @@ -147,7 +139,7 @@ in rec { inherit system; }); - iso_graphical = genAttrs [ "x86_64-linux" ] (system: makeIso { + iso_graphical = forTheseSystems [ "x86_64-linux" ] (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-graphical-kde.nix; type = "graphical"; inherit system; @@ -155,7 +147,7 @@ in rec { # A variant with a more recent (but possibly less stable) kernel # that might support more hardware. - iso_minimal_new_kernel = genAttrs [ "x86_64-linux" ] (system: makeIso { + iso_minimal_new_kernel = forTheseSystems [ "x86_64-linux" ] (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-minimal-new-kernel.nix; type = "minimal-new-kernel"; inherit system; @@ -163,7 +155,7 @@ in rec { # A bootable VirtualBox virtual appliance as an OVA file (i.e. packaged OVF). - ova = genAttrs [ "x86_64-linux" ] (system: + ova = forTheseSystems [ "x86_64-linux" ] (system: with import nixpkgs { inherit system; }; @@ -237,7 +229,7 @@ in rec { tests.blivet = callTest tests/blivet.nix {}; tests.boot = callSubTests tests/boot.nix {}; tests.boot-stage1 = callTest tests/boot-stage1.nix {}; - tests.cadvisor = hydraJob (import tests/cadvisor.nix { system = "x86_64-linux"; }); + tests.cadvisor = callTestOnTheseSystems ["x86_64-linux"] tests/cadvisor.nix {}; tests.chromium = (callSubTests tests/chromium.nix { system = "x86_64-linux"; }).stable; tests.cjdns = callTest tests/cjdns.nix {}; tests.cloud-init = callTest tests/cloud-init.nix {}; @@ -252,19 +244,20 @@ in rec { tests.containers-hosts = callTest tests/containers-hosts.nix {}; tests.containers-macvlans = callTest tests/containers-macvlans.nix {}; tests.couchdb = callTest tests/couchdb.nix {}; - tests.docker = hydraJob (import tests/docker.nix { system = "x86_64-linux"; }); - tests.docker-edge = hydraJob (import tests/docker-edge.nix { system = "x86_64-linux"; }); + tests.docker = callTestOnTheseSystems ["x86_64-linux"] tests/docker.nix {}; + tests.docker-edge = callTestOnTheseSystems ["x86_64-linux"] tests/docker-edge.nix {}; tests.dovecot = callTest tests/dovecot.nix {}; + tests.dnscrypt-proxy = callTestOnTheseSystems ["x86_64-linux"] tests/dnscrypt-proxy.nix {}; tests.ecryptfs = callTest tests/ecryptfs.nix {}; - tests.etcd = hydraJob (import tests/etcd.nix { system = "x86_64-linux"; }); + tests.etcd = callTestOnTheseSystems ["x86_64-linux"] tests/etcd.nix {}; tests.ec2-nixops = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-nixops; tests.ec2-config = hydraJob (import tests/ec2.nix { system = "x86_64-linux"; }).boot-ec2-config; - tests.elk = hydraJob (import tests/elk.nix { system = "x86_64-linux"; }); + tests.elk = callSubTests tests/elk.nix { system = "x86_64-linux"; }; tests.env = callTest tests/env.nix {}; tests.ferm = callTest tests/ferm.nix {}; tests.firefox = callTest tests/firefox.nix {}; tests.firewall = callTest tests/firewall.nix {}; - tests.fleet = hydraJob (import tests/fleet.nix { system = "x86_64-linux"; }); + tests.fleet = callTestOnTheseSystems ["x86_64-linux"] tests/fleet.nix {}; #tests.gitlab = callTest tests/gitlab.nix {}; tests.gitolite = callTest tests/gitolite.nix {}; tests.gocd-agent = callTest tests/gocd-agent.nix {}; @@ -311,6 +304,7 @@ in rec { tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; }; tests.nat.firewall-conntrack = callTest tests/nat.nix { withFirewall = true; withConntrackHelpers = true; }; tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; }; + tests.netdata = callTest tests/netdata.nix { }; tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; }; tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; }; # TODO: put in networking.nix after the test becomes more complete @@ -324,7 +318,7 @@ in rec { tests.openssh = callTest tests/openssh.nix {}; tests.owncloud = callTest tests/owncloud.nix {}; tests.pam-oath-login = callTest tests/pam-oath-login.nix {}; - #tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; }); + #tests.panamax = callTestOnTheseSystems ["x86_64-linux"] tests/panamax.nix {}; tests.peerflix = callTest tests/peerflix.nix {}; tests.php-pcre = callTest tests/php-pcre.nix {}; tests.postgresql = callSubTests tests/postgresql.nix {}; @@ -346,6 +340,7 @@ in rec { tests.smokeping = callTest tests/smokeping.nix {}; tests.snapper = callTest tests/snapper.nix {}; tests.statsd = callTest tests/statsd.nix {}; + tests.sudo = callTest tests/sudo.nix {}; tests.switchTest = callTest tests/switch-test.nix {}; tests.taskserver = callTest tests/taskserver.nix {}; tests.tomcat = callTest tests/tomcat.nix {}; diff --git a/nixos/tests/dnscrypt-proxy.nix b/nixos/tests/dnscrypt-proxy.nix new file mode 100644 index 000000000000..845623368250 --- /dev/null +++ b/nixos/tests/dnscrypt-proxy.nix @@ -0,0 +1,32 @@ +import ./make-test.nix ({ pkgs, ... }: { + name = "dnscrypt-proxy"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ joachifm ]; + }; + + nodes = { + # A client running the recommended setup: DNSCrypt proxy as a forwarder + # for a caching DNS client. + client = + { config, pkgs, ... }: + let localProxyPort = 43; in + { + security.apparmor.enable = true; + + services.dnscrypt-proxy.enable = true; + services.dnscrypt-proxy.localPort = localProxyPort; + services.dnscrypt-proxy.extraArgs = [ "-X libdcplugin_example.so" ]; + + services.dnsmasq.enable = true; + services.dnsmasq.servers = [ "127.0.0.1#${toString localProxyPort}" ]; + }; + }; + + testScript = '' + $client->waitForUnit("dnsmasq"); + + # The daemon is socket activated; sending a single ping should activate it. + $client->execute("${pkgs.iputils}/bin/ping -c1 example.com"); + $client->succeed("systemctl is-active dnscrypt-proxy"); + ''; +}) diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix index 65ff1cac070b..ed656b3628b9 100644 --- a/nixos/tests/elk.nix +++ b/nixos/tests/elk.nix @@ -1,95 +1,107 @@ -# Test the ELK stack: Elasticsearch, Logstash and Kibana. - -import ./make-test.nix ({ pkgs, ...} : +{ system ? builtins.currentSystem }: +with import ../lib/testing.nix { inherit system; }; +with pkgs.lib; let esUrl = "http://localhost:9200"; -in { - name = "ELK"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ eelco chaoflow offline basvandijk ]; - }; - nodes = { - one = - { config, pkgs, ... }: { - # Not giving the machine at least 2060MB results in elasticsearch failing with the following error: - # - # OpenJDK 64-Bit Server VM warning: - # INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) - # failed; error='Cannot allocate memory' (errno=12) - # - # There is insufficient memory for the Java Runtime Environment to continue. - # Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory. - # - # When setting this to 2500 I got "Kernel panic - not syncing: Out of - # memory: compulsory panic_on_oom is enabled" so lets give it even a - # bit more room: - virtualisation.memorySize = 3000; + mkElkTest = name : elk : makeTest { + inherit name; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco chaoflow offline basvandijk ]; + }; + nodes = { + one = + { config, pkgs, ... }: { + # Not giving the machine at least 2060MB results in elasticsearch failing with the following error: + # + # OpenJDK 64-Bit Server VM warning: + # INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) + # failed; error='Cannot allocate memory' (errno=12) + # + # There is insufficient memory for the Java Runtime Environment to continue. + # Native memory allocation (mmap) failed to map 2060255232 bytes for committing reserved memory. + # + # When setting this to 2500 I got "Kernel panic - not syncing: Out of + # memory: compulsory panic_on_oom is enabled" so lets give it even a + # bit more room: + virtualisation.memorySize = 3000; - # For querying JSON objects returned from elasticsearch and kibana. - environment.systemPackages = [ pkgs.jq ]; + # For querying JSON objects returned from elasticsearch and kibana. + environment.systemPackages = [ pkgs.jq ]; - services = { - logstash = { - enable = true; - package = pkgs.logstash5; - inputConfig = '' - exec { command => "echo -n flowers" interval => 1 type => "test" } - exec { command => "echo -n dragons" interval => 1 type => "test" } - ''; - filterConfig = '' - if [message] =~ /dragons/ { - drop {} - } - ''; - outputConfig = '' - file { - path => "/tmp/logstash.out" - codec => line { format => "%{message}" } - } - elasticsearch { - hosts => [ "${esUrl}" ] - } - ''; - }; + services = { + logstash = { + enable = true; + package = elk.logstash; + inputConfig = '' + exec { command => "echo -n flowers" interval => 1 type => "test" } + exec { command => "echo -n dragons" interval => 1 type => "test" } + ''; + filterConfig = '' + if [message] =~ /dragons/ { + drop {} + } + ''; + outputConfig = '' + file { + path => "/tmp/logstash.out" + codec => line { format => "%{message}" } + } + elasticsearch { + hosts => [ "${esUrl}" ] + } + ''; + }; - elasticsearch = { - enable = true; - package = pkgs.elasticsearch5; - }; + elasticsearch = { + enable = true; + package = elk.elasticsearch; + }; - kibana = { - enable = true; - package = pkgs.kibana5; - elasticsearch.url = esUrl; + kibana = { + enable = true; + package = elk.kibana; + elasticsearch.url = esUrl; + }; }; }; - }; - }; + }; - testScript = '' - startAll; + testScript = '' + startAll; - $one->waitForUnit("elasticsearch.service"); + $one->waitForUnit("elasticsearch.service"); - # Continue as long as the status is not "red". The status is probably - # "yellow" instead of "green" because we are using a single elasticsearch - # node which elasticsearch considers risky. - # - # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green". - $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red"); + # Continue as long as the status is not "red". The status is probably + # "yellow" instead of "green" because we are using a single elasticsearch + # node which elasticsearch considers risky. + # + # TODO: extend this test with multiple elasticsearch nodes and see if the status turns "green". + $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_cluster/health' | jq .status | grep -v red"); - # Perform some simple logstash tests. - $one->waitForUnit("logstash.service"); - $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers"); - $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons"); + # Perform some simple logstash tests. + $one->waitForUnit("logstash.service"); + $one->waitUntilSucceeds("cat /tmp/logstash.out | grep flowers"); + $one->waitUntilSucceeds("cat /tmp/logstash.out | grep -v dragons"); - # See if kibana is healthy. - $one->waitForUnit("kibana.service"); - $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green"); + # See if kibana is healthy. + $one->waitForUnit("kibana.service"); + $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green"); - # See if logstash messages arive in elasticsearch. - $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0"); - $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0"); - ''; -}) + # See if logstash messages arive in elasticsearch. + $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0"); + $one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0"); + ''; + }; +in mapAttrs mkElkTest { + "ELK-5" = { + elasticsearch = pkgs.elasticsearch5; + logstash = pkgs.logstash5; + kibana = pkgs.kibana5; + }; + "ELK-6" = { + elasticsearch = pkgs.elasticsearch6; + logstash = pkgs.logstash6; + kibana = pkgs.kibana6; + }; +} diff --git a/nixos/tests/keymap.nix b/nixos/tests/keymap.nix index c431c1a34174..eec674e227df 100644 --- a/nixos/tests/keymap.nix +++ b/nixos/tests/keymap.nix @@ -46,6 +46,7 @@ let in makeTest { name = "keymap-${layout}"; + machine.services.xserver.desktopManager.xterm.enable = false; machine.i18n.consoleKeyMap = mkOverride 900 layout; machine.services.xserver.layout = mkOverride 900 layout; machine.imports = [ ./common/x11.nix extraConfig ]; diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index 79290861cb0b..6de17518214c 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -115,11 +115,6 @@ import ./make-test.nix ({ pkgs, ...} : { $machine->succeed("nix-store -qR /run/current-system | grep nixos-"); }; - # Test sudo - subtest "sudo", sub { - $machine->succeed("su - sybil -c 'sudo true'"); - }; - # Test sysctl subtest "sysctl", sub { $machine->waitForUnit("systemd-sysctl.service"); diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix new file mode 100644 index 000000000000..58733c1b3379 --- /dev/null +++ b/nixos/tests/netdata.nix @@ -0,0 +1,31 @@ +# This test runs netdata and checks for data via apps.plugin + +import ./make-test.nix ({ pkgs, ...} : { + name = "netdata"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ cransom ]; + }; + + nodes = { + netdata = + { config, pkgs, ... }: + { + environment.systemPackages = with pkgs; [ curl jq ]; + services.netdata.enable = true; + }; + }; + + testScript = '' + startAll; + + $netdata->waitForUnit("netdata.service"); + # check if netdata can read disk ops for root owned processes. + # if > 0, successful. verifies both netdata working and + # apps.plugin has elevated capabilities. + my $cmd = <<'CMD'; + curl -s http://localhost:19999/api/v1/data\?chart=users.pwrites | \ + jq -e '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0' + CMD + $netdata->waitUntilSucceeds($cmd); + ''; +}) diff --git a/nixos/tests/sudo.nix b/nixos/tests/sudo.nix new file mode 100644 index 000000000000..35addb0ee805 --- /dev/null +++ b/nixos/tests/sudo.nix @@ -0,0 +1,93 @@ +# Some tests to ensure sudo is working properly. + +let + password = "helloworld"; + +in + import ./make-test.nix ({ pkgs, ...} : { + name = "sudo"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ lschuermann ]; + }; + + machine = + { config, lib, pkgs, ... }: + with lib; + { + users.extraGroups = { foobar = {}; barfoo = {}; baz = { gid = 1337; }; }; + users.users = { + test0 = { isNormalUser = true; extraGroups = [ "wheel" ]; }; + test1 = { isNormalUser = true; password = password; }; + test2 = { isNormalUser = true; extraGroups = [ "foobar" ]; password = password; }; + test3 = { isNormalUser = true; extraGroups = [ "barfoo" ]; }; + test4 = { isNormalUser = true; extraGroups = [ "baz" ]; }; + test5 = { isNormalUser = true; }; + }; + + security.sudo = { + enable = true; + wheelNeedsPassword = false; + + extraRules = [ + # SUDOERS SYNTAX CHECK (Test whether the module produces a valid output; + # errors being detected by the visudo checks. + + # These should not create any entries + { users = [ "notest1" ]; commands = [ ]; } + { commands = [ { command = "ALL"; options = [ ]; } ]; } + + # Test defining commands with the options syntax, though not setting any options + { users = [ "notest2" ]; commands = [ { command = "ALL"; options = [ ]; } ]; } + + + # CONFIGURATION FOR TEST CASES + { users = [ "test1" ]; groups = [ "foobar" ]; commands = [ "ALL" ]; } + { groups = [ "barfoo" 1337 ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "NOSETENV" ]; } ]; } + { users = [ "test5" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" "SETENV" ]; } ]; runAs = "test1:barfoo"; } + ]; + }; + }; + + testScript = + '' + subtest "users in wheel group should have passwordless sudo", sub { + $machine->succeed("su - test0 -c \"sudo -u root true\""); + }; + + subtest "test1 user should have sudo with password", sub { + $machine->succeed("su - test1 -c \"echo ${password} | sudo -S -u root true\""); + }; + + subtest "test1 user should not be able to use sudo without password", sub { + $machine->fail("su - test1 -c \"sudo -n -u root true\""); + }; + + subtest "users in group 'foobar' should be able to use sudo with password", sub { + $machine->succeed("sudo -u test2 echo ${password} | sudo -S -u root true"); + }; + + subtest "users in group 'barfoo' should be able to use sudo without password", sub { + $machine->succeed("sudo -u test3 sudo -n -u root true"); + }; + + subtest "users in group 'baz' (GID 1337) should be able to use sudo without password", sub { + $machine->succeed("sudo -u test4 sudo -n -u root echo true"); + }; + + subtest "test5 user should be able to run commands under test1", sub { + $machine->succeed("sudo -u test5 sudo -n -u test1 true"); + }; + + subtest "test5 user should not be able to run commands under root", sub { + $machine->fail("sudo -u test5 sudo -n -u root true"); + }; + + subtest "test5 user should be able to keep his environment", sub { + $machine->succeed("sudo -u test5 sudo -n -E -u test1 true"); + }; + + subtest "users in group 'barfoo' should not be able to keep their environment", sub { + $machine->fail("sudo -u test3 sudo -n -E -u root true"); + }; + ''; + }) |