diff options
Diffstat (limited to 'nixos/modules')
64 files changed, 2507 insertions, 481 deletions
diff --git a/nixos/modules/config/power-management.nix b/nixos/modules/config/power-management.nix index a4a4d6e1a6a..4c37e8a6208 100644 --- a/nixos/modules/config/power-management.nix +++ b/nixos/modules/config/power-management.nix @@ -69,9 +69,6 @@ in config = mkIf cfg.enable { - # Leftover for old setups, should be set by nixos-generate-config now - powerManagement.cpuFreqGovernor = mkDefault "ondemand"; - systemd.targets.post-resume = { description = "Post-Resume Actions"; requires = [ "post-resume.service" ]; diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix index 65f2e5d7af9..398660967c5 100644 --- a/nixos/modules/config/shells-environment.nix +++ b/nixos/modules/config/shells-environment.nix @@ -36,7 +36,7 @@ in default = {}; description = '' A set of environment variables used in the global environment. - These variables will be set on shell initialisation. + These variables will be set on shell initialisation (e.g. in /etc/profile). The value of each variable can be either a string or a list of strings. The latter is concatenated, interspersed with colon characters. diff --git a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix index efb9ba39bcd..3306846b7fa 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-aarch64.nix @@ -27,6 +27,7 @@ in boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true; + boot.consoleLogLevel = lib.mkDefault 7; boot.kernelPackages = pkgs.linuxPackages_latest; # The serial ports listed here are: @@ -42,8 +43,17 @@ in populateBootCommands = let configTxt = pkgs.writeText "config.txt" '' kernel=u-boot-rpi3.bin + + # Boot in 64-bit mode. arm_control=0x200 + + # U-Boot used to need this to work, regardless of whether UART is actually used or not. + # TODO: check when/if this can be removed. enable_uart=1 + + # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel + # when attempting to show low-voltage or overtemperature warnings. + avoid_warnings=1 ''; in '' (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/boot/) diff --git a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix index 880a6bf2e1e..08903ba397a 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-armv7l-multiplatform.nix @@ -27,6 +27,7 @@ in boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true; + boot.consoleLogLevel = lib.mkDefault 7; boot.kernelPackages = pkgs.linuxPackages_latest; # The serial ports listed here are: # - ttyS0: for Tegra (Jetson TK1) @@ -42,11 +43,18 @@ in sdImage = { populateBootCommands = let configTxt = pkgs.writeText "config.txt" '' + # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel + # when attempting to show low-voltage or overtemperature warnings. + avoid_warnings=1 + [pi2] kernel=u-boot-rpi2.bin [pi3] kernel=u-boot-rpi3.bin + + # U-Boot used to need this to work, regardless of whether UART is actually used or not. + # TODO: check when/if this can be removed. enable_uart=1 ''; in '' diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix index eb676eae05e..2833b75b84d 100644 --- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix +++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi.nix @@ -27,6 +27,7 @@ in boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true; + boot.consoleLogLevel = lib.mkDefault 7; boot.kernelPackages = pkgs.linuxPackages_rpi; # FIXME: this probably should be in installation-device.nix diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index c6440dd906f..c0c6a6ef924 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -301,6 +301,9 @@ pykms = 282; kodi = 283; restya-board = 284; + mighttpd2 = 285; + hass = 286; + monero = 287; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -570,6 +573,9 @@ pykms = 282; kodi = 283; restya-board = 284; + mighttpd2 = 285; + hass = 286; + monero = 287; # 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/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 351a802a517..e747fbc6755 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -6,10 +6,10 @@ let cfg = config.nixpkgs; isConfig = x: - builtins.isAttrs x || builtins.isFunction x; + builtins.isAttrs x || lib.isFunction x; optCall = f: x: - if builtins.isFunction f + if lib.isFunction f then f x else f; @@ -40,7 +40,7 @@ let overlayType = mkOptionType { name = "nixpkgs-overlay"; description = "nixpkgs overlay"; - check = builtins.isFunction; + check = lib.isFunction; merge = lib.mergeOneOption; }; @@ -112,7 +112,6 @@ in [ (self: super: { openssh = super.openssh.override { hpnSupport = true; - withKerberos = true; kerberos = self.libkrb5; }; }; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 8d329b5b4b2..c0c72f2bdb9 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -84,6 +84,7 @@ ./programs/info.nix ./programs/java.nix ./programs/kbdlight.nix + ./programs/less.nix ./programs/light.nix ./programs/man.nix ./programs/mosh.nix @@ -110,6 +111,7 @@ ./programs/wireshark.nix ./programs/xfs_quota.nix ./programs/xonsh.nix + ./programs/yabar.nix ./programs/zsh/oh-my-zsh.nix ./programs/zsh/zsh.nix ./programs/zsh/zsh-syntax-highlighting.nix @@ -200,6 +202,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 +228,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 @@ -314,6 +316,7 @@ ./services/misc/gogs.nix ./services/misc/gollum.nix ./services/misc/gpsd.nix + ./services/misc/home-assistant.nix ./services/misc/ihaskell.nix ./services/misc/irkerd.nix ./services/misc/jackett.nix @@ -415,7 +418,8 @@ ./services/network-filesystems/ipfs.nix ./services/network-filesystems/netatalk.nix ./services/network-filesystems/nfsd.nix - ./services/network-filesystems/openafs-client/default.nix + ./services/network-filesystems/openafs/client.nix + ./services/network-filesystems/openafs/server.nix ./services/network-filesystems/rsyncd.nix ./services/network-filesystems/samba.nix ./services/network-filesystems/tahoe.nix @@ -424,6 +428,7 @@ ./services/network-filesystems/yandex-disk.nix ./services/network-filesystems/xtreemfs.nix ./services/networking/amuled.nix + ./services/networking/aria2.nix ./services/networking/asterisk.nix ./services/networking/atftpd.nix ./services/networking/avahi-daemon.nix @@ -488,6 +493,7 @@ ./services/networking/minidlna.nix ./services/networking/miniupnpd.nix ./services/networking/mosquitto.nix + ./services/networking/monero.nix ./services/networking/miredo.nix ./services/networking/mstpd.nix ./services/networking/murmur.nix @@ -540,6 +546,7 @@ ./services/networking/ssh/lshd.nix ./services/networking/ssh/sshd.nix ./services/networking/strongswan.nix + ./services/networking/stunnel.nix ./services/networking/supplicant.nix ./services/networking/supybot.nix ./services/networking/syncthing.nix @@ -634,6 +641,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/profiles/clone-config.nix b/nixos/modules/profiles/clone-config.nix index 77d86f8d740..5b4e68beb6a 100644 --- a/nixos/modules/profiles/clone-config.nix +++ b/nixos/modules/profiles/clone-config.nix @@ -17,7 +17,7 @@ let # you should use files). moduleFiles = # FIXME: use typeOf (Nix 1.6.1). - filter (x: !isAttrs x && !builtins.isFunction x) modules; + filter (x: !isAttrs x && !lib.isFunction x) modules; # Partition module files because between NixOS and non-NixOS files. NixOS # files may change if the repository is updated. diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix new file mode 100644 index 00000000000..c0283c9e686 --- /dev/null +++ b/nixos/modules/programs/less.nix @@ -0,0 +1,118 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.less; + + configFile = '' + #command + ${concatStringsSep "\n" + (mapAttrsToList (command: action: "${command} ${action}") cfg.commands) + } + ${if cfg.clearDefaultCommands then "#stop" else ""} + + #line-edit + ${concatStringsSep "\n" + (mapAttrsToList (command: action: "${command} ${action}") cfg.lineEditingKeys) + } + + #env + ${concatStringsSep "\n" + (mapAttrsToList (variable: values: "${variable}=${values}") cfg.envVariables) + } + ''; + + lessKey = pkgs.runCommand "lesskey" + { src = pkgs.writeText "lessconfig" configFile; } + "${pkgs.less}/bin/lesskey -o $out $src"; + +in + +{ + options = { + + programs.less = { + + enable = mkEnableOption "less"; + + commands = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { + "h" = "noaction 5\e("; + "l" = "noaction 5\e)"; + }; + description = "Defines new command keys."; + }; + + clearDefaultCommands = mkOption { + type = types.bool; + default = false; + description = '' + Clear all default commands. + You should remember to set the quit key. + Otherwise you will not be able to leave less without killing it. + ''; + }; + + lineEditingKeys = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { + "\e" = "abort"; + }; + description = "Defines new line-editing keys."; + }; + + envVariables = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { + LESS = "--quit-if-one-screen"; + }; + description = "Defines environment variables."; + }; + + lessopen = mkOption { + type = types.nullOr types.str; + default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s"; + description = '' + Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed. + ''; + }; + + lessclose = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may perform any desired clean-up action (such as deleting the replacement file created by LESSOPEN). + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.less ]; + + environment.variables = { + "LESSKEY_SYSTEM" = toString lessKey; + } // optionalAttrs (cfg.lessopen != null) { + "LESSOPEN" = cfg.lessopen; + } // optionalAttrs (cfg.lessclose != null) { + "LESSCLOSE" = cfg.lessclose; + }; + + warnings = optional ( + cfg.clearDefaultCommands && (all (x: x != "quit") (attrValues cfg.commands)) + ) '' + config.programs.less.clearDefaultCommands clears all default commands of less but there is no alternative binding for exiting. + Consider adding a binding for 'quit'. + ''; + }; + + meta.maintainers = with maintainers; [ johnazoidberg ]; + +} diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix index 0f3f42901ba..8ec4169207d 100644 --- a/nixos/modules/programs/shadow.nix +++ b/nixos/modules/programs/shadow.nix @@ -26,8 +26,9 @@ let # Ensure privacy for newly created home directories. UMASK 077 - # Uncomment this to allow non-root users to change their account - #information. This should be made configurable. + # Uncomment this and install chfn SUID to allow non-root + # users to change their account GECOS information. + # This should be made configurable. #CHFN_RESTRICT frwh ''; @@ -103,13 +104,12 @@ in security.wrappers = { su.source = "${pkgs.shadow.su}/bin/su"; - chfn.source = "${pkgs.shadow.out}/bin/chfn"; + sg.source = "${pkgs.shadow.out}/bin/sg"; + newgrp.source = "${pkgs.shadow.out}/bin/newgrp"; newuidmap.source = "${pkgs.shadow.out}/bin/newuidmap"; newgidmap.source = "${pkgs.shadow.out}/bin/newgidmap"; } // (if config.users.mutableUsers then { passwd.source = "${pkgs.shadow.out}/bin/passwd"; - sg.source = "${pkgs.shadow.out}/bin/sg"; - newgrp.source = "${pkgs.shadow.out}/bin/newgrp"; } else {}); }; } diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix new file mode 100644 index 00000000000..a01083c3ace --- /dev/null +++ b/nixos/modules/programs/yabar.nix @@ -0,0 +1,149 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + cfg = config.programs.yabar; + + mapExtra = v: lib.concatStringsSep "\n" (mapAttrsToList ( + key: val: "${key} = ${if (isString val) then "\"${val}\"" else "${builtins.toString val}"};" + ) v); + + listKeys = r: concatStringsSep "," (map (n: "\"${n}\"") (attrNames r)); + + configFile = let + bars = mapAttrsToList ( + name: cfg: '' + ${name}: { + font: "${cfg.font}"; + position: "${cfg.position}"; + + ${mapExtra cfg.extra} + + block-list: [${listKeys cfg.indicators}] + + ${concatStringsSep "\n" (mapAttrsToList ( + name: cfg: '' + ${name}: { + exec: "${cfg.exec}"; + align: "${cfg.align}"; + ${mapExtra cfg.extra} + }; + '' + ) cfg.indicators)} + }; + '' + ) cfg.bars; + in pkgs.writeText "yabar.conf" '' + bar-list = [${listKeys cfg.bars}]; + ${concatStringsSep "\n" bars} + ''; +in + { + options.programs.yabar = { + enable = mkEnableOption "yabar"; + + package = mkOption { + default = pkgs.yabar; + example = literalExample "pkgs.yabar-unstable"; + type = types.package; + + description = '' + The package which contains the `yabar` binary. + + Nixpkgs provides the `yabar` and `yabar-unstable` + derivations since 18.03, so it's possible to choose. + ''; + }; + + bars = mkOption { + default = {}; + type = types.attrsOf(types.submodule { + options = { + font = mkOption { + default = "sans bold 9"; + example = "Droid Sans, FontAwesome Bold 9"; + type = types.string; + + description = '' + The font that will be used to draw the status bar. + ''; + }; + + position = mkOption { + default = "top"; + example = "bottom"; + type = types.enum [ "top" "bottom" ]; + + description = '' + The position where the bar will be rendered. + ''; + }; + + extra = mkOption { + default = {}; + type = types.attrsOf types.string; + + description = '' + An attribute set which contains further attributes of a bar. + ''; + }; + + indicators = mkOption { + default = {}; + type = types.attrsOf(types.submodule { + options.exec = mkOption { + example = "YABAR_DATE"; + type = types.string; + description = '' + The type of the indicator to be executed. + ''; + }; + + options.align = mkOption { + default = "left"; + example = "right"; + type = types.enum [ "left" "center" "right" ]; + + description = '' + Whether to align the indicator at the left or right of the bar. + ''; + }; + + options.extra = mkOption { + default = {}; + type = types.attrsOf (types.either types.string types.int); + + description = '' + An attribute set which contains further attributes of a indicator. + ''; + }; + }); + + description = '' + Indicators that should be rendered by yabar. + ''; + }; + }; + }); + + description = '' + List of bars that should be rendered by yabar. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.user.services.yabar = { + description = "yabar service"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + + script = '' + ${cfg.package}/bin/yabar -c ${configFile} + ''; + + serviceConfig.Restart = "always"; + }; + }; + } diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix index 9077643c444..b995d390b27 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/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix index 6fb1346bbb3..5102bfef032 100644 --- a/nixos/modules/programs/zsh/zsh.nix +++ b/nixos/modules/programs/zsh/zsh.nix @@ -36,8 +36,9 @@ in shellAliases = mkOption { default = config.environment.shellAliases; description = '' - Set of aliases for zsh shell. See <option>environment.shellAliases</option> - for an option format description. + Set of aliases for zsh shell. Overrides the default value taken from + <option>environment.shellAliases</option>. + See <option>environment.shellAliases</option> for an option format description. ''; type = types.attrs; # types.attrsOf types.stringOrPath; }; diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index fb011019f7f..0736239ed2c 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -6,10 +6,11 @@ let cfg = config.security.acme; - certOpts = { ... }: { + certOpts = { name, ... }: { options = { webroot = mkOption { type = types.str; + example = "/var/lib/acme/acme-challenges"; description = '' Where the webroot of the HTTP vhost is located. <filename>.well-known/acme-challenge/</filename> directory @@ -20,8 +21,8 @@ let }; domain = mkOption { - type = types.nullOr types.str; - default = null; + type = types.str; + default = name; description = "Domain to fetch certificate for (defaults to the entry name)"; }; @@ -48,7 +49,7 @@ let default = false; description = '' Give read permissions to the specified group - (<option>security.acme.group</option>) to read SSL private certificates. + (<option>security.acme.cert.<name>.group</option>) to read SSL private certificates. ''; }; @@ -87,7 +88,7 @@ let } ''; description = '' - Extra domain names for which certificates are to be issued, with their + A list of extra domain names, which are included in the one certificate to be issued, with their own server roots if needed. ''; }; @@ -139,6 +140,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; @@ -185,10 +194,9 @@ in servicesLists = mapAttrsToList certToServices cfg.certs; certToServices = cert: data: let - 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" data.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 cfd0595e63b..a57f14bb5ae 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/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix index 36d5340a306..5b739050355 100644 --- a/nixos/modules/services/databases/mysql.nix +++ b/nixos/modules/services/databases/mysql.nix @@ -289,10 +289,10 @@ in # Create initial databases if ! test -e "${cfg.dataDir}/${database.name}"; then echo "Creating initial database: ${database.name}" - ( echo "create database ${database.name};" + ( echo "create database `${database.name}`;" ${optionalString (database ? "schema") '' - echo "use ${database.name};" + echo "use `${database.name}`;" if [ -f "${database.schema}" ] then 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 00000000000..2740a22c7ca --- /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/80-net-setup-link.rules b/nixos/modules/services/hardware/80-net-setup-link.rules new file mode 100644 index 00000000000..18547f170a3 --- /dev/null +++ b/nixos/modules/services/hardware/80-net-setup-link.rules @@ -0,0 +1,13 @@ +# Copied from systemd 203. +ACTION=="remove", GOTO="net_name_slot_end" +SUBSYSTEM!="net", GOTO="net_name_slot_end" +NAME!="", GOTO="net_name_slot_end" + +IMPORT{cmdline}="net.ifnames" +ENV{net.ifnames}=="0", GOTO="net_name_slot_end" + +NAME=="", ENV{ID_NET_NAME_ONBOARD}!="", NAME="$env{ID_NET_NAME_ONBOARD}" +NAME=="", ENV{ID_NET_NAME_SLOT}!="", NAME="$env{ID_NET_NAME_SLOT}" +NAME=="", ENV{ID_NET_NAME_PATH}!="", NAME="$env{ID_NET_NAME_PATH}" + +LABEL="net_name_slot_end" 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 b0f9ff56d1b..00000000000 --- 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/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 730e538e72f..9f42f9e59ad 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -119,7 +119,7 @@ let fi ${optionalString config.networking.usePredictableInterfaceNames '' - cp ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/80-net-setup-link.rules + cp ${./80-net-setup-link.rules} $out/80-net-setup-link.rules ''} # If auto-configuration is disabled, then remove diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 18101a31225..b42c73b8666 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -104,7 +104,7 @@ let }; mailboxConfig = mailbox: '' - mailbox ${mailbox.name} { + mailbox "${mailbox.name}" { auto = ${toString mailbox.auto} '' + optionalString (mailbox.specialUse != null) '' special_use = \${toString mailbox.specialUse} @@ -113,7 +113,7 @@ let mailboxes = { lib, pkgs, ... }: { options = { name = mkOption { - type = types.str; + type = types.strMatching ''[^"]+''; example = "Spam"; description = "The name of the mailbox."; }; diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix index 867c0ea6761..22af7e876af 100644 --- a/nixos/modules/services/mail/postfix.nix +++ b/nixos/modules/services/mail/postfix.nix @@ -15,20 +15,18 @@ let haveVirtual = cfg.virtual != ""; clientAccess = - if (cfg.dnsBlacklistOverrides != "") - then [ "check_client_access hash:/etc/postfix/client_access" ] - else []; + optional (cfg.dnsBlacklistOverrides != "") + "check_client_access hash:/etc/postfix/client_access"; dnsBl = - if (cfg.dnsBlacklists != []) - then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ] - else []; + optionals (cfg.dnsBlacklists != []) + (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists); clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl); mainCf = let escape = replaceStrings ["$"] ["$$"]; - mkList = items: "\n " + concatStringsSep "\n " items; + mkList = items: "\n " + concatStringsSep ",\n " items; mkVal = value: if isList value then mkList value else " " + (if value == true then "yes" @@ -36,72 +34,9 @@ let else toString value); mkEntry = name: value: "${escape name} =${mkVal value}"; in - concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf cfg.config)) + concatStringsSep "\n" (mapAttrsToList mkEntry cfg.config) + "\n" + cfg.extraConfig; - defaultConf = { - compatibility_level = "9999"; - mail_owner = user; - default_privs = "nobody"; - - # NixOS specific locations - data_directory = "/var/lib/postfix/data"; - queue_directory = "/var/lib/postfix/queue"; - - # Default location of everything in package - meta_directory = "${pkgs.postfix}/etc/postfix"; - command_directory = "${pkgs.postfix}/bin"; - sample_directory = "/etc/postfix"; - newaliases_path = "${pkgs.postfix}/bin/newaliases"; - mailq_path = "${pkgs.postfix}/bin/mailq"; - readme_directory = false; - sendmail_path = "${pkgs.postfix}/bin/sendmail"; - daemon_directory = "${pkgs.postfix}/libexec/postfix"; - manpage_directory = "${pkgs.postfix}/share/man"; - html_directory = "${pkgs.postfix}/share/postfix/doc/html"; - shlib_directory = false; - relayhost = if cfg.relayHost == "" then "" else - if cfg.lookupMX - then "${cfg.relayHost}:${toString cfg.relayPort}" - else "[${cfg.relayHost}]:${toString cfg.relayPort}"; - - mail_spool_directory = "/var/spool/mail/"; - setgid_group = setgidGroup; - } - // optionalAttrs config.networking.enableIPv6 { inet_protocols = "all"; } - // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; } - // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; } - // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; } - // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; } - // optionalAttrs (cfg.origin != "") { myorigin = cfg.origin; } - // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; } - // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; } - // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; } - // optionalAttrs haveAliases { alias_maps = "${cfg.aliasMapType}:/etc/postfix/aliases"; } - // optionalAttrs haveTransport { transport_maps = "hash:/etc/postfix/transport"; } - // optionalAttrs haveVirtual { virtual_alias_maps = "${cfg.virtualMapType}:/etc/postfix/virtual"; } - // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; } - // optionalAttrs cfg.useSrs { - sender_canonical_maps = "tcp:127.0.0.1:10001"; - sender_canonical_classes = "envelope_sender"; - recipient_canonical_maps = "tcp:127.0.0.1:10002"; - recipient_canonical_classes= "envelope_recipient"; - } - // optionalAttrs cfg.enableHeaderChecks { header_checks = "regexp:/etc/postfix/header_checks"; } - // optionalAttrs (cfg.sslCert != "") { - smtp_tls_CAfile = cfg.sslCACert; - smtp_tls_cert_file = cfg.sslCert; - smtp_tls_key_file = cfg.sslKey; - - smtp_use_tls = true; - - smtpd_tls_CAfile = cfg.sslCACert; - smtpd_tls_cert_file = cfg.sslCert; - smtpd_tls_key_file = cfg.sslKey; - - smtpd_use_tls = true; - }; - masterCfOptions = { options, config, name, ... }: { options = { name = mkOption { @@ -507,7 +442,6 @@ in config = mkOption { type = with types; attrsOf (either bool (either str (listOf str))); - default = defaultConf; description = '' The main.cf configuration file as key value set. ''; @@ -749,6 +683,67 @@ in ''; }; + services.postfix.config = (mapAttrs (_: v: mkDefault v) { + compatibility_level = "9999"; + mail_owner = cfg.user; + default_privs = "nobody"; + + # NixOS specific locations + data_directory = "/var/lib/postfix/data"; + queue_directory = "/var/lib/postfix/queue"; + + # Default location of everything in package + meta_directory = "${pkgs.postfix}/etc/postfix"; + command_directory = "${pkgs.postfix}/bin"; + sample_directory = "/etc/postfix"; + newaliases_path = "${pkgs.postfix}/bin/newaliases"; + mailq_path = "${pkgs.postfix}/bin/mailq"; + readme_directory = false; + sendmail_path = "${pkgs.postfix}/bin/sendmail"; + daemon_directory = "${pkgs.postfix}/libexec/postfix"; + manpage_directory = "${pkgs.postfix}/share/man"; + html_directory = "${pkgs.postfix}/share/postfix/doc/html"; + shlib_directory = false; + mail_spool_directory = "/var/spool/mail/"; + setgid_group = cfg.setgidGroup; + }) + // optionalAttrs (cfg.relayHost != "") { relayhost = if cfg.lookupMX + then "${cfg.relayHost}:${toString cfg.relayPort}" + else "[${cfg.relayHost}]:${toString cfg.relayPort}"; } + // optionalAttrs config.networking.enableIPv6 { inet_protocols = mkDefault "all"; } + // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; } + // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; } + // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; } + // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; } + // optionalAttrs (cfg.origin != "") { myorigin = cfg.origin; } + // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; } + // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; } + // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; } + // optionalAttrs haveAliases { alias_maps = [ "${cfg.aliasMapType}:/etc/postfix/aliases" ]; } + // optionalAttrs haveTransport { transport_maps = [ "hash:/etc/postfix/transport" ]; } + // optionalAttrs haveVirtual { virtual_alias_maps = [ "${cfg.virtualMapType}:/etc/postfix/virtual" ]; } + // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; } + // optionalAttrs cfg.useSrs { + sender_canonical_maps = [ "tcp:127.0.0.1:10001" ]; + sender_canonical_classes = [ "envelope_sender" ]; + recipient_canonical_maps = [ "tcp:127.0.0.1:10002" ]; + recipient_canonical_classes = [ "envelope_recipient" ]; + } + // optionalAttrs cfg.enableHeaderChecks { header_checks = [ "regexp:/etc/postfix/header_checks" ]; } + // optionalAttrs (cfg.sslCert != "") { + smtp_tls_CAfile = cfg.sslCACert; + smtp_tls_cert_file = cfg.sslCert; + smtp_tls_key_file = cfg.sslKey; + + smtp_use_tls = true; + + smtpd_tls_CAfile = cfg.sslCACert; + smtpd_tls_cert_file = cfg.sslCert; + smtpd_tls_key_file = cfg.sslKey; + + smtpd_use_tls = true; + }; + services.postfix.masterConfig = { smtp_inet = { name = "smtp"; diff --git a/nixos/modules/services/mail/rspamd.nix b/nixos/modules/services/mail/rspamd.nix index b80aa48f2c8..09fb587e74b 100644 --- a/nixos/modules/services/mail/rspamd.nix +++ b/nixos/modules/services/mail/rspamd.nix @@ -1,14 +1,152 @@ -{ config, lib, pkgs, ... }: +{ config, options, pkgs, lib, ... }: with lib; let cfg = config.services.rspamd; + opts = options.services.rspamd; - mkBindSockets = socks: concatStringsSep "\n" (map (each: " bind_socket = \"${each}\"") socks); + bindSocketOpts = {options, config, ... }: { + options = { + socket = mkOption { + type = types.str; + example = "localhost:11333"; + description = '' + Socket for this worker to listen on in a format acceptable by rspamd. + ''; + }; + mode = mkOption { + type = types.str; + default = "0644"; + description = "Mode to set on unix socket"; + }; + owner = mkOption { + type = types.str; + default = "${cfg.user}"; + description = "Owner to set on unix socket"; + }; + group = mkOption { + type = types.str; + default = "${cfg.group}"; + description = "Group to set on unix socket"; + }; + rawEntry = mkOption { + type = types.str; + internal = true; + }; + }; + config.rawEntry = let + maybeOption = option: + optionalString options.${option}.isDefined " ${option}=${config.${option}}"; + in + if (!(hasPrefix "/" config.socket)) then "${config.socket}" + else "${config.socket}${maybeOption "mode"}${maybeOption "owner"}${maybeOption "group"}"; + }; - rspamdConfFile = pkgs.writeText "rspamd.conf" + workerOpts = { name, ... }: { + options = { + enable = mkOption { + type = types.nullOr types.bool; + default = null; + description = "Whether to run the rspamd worker."; + }; + name = mkOption { + type = types.nullOr types.str; + default = name; + description = "Name of the worker"; + }; + type = mkOption { + type = types.nullOr (types.enum [ + "normal" "controller" "fuzzy_storage" "proxy" "lua" + ]); + description = "The type of this worker"; + }; + bindSockets = mkOption { + type = types.listOf (types.either types.str (types.submodule bindSocketOpts)); + default = []; + description = '' + List of sockets to listen, in format acceptable by rspamd + ''; + example = [{ + socket = "/run/rspamd.sock"; + mode = "0666"; + owner = "rspamd"; + } "*:11333"]; + apply = value: map (each: if (isString each) + then if (isUnixSocket each) + then {socket = each; owner = cfg.user; group = cfg.group; mode = "0644"; rawEntry = "${each}";} + else {socket = each; rawEntry = "${each}";} + else each) value; + }; + count = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Number of worker instances to run + ''; + }; + includes = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of files to include in configuration + ''; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional entries to put verbatim into worker section of rspamd config file."; + }; + }; + config = mkIf (name == "normal" || name == "controller" || name == "fuzzy") { + type = mkDefault name; + includes = mkDefault [ "$CONFDIR/worker-${name}.inc" ]; + bindSockets = mkDefault (if name == "normal" + then [{ + socket = "/run/rspamd/rspamd.sock"; + mode = "0660"; + owner = cfg.user; + group = cfg.group; + }] + else if name == "controller" + then [ "localhost:11334" ] + else [] ); + }; + }; + + indexOf = default: start: list: e: + if list == [] + then default + else if (head list) == e then start + else (indexOf default (start + (length (listenStreams (head list).socket))) (tail list) e); + + systemdSocket = indexOf (abort "Socket not found") 0 allSockets; + + isUnixSocket = socket: hasPrefix "/" (if (isString socket) then socket else socket.socket); + isPort = hasPrefix "*:"; + isIPv4Socket = hasPrefix "*v4:"; + isIPv6Socket = hasPrefix "*v6:"; + isLocalHost = hasPrefix "localhost:"; + listenStreams = socket: + if (isLocalHost socket) then + let port = (removePrefix "localhost:" socket); + in [ "127.0.0.1:${port}" ] ++ (if config.networking.enableIPv6 then ["[::1]:${port}"] else []) + else if (isIPv6Socket socket) then [removePrefix "*v6:" socket] + else if (isPort socket) then [removePrefix "*:" socket] + else if (isIPv4Socket socket) then + throw "error: IPv4 only socket not supported in rspamd with socket activation" + else if (length (splitString " " socket)) != 1 then + throw "error: string options not supported in rspamd with socket activation" + else [socket]; + + mkBindSockets = enabled: socks: concatStringsSep "\n " (flatten (map (each: + if cfg.socketActivation && enabled != false then + let systemd = (systemdSocket each); + in (imap (idx: e: "bind_socket = \"systemd:${toString (systemd + idx - 1)}\";") (listenStreams each.socket)) + else "bind_socket = \"${each.rawEntry}\";") socks)); + + rspamdConfFile = pkgs.writeText "rspamd.conf" '' .include "$CONFDIR/common.conf" @@ -22,19 +160,33 @@ let .include "$CONFDIR/logging.inc" } - worker { - ${mkBindSockets cfg.bindSocket} - .include "$CONFDIR/worker-normal.inc" - } - - worker { - ${mkBindSockets cfg.bindUISocket} - .include "$CONFDIR/worker-controller.inc" - } + ${concatStringsSep "\n" (mapAttrsToList (name: value: '' + worker ${optionalString (value.name != "normal" && value.name != "controller") "${value.name}"} { + type = "${value.type}"; + ${optionalString (value.enable != null) + "enabled = ${if value.enable != false then "yes" else "no"};"} + ${mkBindSockets value.enable value.bindSockets} + ${optionalString (value.count != null) "count = ${toString value.count};"} + ${concatStringsSep "\n " (map (each: ".include \"${each}\"") value.includes)} + ${value.extraConfig} + } + '') cfg.workers)} ${cfg.extraConfig} ''; + allMappedSockets = flatten (mapAttrsToList (name: value: + if value.enable != false + then imap (idx: each: { + name = "${name}"; + index = idx; + value = each; + }) value.bindSockets + else []) cfg.workers); + allSockets = map (e: e.value) allMappedSockets; + + allSocketNames = map (each: "rspamd-${each.name}-${toString each.index}.socket") allMappedSockets; + in { @@ -48,36 +200,43 @@ in enable = mkEnableOption "Whether to run the rspamd daemon."; debug = mkOption { + type = types.bool; default = false; description = "Whether to run the rspamd daemon in debug mode."; }; - bindSocket = mkOption { - type = types.listOf types.str; - default = [ - "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}" - ]; - defaultText = ''[ - "/run/rspamd/rspamd.sock mode=0660 owner=${cfg.user} group=${cfg.group}" - ]''; + socketActivation = mkOption { + type = types.bool; description = '' - List of sockets to listen, in format acceptable by rspamd - ''; - example = '' - bindSocket = [ - "/run/rspamd.sock mode=0666 owner=rspamd" - "*:11333" - ]; + Enable systemd socket activation for rspamd. ''; }; - bindUISocket = mkOption { - type = types.listOf types.str; - default = [ - "localhost:11334" - ]; + workers = mkOption { + type = with types; attrsOf (submodule workerOpts); description = '' - List of sockets for web interface, in format acceptable by rspamd + Attribute set of workers to start. + ''; + default = { + normal = {}; + controller = {}; + }; + example = literalExample '' + { + normal = { + includes = [ "$CONFDIR/worker-normal.inc" ]; + bindSockets = [{ + socket = "/run/rspamd/rspamd.sock"; + mode = "0660"; + owner = "${cfg.user}"; + group = "${cfg.group}"; + }]; + }; + controller = { + includes = [ "$CONFDIR/worker-controller.inc" ]; + bindSockets = [ "[::1]:11334" ]; + }; + } ''; }; @@ -113,6 +272,13 @@ in config = mkIf cfg.enable { + services.rspamd.socketActivation = mkDefault (!opts.bindSocket.isDefined && !opts.bindUISocket.isDefined); + + assertions = [ { + assertion = !cfg.socketActivation || !(opts.bindSocket.isDefined || opts.bindUISocket.isDefined); + message = "Can't use socketActivation for rspamd when using renamed bind socket options"; + } ]; + # Allow users to run 'rspamc' and 'rspamadm'. environment.systemPackages = [ pkgs.rspamd ]; @@ -128,17 +294,22 @@ in gid = config.ids.gids.rspamd; }; + environment.etc."rspamd.conf".source = rspamdConfFile; + systemd.services.rspamd = { description = "Rspamd Service"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + wantedBy = mkIf (!cfg.socketActivation) [ "multi-user.target" ]; + after = [ "network.target" ] ++ + (if cfg.socketActivation then allSocketNames else []); + requires = mkIf cfg.socketActivation allSocketNames; serviceConfig = { ExecStart = "${pkgs.rspamd}/bin/rspamd ${optionalString cfg.debug "-d"} --user=${cfg.user} --group=${cfg.group} --pid=/run/rspamd.pid -c ${rspamdConfFile} -f"; Restart = "always"; RuntimeDirectory = "rspamd"; PrivateTmp = true; + Sockets = mkIf cfg.socketActivation (concatStringsSep " " allSocketNames); }; preStart = '' @@ -146,5 +317,25 @@ in ${pkgs.coreutils}/bin/chown ${cfg.user}:${cfg.group} /var/lib/rspamd ''; }; + systemd.sockets = mkIf cfg.socketActivation + (listToAttrs (map (each: { + name = "rspamd-${each.name}-${toString each.index}"; + value = { + description = "Rspamd socket ${toString each.index} for worker ${each.name}"; + wantedBy = [ "sockets.target" ]; + listenStreams = (listenStreams each.value.socket); + socketConfig = { + BindIPv6Only = mkIf (isIPv6Socket each.value.socket) "ipv6-only"; + Service = "rspamd.service"; + SocketUser = mkIf (isUnixSocket each.value.socket) each.value.owner; + SocketGroup = mkIf (isUnixSocket each.value.socket) each.value.group; + SocketMode = mkIf (isUnixSocket each.value.socket) each.value.mode; + }; + }; + }) allMappedSockets)); }; + imports = [ + (mkRenamedOptionModule [ "services" "rspamd" "bindSocket" ] [ "services" "rspamd" "workers" "normal" "bindSockets" ]) + (mkRenamedOptionModule [ "services" "rspamd" "bindUISocket" ] [ "services" "rspamd" "workers" "controller" "bindSockets" ]) + ]; } diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix new file mode 100644 index 00000000000..cc60a143fa6 --- /dev/null +++ b/nixos/modules/services/misc/home-assistant.nix @@ -0,0 +1,135 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.home-assistant; + + configFile = pkgs.writeText "configuration.yaml" (builtins.toJSON cfg.config); + + availableComponents = pkgs.home-assistant.availableComponents; + + # Given component "parentConfig.platform", returns whether config.parentConfig + # is a list containing a set with set.platform == "platform". + # + # For example, the component sensor.luftdaten is used as follows: + # config.sensor = [ { + # platform = "luftdaten"; + # ... + # } ]; + useComponentPlatform = component: + let + path = splitString "." component; + parentConfig = attrByPath (init path) null cfg.config; + platform = last path; + in isList parentConfig && any + (item: item.platform or null == platform) + parentConfig; + + # Returns whether component is used in config + useComponent = component: + hasAttrByPath (splitString "." component) cfg.config + || useComponentPlatform component; + + # List of components used in config + extraComponents = filter useComponent availableComponents; + + package = if cfg.autoExtraComponents + then (cfg.package.override { inherit extraComponents; }) + else cfg.package; + +in { + meta.maintainers = with maintainers; [ dotlambda ]; + + options.services.home-assistant = { + enable = mkEnableOption "Home Assistant"; + + configDir = mkOption { + default = "/var/lib/hass"; + type = types.path; + description = "The config directory, where your <filename>configuration.yaml</filename> is located."; + }; + + config = mkOption { + default = null; + type = with types; nullOr attrs; + example = literalExample '' + { + homeassistant = { + name = "Home"; + time_zone = "UTC"; + }; + frontend = { }; + http = { }; + feedreader.urls = [ "https://nixos.org/blogs.xml" ]; + } + ''; + description = '' + Your <filename>configuration.yaml</filename> as a Nix attribute set. + Beware that setting this option will delete your previous <filename>configuration.yaml</filename>. + ''; + }; + + package = mkOption { + default = pkgs.home-assistant; + defaultText = "pkgs.home-assistant"; + type = types.package; + example = literalExample '' + pkgs.home-assistant.override { + extraPackages = ps: with ps; [ colorlog ]; + } + ''; + description = '' + Home Assistant package to use. + Override <literal>extraPackages</literal> in order to add additional dependencies. + ''; + }; + + autoExtraComponents = mkOption { + default = true; + type = types.bool; + description = '' + If set to <literal>true</literal>, the components used in <literal>config</literal> + are set as the specified package's <literal>extraComponents</literal>. + This in turn adds all packaged dependencies to the derivation. + You might still see import errors in your log. + In this case, you will need to package the necessary dependencies yourself + or ask for someone else to package them. + If a dependency is packaged but not automatically added to this list, + you might need to specify it in <literal>extraPackages</literal>. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.home-assistant = { + description = "Home Assistant"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + preStart = lib.optionalString (cfg.config != null) '' + rm -f ${cfg.configDir}/configuration.yaml + ln -s ${configFile} ${cfg.configDir}/configuration.yaml + ''; + serviceConfig = { + ExecStart = '' + ${package}/bin/hass --config "${cfg.configDir}" + ''; + User = "hass"; + Group = "hass"; + Restart = "on-failure"; + ProtectSystem = "strict"; + ReadWritePaths = "${cfg.configDir}"; + PrivateTmp = true; + }; + }; + + users.extraUsers.hass = { + home = cfg.configDir; + createHome = true; + group = "hass"; + uid = config.ids.uids.hass; + }; + + users.extraGroups.hass.gid = config.ids.gids.hass; + }; +} diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix index 80979547d33..7e880ad09b8 100644 --- a/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -4,6 +4,8 @@ with lib; let cfg = config.services.matrix-synapse; + pg = config.services.postgresql; + usePostgresql = cfg.database_type == "psycopg2"; 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)}]}''; @@ -38,7 +40,7 @@ database: { name: "${cfg.database_type}", args: { ${concatStringsSep ",\n " ( - mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args + mapAttrsToList (n: v: "\"${n}\": ${builtins.toJSON v}") cfg.database_args )} } } @@ -155,7 +157,7 @@ in { tls_certificate_path = mkOption { type = types.nullOr types.str; default = null; - example = "/var/lib/matrix-synapse/homeserver.tls.crt"; + example = "${cfg.dataDir}/homeserver.tls.crt"; description = '' PEM encoded X509 certificate for TLS. You can replace the self-signed certificate that synapse @@ -167,7 +169,7 @@ in { tls_private_key_path = mkOption { type = types.nullOr types.str; default = null; - example = "/var/lib/matrix-synapse/homeserver.tls.key"; + example = "${cfg.dataDir}/homeserver.tls.key"; description = '' PEM encoded private key for TLS. Specify null if synapse is not speaking TLS directly. @@ -176,7 +178,7 @@ in { tls_dh_params_path = mkOption { type = types.nullOr types.str; default = null; - example = "/var/lib/matrix-synapse/homeserver.tls.dh"; + example = "${cfg.dataDir}/homeserver.tls.dh"; description = '' PEM dh parameters for ephemeral keys ''; @@ -184,6 +186,7 @@ in { server_name = mkOption { type = types.str; example = "example.com"; + default = config.networking.hostName; description = '' The domain name of the server, with optional explicit port. This is used by remote servers to connect to this server, @@ -339,16 +342,39 @@ in { }; database_type = mkOption { type = types.enum [ "sqlite3" "psycopg2" ]; - default = "sqlite3"; + default = if versionAtLeast config.system.stateVersion "18.03" + then "psycopg2" + else "sqlite3"; description = '' The database engine name. Can be sqlite or psycopg2. ''; }; + create_local_database = mkOption { + type = types.bool; + default = true; + description = '' + Whether to create a local database automatically. + ''; + }; + database_name = mkOption { + type = types.str; + default = "matrix-synapse"; + description = "Database name."; + }; + database_user = mkOption { + type = types.str; + default = "matrix-synapse"; + description = "Database user name."; + }; database_args = mkOption { type = types.attrs; default = { - database = "${cfg.dataDir}/homeserver.db"; - }; + sqlite3 = { database = "${cfg.dataDir}/homeserver.db"; }; + psycopg2 = { + user = cfg.database_user; + database = cfg.database_name; + }; + }."${cfg.database_type}"; description = '' Arguments to pass to the engine. ''; @@ -623,15 +649,36 @@ in { gid = config.ids.gids.matrix-synapse; } ]; + services.postgresql.enable = mkIf usePostgresql (mkDefault true); + systemd.services.matrix-synapse = { description = "Synapse Matrix homeserver"; - after = [ "network.target" ]; + after = [ "network.target" "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; preStart = '' ${cfg.package}/bin/homeserver \ --config-path ${configFile} \ --keys-directory ${cfg.dataDir} \ --generate-keys + '' + optionalString (usePostgresql && cfg.create_local_database) '' + if ! test -e "${cfg.dataDir}/db-created"; then + ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ + ${pg.package}/bin/createuser \ + --login \ + --no-createdb \ + --no-createrole \ + --encrypted \ + ${cfg.database_user} + ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ + ${pg.package}/bin/createdb \ + --owner=${cfg.database_user} \ + --encoding=UTF8 \ + --lc-collate=C \ + --lc-ctype=C \ + --template=template0 \ + ${cfg.database_name} + touch "${cfg.dataDir}/db-created" + fi ''; serviceConfig = { Type = "simple"; diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index beca820d2d6..a169b0f2c78 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -8,7 +8,7 @@ let nix = cfg.package.out; - isNix112 = versionAtLeast (getVersion nix) "1.12pre"; + isNix20 = versionAtLeast (getVersion nix) "2.0pre"; makeNixBuildUser = nr: { name = "nixbld${toString nr}"; @@ -26,32 +26,40 @@ let nixConf = let - # If we're using sandbox for builds, then provide /bin/sh in - # the sandbox as a bind-mount to bash. This means we also need to - # include the entire closure of bash. + # In Nix < 2.0, If we're using sandbox for builds, then provide + # /bin/sh in the sandbox as a bind-mount to bash. This means we + # also need to include the entire closure of bash. Nix >= 2.0 + # provides a /bin/sh by default. sh = pkgs.stdenv.shell; binshDeps = pkgs.writeReferencesToFile sh; in - pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } '' - extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done) + pkgs.runCommand "nix.conf" { extraOptions = cfg.extraOptions; inherit binshDeps; } '' + ${optionalString (!isNix20) '' + extraPaths=$(for i in $(cat binshDeps); do if test -d $i; then echo $i; fi; done) + ''} cat > $out <<END # WARNING: this file is generated from the nix.* options in # your NixOS configuration, typically # /etc/nixos/configuration.nix. Do not edit it! build-users-group = nixbld - build-max-jobs = ${toString (cfg.maxJobs)} - build-cores = ${toString (cfg.buildCores)} - build-use-sandbox = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox} - build-sandbox-paths = ${toString cfg.sandboxPaths} /bin/sh=${sh} $(echo $extraPaths) - binary-caches = ${toString cfg.binaryCaches} - trusted-binary-caches = ${toString cfg.trustedBinaryCaches} - binary-cache-public-keys = ${toString cfg.binaryCachePublicKeys} + ${if isNix20 then "max-jobs" else "build-max-jobs"} = ${toString (cfg.maxJobs)} + ${if isNix20 then "cores" else "build-cores"} = ${toString (cfg.buildCores)} + ${if isNix20 then "sandbox" else "build-use-sandbox"} = ${if (builtins.isBool cfg.useSandbox) then boolToString cfg.useSandbox else cfg.useSandbox} + ${if isNix20 then "extra-sandbox-paths" else "build-sandbox-paths"} = ${toString cfg.sandboxPaths} ${optionalString (!isNix20) "/bin/sh=${sh} $(echo $extraPaths)"} + ${if isNix20 then "substituters" else "binary-caches"} = ${toString cfg.binaryCaches} + ${if isNix20 then "trusted-substituters" else "trusted-binary-caches"} = ${toString cfg.trustedBinaryCaches} + ${if isNix20 then "trusted-public-keys" else "binary-cache-public-keys"} = ${toString cfg.binaryCachePublicKeys} auto-optimise-store = ${boolToString cfg.autoOptimiseStore} - ${optionalString cfg.requireSignedBinaryCaches '' - signed-binary-caches = * + ${if isNix20 then '' + require-sigs = ${if cfg.requireSignedBinaryCaches then "true" else "false"} + '' else '' + signed-binary-caches = ${if cfg.requireSignedBinaryCaches then "*" else ""} ''} trusted-users = ${toString cfg.trustedUsers} allowed-users = ${toString cfg.allowedUsers} + ${optionalString (isNix20 && !cfg.distributedBuilds) '' + builders = + ''} $extraOptions END ''; @@ -377,8 +385,9 @@ in systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; systemd.services.nix-daemon = - { path = [ nix pkgs.openssl.bin pkgs.utillinux config.programs.ssh.package ] - ++ optionals cfg.distributedBuilds [ pkgs.gzip ]; + { path = [ nix pkgs.utillinux ] + ++ optionals cfg.distributedBuilds [ config.programs.ssh.package pkgs.gzip ] + ++ optionals (!isNix20) [ pkgs.openssl.bin ]; environment = cfg.envVars // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; } @@ -396,10 +405,9 @@ in }; nix.envVars = - { NIX_CONF_DIR = "/etc/nix"; - } + optionalAttrs (!isNix20) { + NIX_CONF_DIR = "/etc/nix"; - // optionalAttrs (!isNix112) { # Enable the copy-from-other-stores substituter, which allows # builds to be sped up by copying build results from remote # Nix stores. To do this, mount the remote file system on a @@ -407,12 +415,8 @@ in NIX_OTHER_STORES = "/run/nix/remote-stores/*/nix"; } - // optionalAttrs cfg.distributedBuilds { - NIX_BUILD_HOOK = - if isNix112 then - "${nix}/libexec/nix/build-remote" - else - "${nix}/libexec/nix/build-remote.pl"; + // optionalAttrs (cfg.distributedBuilds && !isNix20) { + NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote.pl"; }; # Set up the environment variables for running Nix. @@ -420,7 +424,7 @@ in { NIX_PATH = concatStringsSep ":" cfg.nixPath; }; - environment.extraInit = + environment.extraInit = optionalString (!isNix20) '' # Set up secure multi-user builds: non-root users build through the # Nix daemon. diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix index e1fde4fc950..d23b329eeb2 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/network-filesystems/openafs-client/default.nix b/nixos/modules/services/network-filesystems/openafs-client/default.nix deleted file mode 100644 index 0946e379e79..00000000000 --- a/nixos/modules/services/network-filesystems/openafs-client/default.nix +++ /dev/null @@ -1,99 +0,0 @@ -{ config, pkgs, lib, ... }: - -let - inherit (lib) mkOption mkIf; - - cfg = config.services.openafsClient; - - cellServDB = pkgs.fetchurl { - url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14; - sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc"; - }; - - afsConfig = pkgs.runCommand "afsconfig" {} '' - mkdir -p $out - echo ${cfg.cellName} > $out/ThisCell - cp ${cellServDB} $out/CellServDB - echo "/afs:${cfg.cacheDirectory}:${cfg.cacheSize}" > $out/cacheinfo - ''; - - openafsPkgs = config.boot.kernelPackages.openafsClient; -in -{ - ###### interface - - options = { - - services.openafsClient = { - - enable = mkOption { - default = false; - description = "Whether to enable the OpenAFS client."; - }; - - cellName = mkOption { - default = "grand.central.org"; - description = "Cell name."; - }; - - cacheSize = mkOption { - default = "100000"; - description = "Cache size."; - }; - - cacheDirectory = mkOption { - default = "/var/cache/openafs"; - description = "Cache directory."; - }; - - crypt = mkOption { - default = false; - description = "Whether to enable (weak) protocol encryption."; - }; - - sparse = mkOption { - default = false; - description = "Minimal cell list in /afs."; - }; - - }; - }; - - - ###### implementation - - config = mkIf cfg.enable { - - environment.systemPackages = [ openafsPkgs ]; - - environment.etc = [ - { source = afsConfig; - target = "openafs"; - } - ]; - - systemd.services.afsd = { - description = "AFS client"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - serviceConfig = { RemainAfterExit = true; }; - - preStart = '' - mkdir -p -m 0755 /afs - mkdir -m 0700 -p ${cfg.cacheDirectory} - ${pkgs.kmod}/bin/insmod ${openafsPkgs}/lib/openafs/libafs-*.ko || true - ${openafsPkgs}/sbin/afsd -confdir ${afsConfig} -cachedir ${cfg.cacheDirectory} ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} -fakestat -afsdb - ${openafsPkgs}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"} - ''; - - # Doing this in preStop, because after these commands AFS is basically - # stopped, so systemd has nothing to do, just noticing it. If done in - # postStop, then we get a hang + kernel oops, because AFS can't be - # stopped simply by sending signals to processes. - preStop = '' - ${pkgs.utillinux}/bin/umount /afs - ${openafsPkgs}/sbin/afsd -shutdown - ''; - }; - }; -} diff --git a/nixos/modules/services/network-filesystems/openafs/client.nix b/nixos/modules/services/network-filesystems/openafs/client.nix new file mode 100644 index 00000000000..3826fe3edfd --- /dev/null +++ b/nixos/modules/services/network-filesystems/openafs/client.nix @@ -0,0 +1,239 @@ +{ config, pkgs, lib, ... }: + +with import ./lib.nix { inherit lib; }; + +let + inherit (lib) getBin mkOption mkIf optionalString singleton types; + + cfg = config.services.openafsClient; + + cellServDB = pkgs.fetchurl { + url = http://dl.central.org/dl/cellservdb/CellServDB.2017-03-14; + sha256 = "1197z6c5xrijgf66rhaymnm5cvyg2yiy1i20y4ah4mrzmjx0m7sc"; + }; + + clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB); + + afsConfig = pkgs.runCommand "afsconfig" {} '' + mkdir -p $out + echo ${cfg.cellName} > $out/ThisCell + cat ${cellServDB} ${clientServDB} > $out/CellServDB + echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo + ''; + + openafsMod = config.boot.kernelPackages.openafs; + openafsBin = lib.getBin pkgs.openafs; +in +{ + ###### interface + + options = { + + services.openafsClient = { + + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to enable the OpenAFS client."; + }; + + afsdb = mkOption { + default = true; + type = types.bool; + description = "Resolve cells via AFSDB DNS records."; + }; + + cellName = mkOption { + default = ""; + type = types.str; + description = "Cell name."; + example = "grand.central.org"; + }; + + cellServDB = mkOption { + default = []; + type = with types; listOf (submodule { options = cellServDBConfig; }); + description = '' + This cell's database server records, added to the global + CellServDB. See CellServDB(5) man page for syntax. Ignored when + <literal>afsdb</literal> is set to <literal>true</literal>. + ''; + example = '' + [ { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; } + { ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; } + ] + ''; + }; + + cache = { + blocks = mkOption { + default = 100000; + type = types.int; + description = "Cache size in 1KB blocks."; + }; + + chunksize = mkOption { + default = 0; + type = types.ints.between 0 30; + description = '' + Size of each cache chunk given in powers of + 2. <literal>0</literal> resets the chunk size to its default + values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for + diskcache). Maximum value is 30. Important performance + parameter. Set to higher values when dealing with large files. + ''; + }; + + directory = mkOption { + default = "/var/cache/openafs"; + type = types.str; + description = "Cache directory."; + }; + + diskless = mkOption { + default = false; + type = types.bool; + description = '' + Use in-memory cache for diskless machines. Has no real + performance benefit anymore. + ''; + }; + }; + + crypt = mkOption { + default = true; + type = types.bool; + description = "Whether to enable (weak) protocol encryption."; + }; + + daemons = mkOption { + default = 2; + type = types.int; + description = '' + Number of daemons to serve user requests. Numbers higher than 6 + usually do no increase performance. Default is sufficient for up + to five concurrent users. + ''; + }; + + fakestat = mkOption { + default = false; + type = types.bool; + description = '' + Return fake data on stat() calls. If <literal>true</literal>, + always do so. If <literal>false</literal>, only do so for + cross-cell mounts (as these are potentially expensive). + ''; + }; + + inumcalc = mkOption { + default = "compat"; + type = types.strMatching "compat|md5"; + description = '' + Inode calculation method. <literal>compat</literal> is + computationally less expensive, but <literal>md5</literal> greatly + reduces the likelihood of inode collisions in larger scenarios + involving multiple cells mounted into one AFS space. + ''; + }; + + mountPoint = mkOption { + default = "/afs"; + type = types.str; + description = '' + Mountpoint of the AFS file tree, conventionally + <literal>/afs</literal>. When set to a different value, only + cross-cells that use the same value can be accessed. + ''; + }; + + sparse = mkOption { + default = true; + type = types.bool; + description = "Minimal cell list in /afs."; + }; + + startDisconnected = mkOption { + default = false; + type = types.bool; + description = '' + Start up in disconnected mode. You need to execute + <literal>fs disco online</literal> (as root) to switch to + connected mode. Useful for roaming devices. + ''; + }; + + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [ + { assertion = cfg.afsdb || cfg.cellServDB != []; + message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb."; + } + { assertion = cfg.cellName != ""; + message = "You must specify the local cell name in config.services.openafsClient.cellName."; + } + ]; + + environment.systemPackages = [ pkgs.openafs ]; + + environment.etc = { + clientCellServDB = { + source = pkgs.runCommand "CellServDB" {} '' + cat ${cellServDB} ${clientServDB} > $out + ''; + target = "openafs/CellServDB"; + mode = "0644"; + }; + clientCell = { + text = '' + ${cfg.cellName} + ''; + target = "openafs/ThisCell"; + mode = "0644"; + }; + }; + + systemd.services.afsd = { + description = "AFS client"; + wantedBy = [ "multi-user.target" ]; + after = singleton (if cfg.startDisconnected then "network.target" else "network-online.target"); + serviceConfig = { RemainAfterExit = true; }; + restartIfChanged = false; + + preStart = '' + mkdir -p -m 0755 ${cfg.mountPoint} + mkdir -m 0700 -p ${cfg.cache.directory} + ${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz + ${openafsBin}/sbin/afsd \ + -mountdir ${cfg.mountPoint} \ + -confdir ${afsConfig} \ + ${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \ + -blocks ${toString cfg.cache.blocks} \ + -chunksize ${toString cfg.cache.chunksize} \ + ${optionalString cfg.cache.diskless "-memcache"} \ + -inumcalc ${cfg.inumcalc} \ + ${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \ + ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \ + ${optionalString cfg.afsdb "-afsdb"} + ${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"} + ${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"} + ''; + + # Doing this in preStop, because after these commands AFS is basically + # stopped, so systemd has nothing to do, just noticing it. If done in + # postStop, then we get a hang + kernel oops, because AFS can't be + # stopped simply by sending signals to processes. + preStop = '' + ${pkgs.utillinux}/bin/umount ${cfg.mountPoint} + ${openafsBin}/sbin/afsd -shutdown + ${pkgs.kmod}/sbin/rmmod libafs + ''; + }; + }; +} diff --git a/nixos/modules/services/network-filesystems/openafs/lib.nix b/nixos/modules/services/network-filesystems/openafs/lib.nix new file mode 100644 index 00000000000..ecfc72d2eaf --- /dev/null +++ b/nixos/modules/services/network-filesystems/openafs/lib.nix @@ -0,0 +1,28 @@ +{ lib, ...}: + +let + inherit (lib) concatStringsSep mkOption types; + +in rec { + + mkCellServDB = cellName: db: '' + >${cellName} + '' + (concatStringsSep "\n" (map (dbm: if (dbm.ip != "" && dbm.dnsname != "") then dbm.ip + " #" + dbm.dnsname else "") + db)); + + # CellServDB configuration type + cellServDBConfig = { + ip = mkOption { + type = types.str; + default = ""; + example = "1.2.3.4"; + description = "IP Address of a database server"; + }; + dnsname = mkOption { + type = types.str; + default = ""; + example = "afs.example.org"; + description = "DNS full-qualified domain name of a database server"; + }; + }; +} diff --git a/nixos/modules/services/network-filesystems/openafs/server.nix b/nixos/modules/services/network-filesystems/openafs/server.nix new file mode 100644 index 00000000000..429eb945ac9 --- /dev/null +++ b/nixos/modules/services/network-filesystems/openafs/server.nix @@ -0,0 +1,260 @@ +{ config, pkgs, lib, ... }: + +with import ./lib.nix { inherit lib; }; + +let + inherit (lib) concatStringsSep intersperse mapAttrsToList mkForce mkIf mkMerge mkOption optionalString types; + + bosConfig = pkgs.writeText "BosConfig" ('' + restrictmode 1 + restarttime 16 0 0 0 0 + checkbintime 3 0 5 0 0 + '' + (optionalString cfg.roles.database.enable '' + bnode simple vlserver 1 + parm ${openafsBin}/libexec/openafs/vlserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.vlserverArgs} + end + bnode simple ptserver 1 + parm ${openafsBin}/libexec/openafs/ptserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} ${cfg.roles.database.ptserverArgs} + end + '') + (optionalString cfg.roles.fileserver.enable '' + bnode dafs dafs 1 + parm ${openafsBin}/libexec/openafs/dafileserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.fileserverArgs} + parm ${openafsBin}/libexec/openafs/davolserver ${optionalString cfg.dottedPrincipals "-allow-dotted-principals"} -udpsize ${udpSizeStr} ${cfg.roles.fileserver.volserverArgs} + parm ${openafsBin}/libexec/openafs/salvageserver ${cfg.roles.fileserver.salvageserverArgs} + parm ${openafsBin}/libexec/openafs/dasalvager ${cfg.roles.fileserver.salvagerArgs} + end + '') + (optionalString (cfg.roles.database.enable && cfg.roles.backup.enable) '' + bnode simple buserver 1 + parm ${openafsBin}/libexec/openafs/buserver ${cfg.roles.backup.buserverArgs} ${optionalString (cfg.roles.backup.cellServDB != []) "-cellservdb /etc/openafs/backup/"} + end + '')); + + netInfo = if (cfg.advertisedAddresses != []) then + pkgs.writeText "NetInfo" ((concatStringsSep "\nf " cfg.advertisedAddresses) + "\n") + else null; + + buCellServDB = pkgs.writeText "backup-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.roles.backup.cellServDB); + + cfg = config.services.openafsServer; + + udpSizeStr = toString cfg.udpPacketSize; + + openafsBin = lib.getBin pkgs.openafs; + +in { + + options = { + + services.openafsServer = { + + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the OpenAFS server. An OpenAFS server needs a + complex setup. So, be aware that enabling this service and setting + some options does not give you a turn-key-ready solution. You need + at least a running Kerberos 5 setup, as OpenAFS relies on it for + authentication. See the Guide "QuickStartUnix" coming with + <literal>pkgs.openafs.doc</literal> for complete setup + instructions. + ''; + }; + + advertisedAddresses = mkOption { + default = []; + description = "List of IP addresses this server is advertised under. See NetInfo(5)"; + }; + + cellName = mkOption { + default = ""; + type = types.str; + description = "Cell name, this server will serve."; + example = "grand.central.org"; + }; + + cellServDB = mkOption { + default = []; + type = with types; listOf (submodule [ { options = cellServDBConfig;} ]); + description = "Definition of all cell-local database server machines."; + }; + + roles = { + fileserver = { + enable = mkOption { + default = true; + type = types.bool; + description = "Fileserver role, serves files and volumes from its local storage."; + }; + + fileserverArgs = mkOption { + default = "-vattachpar 128 -vhashsize 11 -L -rxpck 400 -cb 1000000"; + type = types.str; + description = "Arguments to the dafileserver process. See its man page."; + }; + + volserverArgs = mkOption { + default = ""; + type = types.str; + description = "Arguments to the davolserver process. See its man page."; + example = "-sync never"; + }; + + salvageserverArgs = mkOption { + default = ""; + type = types.str; + description = "Arguments to the salvageserver process. See its man page."; + example = "-showlog"; + }; + + salvagerArgs = mkOption { + default = ""; + type = types.str; + description = "Arguments to the dasalvager process. See its man page."; + example = "-showlog -showmounts"; + }; + }; + + database = { + enable = mkOption { + default = true; + type = types.bool; + description = '' + Database server role, maintains the Volume Location Database, + Protection Database (and Backup Database, see + <literal>backup</literal> role). There can be multiple + servers in the database role for replication, which then need + reliable network connection to each other. + + Servers in this role appear in AFSDB DNS records or the + CellServDB. + ''; + }; + + vlserverArgs = mkOption { + default = ""; + type = types.str; + description = "Arguments to the vlserver process. See its man page."; + example = "-rxbind"; + }; + + ptserverArgs = mkOption { + default = ""; + type = types.str; + description = "Arguments to the ptserver process. See its man page."; + example = "-restricted -default_access S---- S-M---"; + }; + }; + + backup = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Backup server role. Use in conjunction with the + <literal>database</literal> role to maintain the Backup + Database. Normally only used in conjunction with tape storage + or IBM's Tivoli Storage Manager. + ''; + }; + + buserverArgs = mkOption { + default = ""; + type = types.str; + description = "Arguments to the buserver process. See its man page."; + example = "-p 8"; + }; + + cellServDB = mkOption { + default = []; + type = with types; listOf (submodule [ { options = cellServDBConfig;} ]); + description = '' + Definition of all cell-local backup database server machines. + Use this when your cell uses less backup database servers than + other database server machines. + ''; + }; + }; + }; + + dottedPrincipals= mkOption { + default = false; + type = types.bool; + description = '' + If enabled, allow principal names containing (.) dots. Enabling + this has security implications! + ''; + }; + + udpPacketSize = mkOption { + default = 1310720; + type = types.int; + description = '' + UDP packet size to use in Bytes. Higher values can speed up + communications. The default of 1 MB is a sufficient in most + cases. Make sure to increase the kernel's UDP buffer size + accordingly via <literal>net.core(w|r|opt)mem_max</literal> + sysctl. + ''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + assertions = [ + { assertion = cfg.cellServDB != []; + message = "You must specify all cell-local database servers in config.services.openafsServer.cellServDB."; + } + { assertion = cfg.cellName != ""; + message = "You must specify the local cell name in config.services.openafsServer.cellName."; + } + ]; + + environment.systemPackages = [ pkgs.openafs ]; + + environment.etc = { + bosConfig = { + source = bosConfig; + target = "openafs/BosConfig"; + mode = "0644"; + }; + cellServDB = { + text = mkCellServDB cfg.cellName cfg.cellServDB; + target = "openafs/server/CellServDB"; + mode = "0644"; + }; + thisCell = { + text = cfg.cellName; + target = "openafs/server/ThisCell"; + mode = "0644"; + }; + buCellServDB = { + enable = (cfg.roles.backup.cellServDB != []); + text = mkCellServDB cfg.cellName cfg.roles.backup.cellServDB; + target = "openafs/backup/CellServDB"; + }; + }; + + systemd.services = { + openafs-server = { + description = "OpenAFS server"; + after = [ "syslog.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + restartIfChanged = false; + unitConfig.ConditionPathExists = [ "/etc/openafs/server/rxkad.keytab" ]; + preStart = '' + mkdir -m 0755 -p /var/openafs + ${optionalString (netInfo != null) "cp ${netInfo} /var/openafs/netInfo"} + ${optionalString (cfg.roles.backup.cellServDB != []) "cp ${buCellServDB}"} + ''; + serviceConfig = { + ExecStart = "${openafsBin}/bin/bosserver -nofork"; + ExecStop = "${openafsBin}/bin/bos shutdown localhost -wait -localauth"; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix index 09cd9cb22ca..b23266e8d43 100644 --- a/nixos/modules/services/network-filesystems/samba.nix +++ b/nixos/modules/services/network-filesystems/samba.nix @@ -54,10 +54,12 @@ let }; serviceConfig = { - ExecStart = "${samba}/sbin/${appName} ${args}"; + ExecStart = "${samba}/sbin/${appName} --foreground --no-process-group ${args}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; LimitNOFILE = 16384; + PIDFile = "/run/${appName}.pid"; Type = "notify"; + NotifyAccess = "all"; #may not do anything... }; restartTriggers = [ configFile ]; @@ -231,11 +233,12 @@ in after = [ "samba-setup.service" "network.target" ]; wantedBy = [ "multi-user.target" ]; }; - + # Refer to https://github.com/samba-team/samba/tree/master/packaging/systemd + # for correct use with systemd services = { - "samba-smbd" = daemonService "smbd" "-F"; - "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" "-F"); - "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" "-F"); + "samba-smbd" = daemonService "smbd" ""; + "samba-nmbd" = mkIf cfg.enableNmbd (daemonService "nmbd" ""); + "samba-winbindd" = mkIf cfg.enableWinbindd (daemonService "winbindd" ""); "samba-setup" = { description = "Samba Setup Task"; script = setupScript; diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix index ad4ac9bf45e..df9c92db2e5 100644 --- a/nixos/modules/services/networking/aria2.nix +++ b/nixos/modules/services/networking/aria2.nix @@ -10,9 +10,9 @@ let settingsDir = "${homeDir}"; sessionFile = "${homeDir}/aria2.session"; downloadDir = "${homeDir}/Downloads"; - + rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to); - + settingsFile = pkgs.writeText "aria2.conf" '' dir=${cfg.downloadDir} @@ -110,12 +110,12 @@ in mkdir -m 0770 -p "${homeDir}" chown aria2:aria2 "${homeDir}" if [[ ! -d "${config.services.aria2.downloadDir}" ]] - then + then mkdir -m 0770 -p "${config.services.aria2.downloadDir}" chown aria2:aria2 "${config.services.aria2.downloadDir}" fi if [[ ! -e "${sessionFile}" ]] - then + then touch "${sessionFile}" chown aria2:aria2 "${sessionFile}" fi @@ -132,4 +132,4 @@ in }; }; }; -} \ No newline at end of file +} diff --git a/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixos/modules/services/networking/dnscrypt-wrapper.nix index 23cc92946e4..bf13d5c6f5f 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 011a9b2f58e..d0c19c4ecb7 100644 --- a/nixos/modules/services/networking/kresd.nix +++ b/nixos/modules/services/networking/kresd.nix @@ -43,7 +43,7 @@ in type = with types; listOf str; default = [ "::1" "127.0.0.1" ]; description = '' - What addresses the server should listen on. + What addresses the server should listen on. (UDP+TCP 53) ''; }; # TODO: perhaps options for more common stuff like cache size or forwarding @@ -99,9 +99,9 @@ in Restart = "on-failure"; }; + # Trust anchor goes from dns-root-data by default. script = '' - exec '${package}/bin/kresd' --config '${configFile}' \ - -k '${pkgs.dns-root-data}/root.key' + exec '${package}/bin/kresd' --config '${configFile}' --forks=1 ''; requires = [ "kresd.socket" ]; diff --git a/nixos/modules/services/networking/monero.nix b/nixos/modules/services/networking/monero.nix new file mode 100644 index 00000000000..31379189f5d --- /dev/null +++ b/nixos/modules/services/networking/monero.nix @@ -0,0 +1,238 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.monero; + dataDir = "/var/lib/monero"; + + listToConf = option: list: + concatMapStrings (value: "${option}=${value}\n") list; + + login = (cfg.rpc.user != null && cfg.rpc.password != null); + + configFile = with cfg; pkgs.writeText "monero.conf" '' + log-file=/dev/stdout + data-dir=${dataDir} + + ${optionalString mining.enable '' + start-mining=${mining.address} + mining-threads=${toString mining.threads} + ''} + + rpc-bind-ip=${rpc.address} + rpc-bind-port=${toString rpc.port} + ${optionalString login '' + rpc-login=${rpc.user}:${rpc.password} + ''} + ${optionalString rpc.restricted '' + restrict-rpc=1 + ''} + + limit-rate-up=${toString limits.upload} + limit-rate-down=${toString limits.download} + max-concurrency=${toString limits.threads} + block-sync-size=${toString limits.syncSize} + + ${listToConf "add-peer" extraNodes} + ${listToConf "add-priority-node" priorityNodes} + ${listToConf "add-exclusive-node" exclusiveNodes} + + ${extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.monero = { + + enable = mkEnableOption "Monero node daemon."; + + mining.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to mine moneroj. + ''; + }; + + mining.address = mkOption { + type = types.str; + default = ""; + description = '' + Monero address where to send mining rewards. + ''; + }; + + mining.threads = mkOption { + type = types.addCheck types.int (x: x>=0); + default = 0; + description = '' + Number of threads used for mining. + Set to <literal>0</literal> to use all available. + ''; + }; + + rpc.user = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + User name for RPC connections. + ''; + }; + + rpc.password = mkOption { + type = types.str; + default = null; + description = '' + Password for RPC connections. + ''; + }; + + rpc.address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + IP address the RPC server will bind to. + ''; + }; + + rpc.port = mkOption { + type = types.int; + default = 18081; + description = '' + Port the RPC server will bind to. + ''; + }; + + rpc.restricted = mkOption { + type = types.bool; + default = false; + description = '' + Whether to restrict RPC to view only commands. + ''; + }; + + limits.upload = mkOption { + type = types.addCheck types.int (x: x>=-1); + default = -1; + description = '' + Limit of the upload rate in kB/s. + Set to <literal>-1</literal> to leave unlimited. + ''; + }; + + limits.download = mkOption { + type = types.addCheck types.int (x: x>=-1); + default = -1; + description = '' + Limit of the download rate in kB/s. + Set to <literal>-1</literal> to leave unlimited. + ''; + }; + + limits.threads = mkOption { + type = types.addCheck types.int (x: x>=0); + default = 0; + description = '' + Maximum number of threads used for a parallel job. + Set to <literal>0</literal> to leave unlimited. + ''; + }; + + limits.syncSize = mkOption { + type = types.addCheck types.int (x: x>=0); + default = 0; + description = '' + Maximum number of blocks to sync at once. + Set to <literal>0</literal> for adaptive. + ''; + }; + + extraNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of additional peer IP addresses to add to the local list. + ''; + }; + + priorityNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of peer IP addresses to connect to and + attempt to keep the connection open. + ''; + }; + + exclusiveNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of peer IP addresses to connect to *only*. + If given the other peer options will be ignored. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to monerod configuration. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.extraUsers = singleton { + name = "monero"; + uid = config.ids.uids.monero; + description = "Monero daemon user"; + home = dataDir; + createHome = true; + }; + + users.extraGroups = singleton { + name = "monero"; + gid = config.ids.gids.monero; + }; + + systemd.services.monero = { + description = "monero daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "monero"; + Group = "monero"; + ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive"; + Restart = "always"; + SuccessExitStatus = [ 0 1 ]; + }; + }; + + assertions = singleton { + assertion = cfg.mining.enable -> cfg.mining.address != ""; + message = '' + You need a Monero address to receive mining rewards: + specify one using option monero.mining.address. + ''; + }; + + }; + +} + diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix index 81915b5a2ef..273ca797b98 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 3fbf5a9f022..7a96b673c51 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/resilio.nix b/nixos/modules/services/networking/resilio.nix index 6d2b7bdbca1..d1c4101f80b 100644 --- a/nixos/modules/services/networking/resilio.nix +++ b/nixos/modules/services/networking/resilio.nix @@ -17,7 +17,7 @@ let search_lan = entry.searchLAN; use_sync_trash = entry.useSyncTrash; - known_hosts = knownHosts; + known_hosts = entry.knownHosts; }) cfg.sharedFolders; configFile = pkgs.writeText "config.json" (builtins.toJSON ({ diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index aa9c0fa1c09..e50c4dbacf3 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; @@ -367,9 +375,6 @@ in # LogLevel VERBOSE logs user's key fingerprint on login. # Needed to have a clear audit track of which key was used to log in. LogLevel VERBOSE - - # Use kernel sandbox mechanisms where possible in unprivileged processes. - UsePrivilegeSeparation sandbox ''; assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true; diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix new file mode 100644 index 00000000000..89a14966eca --- /dev/null +++ b/nixos/modules/services/networking/stunnel.nix @@ -0,0 +1,221 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.stunnel; + yesNo = val: if val then "yes" else "no"; + + verifyChainPathAssert = n: c: { + assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer); + message = "stunnel: \"${n}\" client configuration - hostname verification " + + "is not possible without either verifyChain or verifyPeer enabled"; + }; + + serverConfig = { + options = { + accept = mkOption { + type = types.int; + description = "On which port stunnel should listen for incoming TLS connections."; + }; + + connect = mkOption { + type = types.int; + description = "To which port the decrypted connection should be forwarded."; + }; + + cert = mkOption { + type = types.path; + description = "File containing both the private and public keys."; + }; + }; + }; + + clientConfig = { + options = { + accept = mkOption { + type = types.string; + description = "IP:Port on which connections should be accepted."; + }; + + connect = mkOption { + type = types.string; + description = "IP:Port destination to connect to."; + }; + + verifyChain = mkOption { + type = types.bool; + default = true; + description = "Check if the provided certificate has a valid certificate chain (against CAPath)."; + }; + + verifyPeer = mkOption { + type = types.bool; + default = false; + description = "Check if the provided certificate is contained in CAPath."; + }; + + CAPath = mkOption { + type = types.path; + default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; + description = "Path to a file containing certificates to validate against."; + }; + + verifyHostname = mkOption { + type = with types; nullOr string; + default = null; + description = "If set, stunnel checks if the provided certificate is valid for the given hostname."; + }; + }; + }; + + +in + +{ + + ###### interface + + options = { + + services.stunnel = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the stunnel TLS tunneling service."; + }; + + user = mkOption { + type = with types; nullOr string; + default = "nobody"; + description = "The user under which stunnel runs."; + }; + + group = mkOption { + type = with types; nullOr string; + default = "nogroup"; + description = "The group under which stunnel runs."; + }; + + logLevel = mkOption { + type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ]; + default = "info"; + description = "Verbosity of stunnel output."; + }; + + fipsMode = mkOption { + type = types.bool; + default = false; + description = "Enable FIPS 140-2 mode required for compliance."; + }; + + enableInsecureSSLv3 = mkOption { + type = types.bool; + default = false; + description = "Enable support for the insecure SSLv3 protocol."; + }; + + + servers = mkOption { + description = "Define the server configuations."; + type = with types; attrsOf (submodule serverConfig); + example = { + fancyWebserver = { + enable = true; + accept = 443; + connect = 8080; + cert = "/path/to/pem/file"; + }; + }; + default = { }; + }; + + clients = mkOption { + description = "Define the client configurations."; + type = with types; attrsOf (submodule clientConfig); + example = { + foobar = { + accept = "0.0.0.0:8080"; + connect = "nixos.org:443"; + verifyChain = false; + }; + }; + default = { }; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = concatLists [ + (singleton { + assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0); + message = "stunnel: At least one server- or client-configuration has to be present."; + }) + + (mapAttrsToList verifyChainPathAssert cfg.clients) + ]; + + environment.systemPackages = [ pkgs.stunnel ]; + + environment.etc."stunnel.cfg".text = '' + ${ if cfg.user != null then "setuid = ${cfg.user}" else "" } + ${ if cfg.group != null then "setgid = ${cfg.group}" else "" } + + debug = ${cfg.logLevel} + + ${ optionalString cfg.fipsMode "fips = yes" } + ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" } + + ; ----- SERVER CONFIGURATIONS ----- + ${ lib.concatStringsSep "\n" + (lib.mapAttrsToList + (n: v: '' + [${n}] + accept = ${toString v.accept} + connect = ${toString v.connect} + cert = ${v.cert} + + '') + cfg.servers) + } + + ; ----- CLIENT CONFIGURATIONS ----- + ${ lib.concatStringsSep "\n" + (lib.mapAttrsToList + (n: v: '' + [${n}] + client = yes + accept = ${v.accept} + connect = ${v.connect} + verifyChain = ${yesNo v.verifyChain} + verifyPeer = ${yesNo v.verifyPeer} + ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"} + ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"} + OCSPaia = yes + + '') + cfg.clients) + } + ''; + + systemd.services.stunnel = { + description = "stunnel TLS tunneling service"; + after = [ "network.target" ]; + wants = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ config.environment.etc."stunnel.cfg".source ]; + serviceConfig = { + ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}"; + Type = "forking"; + }; + }; + + }; + +} diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix index c51dd5d9465..adef500b7b5 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/security/tor.nix b/nixos/modules/services/security/tor.nix index fa4aeb22ae9..fed91756e76 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -88,6 +88,9 @@ let ${flip concatMapStrings v.map (p: '' HiddenServicePort ${toString p.port} ${p.destination} '')} + ${optionalString (v.authorizeClient != null) '' + HiddenServiceAuthorizeClient ${v.authorizeClient.authType} ${concatStringsSep "," v.authorizeClient.clientNames} + ''} '')) + cfg.extraConfig; @@ -619,6 +622,33 @@ in })); }; + authorizeClient = mkOption { + default = null; + description = "If configured, the hidden service is accessible for authorized clients only."; + type = types.nullOr (types.submodule ({config, ...}: { + + options = { + + authType = mkOption { + type = types.enum [ "basic" "stealth" ]; + description = '' + Either <literal>"basic"</literal> for a general-purpose authorization protocol + or <literal>"stealth"</literal> for a less scalable protocol + that also hides service activity from unauthorized clients. + ''; + }; + + clientNames = mkOption { + type = types.nonEmptyListOf (types.strMatching "[A-Za-z0-9+-_]+"); + description = '' + Only clients that are listed here are authorized to access the hidden service. + Generated authorization data can be found in <filename>${torDirectory}/onion/$name/hostname</filename>. + Clients need to put this authorization data in their configuration file using <literal>HidServAuth</literal>. + ''; + }; + }; + })); + }; }; config = { diff --git a/nixos/modules/services/web-servers/mighttpd2.nix b/nixos/modules/services/web-servers/mighttpd2.nix new file mode 100644 index 00000000000..a888f623616 --- /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/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 2951e63e863..100fabf902f 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -15,6 +15,9 @@ let } // (optionalAttrs vhostConfig.enableACME { sslCertificate = "/var/lib/acme/${serverName}/fullchain.pem"; sslCertificateKey = "/var/lib/acme/${serverName}/key.pem"; + }) // (optionalAttrs (vhostConfig.useACMEHost != null) { + sslCertificate = "/var/lib/acme/${vhostConfig.useACMEHost}/fullchain.pem"; + sslCertificateKey = "/var/lib/acme/${vhostConfig.useACMEHost}/key.pem"; }) ) cfg.virtualHosts; enableIPv6 = config.networking.enableIPv6; @@ -174,7 +177,7 @@ let redirectListen = filter (x: !x.ssl) defaultListen; - acmeLocation = '' + acmeLocation = optionalString (vhost.enableACME || vhost.useACMEHost != null) '' location /.well-known/acme-challenge { ${optionalString (vhost.acmeFallbackHost != null) "try_files $uri @acme-fallback;"} root ${vhost.acmeRoot}; @@ -194,7 +197,7 @@ let ${concatMapStringsSep "\n" listenString redirectListen} server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases}; - ${optionalString vhost.enableACME acmeLocation} + ${acmeLocation} location / { return 301 https://$host$request_uri; } @@ -204,7 +207,7 @@ let server { ${concatMapStringsSep "\n" listenString hostListen} server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases}; - ${optionalString vhost.enableACME acmeLocation} + ${acmeLocation} ${optionalString (vhost.root != null) "root ${vhost.root};"} ${optionalString (vhost.globalRedirect != null) '' return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; @@ -555,6 +558,14 @@ in are mutually exclusive. ''; } + + { + assertion = all (conf: !(conf.enableACME && conf.useACMEHost != null)) (attrValues virtualHosts); + message = '' + Options services.nginx.service.virtualHosts.<name>.enableACME and + services.nginx.virtualHosts.<name>.useACMEHost are mutually exclusive. + ''; + } ]; systemd.services.nginx = { @@ -580,7 +591,7 @@ in security.acme.certs = filterAttrs (n: v: v != {}) ( let vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts; - acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME) vhostsConfigs; + acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME && vhostConfig.useACMEHost == null) vhostsConfigs; acmePairs = map (vhostConfig: { name = vhostConfig.serverName; value = { user = cfg.user; group = lib.mkDefault cfg.group; diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix index 29f08cc4f30..bf18108a1a3 100644 --- a/nixos/modules/services/web-servers/nginx/vhost-options.nix +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -48,7 +48,21 @@ with lib; enableACME = mkOption { type = types.bool; default = false; - description = "Whether to ask Let's Encrypt to sign a certificate for this vhost."; + description = '' + Whether to ask Let's Encrypt to sign a certificate for this vhost. + Alternately, you can use an existing certificate through <option>useACMEHost</option>. + ''; + }; + + useACMEHost = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + A host of an existing Let's Encrypt certificate to use. + This is useful if you have many subdomains and want to avoid hitting the + <link xlink:href="https://letsencrypt.org/docs/rate-limits/">rate limit</link>. + Alternately, you can generate a certificate through <option>enableACME</option>. + ''; }; acmeRoot = mkOption { diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix index 4ede4fc2096..b6c7fef21fb 100644 --- a/nixos/modules/services/web-servers/traefik.nix +++ b/nixos/modules/services/web-servers/traefik.nix @@ -64,6 +64,16 @@ in { ''; }; + group = mkOption { + default = "traefik"; + type = types.string; + example = "docker"; + description = '' + Set the group that traefik runs under. + For the docker backend this needs to be set to <literal>docker</literal> instead. + ''; + }; + package = mkOption { default = pkgs.traefik; defaultText = "pkgs.traefik"; @@ -87,7 +97,7 @@ in { ]; Type = "simple"; User = "traefik"; - Group = "traefik"; + Group = cfg.group; Restart = "on-failure"; StartLimitInterval = 86400; StartLimitBurst = 5; diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix index 17a2cde3a65..4c76ce0bb19 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -47,6 +47,18 @@ in ${getBin config.hardware.pulseaudio.package}/bin/pactl load-module module-device-manager "do_routing=1" ''} + if [ -f "$HOME/.config/kdeglobals" ] + then + # Remove extraneous font style names. + # See also: https://phabricator.kde.org/D9070 + ${getBin pkgs.gnused}/bin/sed -i "$HOME/.config/kdeglobals" \ + -e '/^fixed=/ s/,Regular$//' \ + -e '/^font=/ s/,Regular$//' \ + -e '/^menuFont=/ s/,Regular$//' \ + -e '/^smallestReadableFont=/ s/,Regular$//' \ + -e '/^toolBarFont=/ s/,Regular$//' + fi + exec "${getBin plasma5.plasma-workspace}/bin/startkde" ''; }; diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix index 9d5d03638e0..c0c9d7ea47f 100644 --- a/nixos/modules/services/x11/desktop-managers/xfce.nix +++ b/nixos/modules/services/x11/desktop-managers/xfce.nix @@ -3,9 +3,7 @@ with lib; let - xcfg = config.services.xserver; - pcfg = config.hardware.pulseaudio; - cfg = xcfg.desktopManager.xfce; + cfg = config.services.xserver.desktopManager.xfce; in { @@ -52,82 +50,93 @@ in description = "Application used by XFCE to lock the screen."; }; }; - }; + config = mkIf cfg.enable { + environment.systemPackages = with pkgs.xfce // pkgs; [ + # Get GTK+ themes and gtk-update-icon-cache + gtk2.out + + # Supplies some abstract icons such as: + # utilities-terminal, accessories-text-editor + gnome3.defaultIconTheme + + hicolor_icon_theme + tango-icon-theme + xfce4-icon-theme + + desktop_file_utils + shared_mime_info + + # Needed by Xfce's xinitrc script + # TODO: replace with command -v + which + + exo + garcon + gtk-xfce-engine + gvfs + libxfce4ui + tumbler + xfconf + + mousepad + ristretto + xfce4-appfinder + xfce4-screenshooter + xfce4-session + xfce4-settings + xfce4-terminal + + (thunar.override { thunarPlugins = cfg.thunarPlugins; }) + thunar-volman # TODO: drop + ] ++ (if config.hardware.pulseaudio.enable + then [ xfce4-mixer-pulse xfce4-volumed-pulse ] + else [ xfce4-mixer xfce4-volumed ]) + # TODO: NetworkManager doesn't belong here + ++ optionals config.networking.networkmanager.enable [ networkmanagerapplet ] + ++ optionals config.powerManagement.enable [ xfce4-power-manager ] + ++ optionals cfg.enableXfwm [ xfwm4 ] + ++ optionals (!cfg.noDesktop) [ + xfce4-panel + xfce4-notifyd + xfdesktop + ]; + + environment.pathsToLink = [ + "/share/xfce4" + "/share/themes" + "/share/mime" + "/share/desktop-directories" + "/share/gtksourceview-2.0" + ]; + + environment.variables = { + GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; + GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ]; + }; - config = mkIf (xcfg.enable && cfg.enable) { - - services.xserver.desktopManager.session = singleton - { name = "xfce"; - bgSupport = true; - start = - '' - ${cfg.extraSessionCommands} + services.xserver.desktopManager.session = [{ + name = "xfce"; + bgSupport = true; + start = '' + ${cfg.extraSessionCommands} - # Set GTK_PATH so that GTK+ can find the theme engines. - export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0" + # Set GTK_PATH so that GTK+ can find the theme engines. + export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0" - # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes. - export GTK_DATA_PREFIX=${config.system.path} + # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes. + export GTK_DATA_PREFIX=${config.system.path} - ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} & - waitPID=$! - ''; - }; + ${pkgs.stdenv.shell} ${pkgs.xfce.xinitrc} & + waitPID=$! + ''; + }]; services.xserver.updateDbusEnvironment = true; - environment.systemPackages = - [ pkgs.gtk2.out # To get GTK+'s themes and gtk-update-icon-cache - pkgs.hicolor_icon_theme - pkgs.tango-icon-theme - pkgs.shared_mime_info - pkgs.which # Needed by the xfce's xinitrc script. - pkgs."${cfg.screenLock}" - pkgs.xfce.exo - pkgs.xfce.gtk_xfce_engine - pkgs.xfce.mousepad - pkgs.xfce.ristretto - pkgs.xfce.terminal - (pkgs.xfce.thunar.override { thunarPlugins = cfg.thunarPlugins; }) - pkgs.xfce.xfce4icontheme - pkgs.xfce.xfce4session - pkgs.xfce.xfce4settings - (if pcfg.enable then pkgs.xfce.xfce4mixer_pulse else pkgs.xfce.xfce4mixer) - (if pcfg.enable then pkgs.xfce.xfce4volumed_pulse else pkgs.xfce.xfce4volumed) - pkgs.xfce.xfce4-screenshooter - pkgs.xfce.xfconf - # This supplies some "abstract" icons such as - # "utilities-terminal" and "accessories-text-editor". - pkgs.gnome3.defaultIconTheme - pkgs.desktop_file_utils - pkgs.xfce.libxfce4ui - pkgs.xfce.garcon - pkgs.xfce.thunar_volman - pkgs.xfce.gvfs - pkgs.xfce.xfce4_appfinder - pkgs.xfce.tumbler # found via dbus - ] - ++ optional cfg.enableXfwm pkgs.xfce.xfwm4 - ++ optional config.powerManagement.enable pkgs.xfce.xfce4_power_manager - ++ optional config.networking.networkmanager.enable pkgs.networkmanagerapplet - ++ optionals (!cfg.noDesktop) - [ pkgs.xfce.xfce4panel - pkgs.xfce.xfdesktop - pkgs.xfce.xfce4notifyd # found via dbus - ]; - - environment.pathsToLink = - [ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ]; - - environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ]; - environment.variables.GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; - # Enable helpful DBus services. services.udisks2.enable = true; services.upower.enable = config.powerManagement.enable; - }; - } diff --git a/nixos/modules/services/x11/window-managers/2bwm.nix b/nixos/modules/services/x11/window-managers/2bwm.nix index e3f5ec7dbe6..fdbdf35b0f5 100644 --- a/nixos/modules/services/x11/window-managers/2bwm.nix +++ b/nixos/modules/services/x11/window-managers/2bwm.nix @@ -25,12 +25,12 @@ in { name = "2bwm"; start = '' - ${pkgs."2bwm"}/bin/2bwm & + ${pkgs._2bwm}/bin/2bwm & waitPID=$! ''; }; - environment.systemPackages = [ pkgs."2bwm" ]; + environment.systemPackages = [ pkgs._2bwm ]; }; diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 7acd621f53a..f96d3c5afba 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/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix index 6e226c19060..4a6e1c7e56e 100644 --- a/nixos/modules/system/boot/initrd-network.nix +++ b/nixos/modules/system/boot/initrd-network.nix @@ -40,6 +40,10 @@ in kernel documentation</link>. Otherwise, if <option>networking.useDHCP</option> is enabled, an IP address is acquired using DHCP. + + You should add the module(s) required for your network card to + boot.initrd.availableKernelModules. lspci -v -s <ethernet controller> + will tell you which. ''; }; diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index 90074a1ba77..d21908f8453 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -206,12 +206,14 @@ in "xhci_hcd" "xhci_pci" "usbhid" - "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" "hid_logitech_hidpp" + "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" + "hid_logitech_hidpp" "hid_logitech_dj" - # Misc. keyboard stuff. + ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ + # Misc. x86 keyboard stuff. "pcips2" "atkbd" "i8042" - # Needed by the stage 2 init script. + # x86 RTC needed by the stage 2 init script. "rtc_cmos" ]; diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 2c0a165887b..30c54ddd0e4 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -24,7 +24,11 @@ let kernel = config.boot.kernelPackages; - packages = if config.boot.zfs.enableUnstable then { + packages = if config.boot.zfs.enableLegacyCrypto then { + spl = kernel.splLegacyCrypto; + zfs = kernel.zfsLegacyCrypto; + zfsUser = pkgs.zfsLegacyCrypto; + } else if config.boot.zfs.enableUnstable then { spl = kernel.splUnstable; zfs = kernel.zfsUnstable; zfsUser = pkgs.zfsUnstable; @@ -75,6 +79,27 @@ in ''; }; + enableLegacyCrypto = mkOption { + type = types.bool; + default = false; + description = '' + Enabling this option will allow you to continue to use the old format for + encrypted datasets. With the inclusion of stability patches the format of + encrypted datasets has changed. They can still be accessed and mounted but + in read-only mode mounted. It is highly recommended to convert them to + the new format. + + This option is only for convenience to people that cannot convert their + datasets to the new format yet and it will be removed in due time. + + For migration strategies from old format to this new one, check the Wiki: + https://nixos.wiki/wiki/NixOS_on_ZFS#Encrypted_Dataset_Format_Change + + See https://github.com/zfsonlinux/zfs/pull/6864 for more details about + the stability patches. + ''; + }; + extraPools = mkOption { type = types.listOf types.str; default = []; diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 1f424f84c6e..63d07832d10 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -230,9 +230,7 @@ let RemainAfterExit = true; }; script = '' - ip tuntap add dev "${i.name}" \ - ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \ - user "${i.virtualOwner}" + ip tuntap add dev "${i.name}" mode "${i.virtualType}" user "${i.virtualOwner}" ''; postStop = '' ip link del ${i.name} || true diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix index a365a01bfb1..5d72ad0f1bd 100644 --- a/nixos/modules/tasks/network-interfaces-systemd.nix +++ b/nixos/modules/tasks/network-interfaces-systemd.nix @@ -74,21 +74,17 @@ in networks."99-main" = genericNetwork mkDefault; } (mkMerge (flip map interfaces (i: { - netdevs = mkIf i.virtual ( - let - devType = if i.virtualType != null then i.virtualType - else (if hasPrefix "tun" i.name then "tun" else "tap"); - in { - "40-${i.name}" = { - netdevConfig = { - Name = i.name; - Kind = devType; - }; - "${devType}Config" = optionalAttrs (i.virtualOwner != null) { - User = i.virtualOwner; - }; + netdevs = mkIf i.virtual ({ + "40-${i.name}" = { + netdevConfig = { + Name = i.name; + Kind = i.virtualType; }; - }); + "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) { + User = i.virtualOwner; + }; + }; + }); networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) { name = mkDefault i.name; DHCP = mkForce (dhcpStr diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index b7e85e402aa..f4851988d63 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -273,11 +273,13 @@ let }; virtualType = mkOption { - default = null; - type = with types; nullOr (enum [ "tun" "tap" ]); + default = if hasPrefix "tun" name then "tun" else "tap"; + defaultText = literalExample ''if hasPrefix "tun" name then "tun" else "tap"''; + type = with types; enum [ "tun" "tap" ]; description = '' - The explicit type of interface to create. Accepts tun or tap strings. - Also accepts null to implicitly detect the type of device. + The type of interface to create. + The default is TUN for an interface name starting + with "tun", otherwise TAP. ''; }; diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix index 9b4136223c0..41dec2af9ed 100644 --- a/nixos/modules/testing/test-instrumentation.nix +++ b/nixos/modules/testing/test-instrumentation.nix @@ -4,13 +4,10 @@ { config, lib, pkgs, ... }: with lib; +with import ../../lib/qemu-flags.nix { inherit pkgs; }; let kernel = config.boot.kernelPackages.kernel; - # FIXME: figure out a common place for this instead of copy pasting - serialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0" - else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0" - else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'"; in { @@ -28,8 +25,8 @@ in systemd.services.backdoor = { wantedBy = [ "multi-user.target" ]; - requires = [ "dev-hvc0.device" "dev-${serialDevice}.device" ]; - after = [ "dev-hvc0.device" "dev-${serialDevice}.device" ]; + requires = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ]; + after = [ "dev-hvc0.device" "dev-${qemuSerialDevice}.device" ]; script = '' export USER=root @@ -46,7 +43,7 @@ in cd /tmp exec < /dev/hvc0 > /dev/hvc0 - while ! exec 2> /dev/${serialDevice}; do sleep 0.1; done + while ! exec 2> /dev/${qemuSerialDevice}; do sleep 0.1; done echo "connecting to host..." >&2 stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion echo @@ -55,10 +52,10 @@ in serviceConfig.KillSignal = "SIGHUP"; }; - # Prevent agetty from being instantiated on ${serialDevice}, since it - # interferes with the backdoor (writes to ${serialDevice} will randomly fail + # Prevent agetty from being instantiated on the serial device, since it + # interferes with the backdoor (writes to it will randomly fail # with EIO). Likewise for hvc0. - systemd.services."serial-getty@${serialDevice}".enable = false; + systemd.services."serial-getty@${qemuSerialDevice}".enable = false; systemd.services."serial-getty@hvc0".enable = false; boot.initrd.preDeviceCommands = @@ -94,7 +91,7 @@ in # Panic if an error occurs in stage 1 (rather than waiting for # user intervention). boot.kernelParams = - [ "console=${serialDevice}" "panic=1" "boot.panic_on_fail" ]; + [ "console=${qemuSerialDevice}" "panic=1" "boot.panic_on_fail" ]; # `xwininfo' is used by the test driver to query open windows. environment.systemPackages = [ pkgs.xorg.xwininfo ]; diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix index b4f9d8b6fc1..5e368acd6d8 100644 --- a/nixos/modules/virtualisation/container-config.nix +++ b/nixos/modules/virtualisation/container-config.nix @@ -11,7 +11,7 @@ with lib; services.udisks2.enable = mkDefault false; powerManagement.enable = mkDefault false; - networking.useHostResolvConf = true; + networking.useHostResolvConf = mkDefault true; # Containers should be light-weight, so start sshd on demand. services.openssh.startWhenNeeded = mkDefault true; diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix index 75717e08ab2..2fb38059b26 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/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix index 64465ae1852..a369b7ddbe1 100644 --- a/nixos/modules/virtualisation/libvirtd.nix +++ b/nixos/modules/virtualisation/libvirtd.nix @@ -128,6 +128,7 @@ in { dmidecode dnsmasq ebtables + cfg.qemuPackage # libvirtd requires qemu-img to manage disk images ] ++ optional vswitch.enable vswitch.package; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 26f7945a4ed..13d0eb7de5c 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -10,21 +10,11 @@ { config, lib, pkgs, ... }: with lib; +with import ../../lib/qemu-flags.nix { inherit pkgs; }; let qemu = config.system.build.qemu or pkgs.qemu_test; - qemuKvm = { - "i686-linux" = "${qemu}/bin/qemu-kvm"; - "x86_64-linux" = "${qemu}/bin/qemu-kvm -cpu kvm64"; - "armv7l-linux" = "${qemu}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host"; - "aarch64-linux" = "${qemu}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host"; - }.${pkgs.stdenv.system}; - - # FIXME: figure out a common place for this instead of copy pasting - serialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0" - else if pkgs.stdenv.isArm || pkgs.stdenv.isAarch64 then "ttyAMA0" - else throw "Unknown QEMU serial device for system '${pkgs.stdenv.system}'"; vmName = if config.networking.hostName == "" @@ -34,7 +24,7 @@ let cfg = config.virtualisation; qemuGraphics = if cfg.graphics then "" else "-nographic"; - kernelConsole = if cfg.graphics then "" else "console=${serialDevice}"; + kernelConsole = if cfg.graphics then "" else "console=${qemuSerialDevice}"; ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ]; # Shell script to start the VM. @@ -83,7 +73,7 @@ let '')} # Start QEMU. - exec ${qemuKvm} \ + exec ${qemuBinary qemu} \ -name ${vmName} \ -m ${toString config.virtualisation.memorySize} \ -smp ${toString config.virtualisation.cores} \ diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix index 00381c426d2..a544403e6be 100644 --- a/nixos/modules/virtualisation/virtualbox-image.nix +++ b/nixos/modules/virtualisation/virtualbox-image.nix @@ -25,7 +25,7 @@ in { name = "nixos-ova-${config.system.nixosLabel}-${pkgs.stdenv.system}"; inherit pkgs lib config; - partitioned = true; + partitionTableType = "legacy"; diskSize = cfg.baseImageSize; postVM = |