diff options
Diffstat (limited to 'nixos')
75 files changed, 1906 insertions, 518 deletions
diff --git a/nixos/doc/manual/administration/imperative-containers.xml b/nixos/doc/manual/administration/imperative-containers.xml index 258e1ea948da..9851eb08afb5 100644 --- a/nixos/doc/manual/administration/imperative-containers.xml +++ b/nixos/doc/manual/administration/imperative-containers.xml @@ -29,8 +29,10 @@ line. For instance, to create a container that has <literal>root</literal>: <screen> -# nixos-container create foo --config 'services.openssh.enable = true; \ - users.extraUsers.root.openssh.authorizedKeys.keys = ["ssh-dss AAAAB3N…"];' +# nixos-container create foo --config ' + services.openssh.enable = true; + users.extraUsers.root.openssh.authorizedKeys.keys = ["ssh-dss AAAAB3N…"]; +' </screen> </para> @@ -97,8 +99,11 @@ This will build and activate the new configuration. You can also specify a new configuration on the command line: <screen> -# nixos-container update foo --config 'services.httpd.enable = true; \ - services.httpd.adminAddr = "foo@example.org";' +# nixos-container update foo --config ' + services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; +' # curl http://$(nixos-container show-ip foo)/ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">… diff --git a/nixos/doc/manual/configuration/file-systems.xml b/nixos/doc/manual/configuration/file-systems.xml index d1b324af3f12..ae3d124cd6bb 100644 --- a/nixos/doc/manual/configuration/file-systems.xml +++ b/nixos/doc/manual/configuration/file-systems.xml @@ -35,6 +35,12 @@ or <literal>ext4</literal>, then it’s best to specify <option>fsType</option> to ensure that the kernel module is available.</para> +<note><para>System startup will fail if any of the filesystems fails to mount, +dropping you to the emergency shell. +You can make a mount asynchronous and non-critical by adding +<literal>options = [ "nofail" ];</literal>. +</para></note> + <xi:include href="luks-file-systems.xml" /> </chapter> diff --git a/nixos/doc/manual/installation/installing-usb.xml b/nixos/doc/manual/installation/installing-usb.xml index dae733060569..4a74e406b14c 100644 --- a/nixos/doc/manual/installation/installing-usb.xml +++ b/nixos/doc/manual/installation/installing-usb.xml @@ -34,6 +34,11 @@ ISO, copy its contents verbatim to your drive, then either: in <link xlink:href="https://www.kernel.org/doc/Documentation/kernel-parameters.txt"> the kernel documentation</link> for more details).</para> </listitem> + <listitem> + <para>If you want to load the contents of the ISO to ram after bootin + (So you can remove the stick after bootup) you can append the parameter + <literal>copytoram</literal>to the <literal>options</literal> field.</para> + </listitem> </itemizedlist> </para> diff --git a/nixos/doc/manual/release-notes/rl-1709.xml b/nixos/doc/manual/release-notes/rl-1709.xml index 5fba4c34ec82..257664397599 100644 --- a/nixos/doc/manual/release-notes/rl-1709.xml +++ b/nixos/doc/manual/release-notes/rl-1709.xml @@ -17,7 +17,29 @@ has the following highlights: </para> A consequence is that UIDs and GIDs are no longer reused. </para> </listitem> - + <listitem> + <para> + The module option <option>services.xserver.xrandrHeads</option> now + causes the first head specified in this list to be set as the primary + head. Apart from that, it's now possible to also set additional options + by using an attribute set, for example: +<programlisting> +{ services.xserver.xrandrHeads = [ + "HDMI-0" + { + output = "DVI-0"; + primary = true; + monitorConfig = '' + Option "Rotate" "right" + ''; + } + ]; +} +</programlisting> + This will set the <literal>DVI-0</literal> output to be the primary head, + even though <literal>HDMI-0</literal> is the first head in the list. + </para> + </listitem> </itemizedlist> <para>The following new services were added since the last release:</para> @@ -39,6 +61,13 @@ following incompatible changes:</para> All JetBrains IDEs are now at <literal>jetbrains</literal>. </para> </listitem> + <listitem> + <para> + <literal>flexget</literal>'s state database cannot be upgraded to its + new internal format, requiring removal of any existing + <literal>db-config.sqlite</literal> which will be automatically recreated. + </para> + </listitem> </itemizedlist> diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix index 8c5de22f30ff..56766ec9047f 100644 --- a/nixos/lib/make-disk-image.nix +++ b/nixos/lib/make-disk-image.nix @@ -33,42 +33,124 @@ , name ? "nixos-disk-image" - # This prevents errors while checking nix-store validity, see - # https://github.com/NixOS/nix/issues/1134 -, fixValidity ? true - , format ? "raw" }: with lib; -pkgs.vmTools.runInLinuxVM ( +let + # Copied from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/installer/cd-dvd/channel.nix + # TODO: factor out more cleanly + + # Do not include these things: + # - The '.git' directory + # - Result symlinks from nix-build ('result', 'result-2', 'result-bin', ...) + # - VIM/Emacs swap/backup files ('.swp', '.swo', '.foo.swp', 'foo~', ...) + filterFn = path: type: let basename = baseNameOf (toString path); in + if type == "directory" then basename != ".git" + else if type == "symlink" then builtins.match "^result(|-.*)$" basename == null + else builtins.match "^((|\..*)\.sw[a-z]|.*~)$" basename == null; + + nixpkgs = builtins.filterSource filterFn pkgs.path; + + channelSources = pkgs.runCommand "nixos-${config.system.nixosVersion}" {} '' + mkdir -p $out + cp -prd ${nixpkgs} $out/nixos + chmod -R u+w $out/nixos + if [ ! -e $out/nixos/nixpkgs ]; then + ln -s . $out/nixos/nixpkgs + fi + rm -rf $out/nixos/.git + echo -n ${config.system.nixosVersionSuffix} > $out/nixos/.version-suffix + ''; + + metaClosure = pkgs.writeText "meta" '' + ${config.system.build.toplevel} + ${config.nix.package.out} + ${channelSources} + ''; + + prepareImageInputs = with pkgs; [ rsync utillinux parted e2fsprogs lkl fakeroot config.system.build.nixos-prepare-root ] ++ stdenv.initialPath; + + # I'm preserving the line below because I'm going to search for it across nixpkgs to consolidate + # image building logic. The comment right below this now appears in 4 different places in nixpkgs :) + # !!! should use XML. + sources = map (x: x.source) contents; + targets = map (x: x.target) contents; + + prepareImage = '' + export PATH=${pkgs.lib.makeSearchPathOutput "bin" "bin" prepareImageInputs} + + mkdir $out + diskImage=nixos.raw + truncate -s ${toString diskSize}M $diskImage + + ${if partitioned then '' + parted $diskImage -- mklabel msdos mkpart primary ext4 1M -1s + offset=$((2048*512)) + '' else '' + offset=0 + ''} + + mkfs.${fsType} -F -L nixos -E offset=$offset $diskImage + + root="$PWD/root" + mkdir -p $root + + # Copy arbitrary other files into the image + # Semi-shamelessly copied from make-etc.sh. I (@copumpkin) shall factor this stuff out as part of + # https://github.com/NixOS/nixpkgs/issues/23052. + set -f + sources_=(${concatStringsSep " " sources}) + targets_=(${concatStringsSep " " targets}) + set +f + + for ((i = 0; i < ''${#targets_[@]}; i++)); do + source="''${sources_[$i]}" + target="''${targets_[$i]}" + + if [[ "$source" =~ '*' ]]; then + # If the source name contains '*', perform globbing. + mkdir -p $root/$target + for fn in $source; do + rsync -a --no-o --no-g "$fn" $root/$target/ + done + else + mkdir -p $root/$(dirname $target) + if ! [ -e $root/$target ]; then + rsync -a --no-o --no-g $source $root/$target + else + echo "duplicate entry $target -> $source" + exit 1 + fi + fi + done + + # TODO: Nix really likes to chown things it creates to its current user... + fakeroot nixos-prepare-root $root ${channelSources} ${config.system.build.toplevel} closure + + echo "copying staging root to image..." + cptofs ${pkgs.lib.optionalString partitioned "-P 1"} -t ${fsType} -i $diskImage $root/* / + ''; +in pkgs.vmTools.runInLinuxVM ( pkgs.runCommand name - { preVM = - '' - mkdir $out - diskImage=$out/nixos.${if format == "qcow2" then "qcow2" else "img"} - ${pkgs.vmTools.qemu}/bin/qemu-img create -f ${format} $diskImage "${toString diskSize}M" - mv closure xchg/ - ''; - buildInputs = with pkgs; [ utillinux perl e2fsprogs parted rsync ]; - - # I'm preserving the line below because I'm going to search for it across nixpkgs to consolidate - # image building logic. The comment right below this now appears in 4 different places in nixpkgs :) - # !!! should use XML. - sources = map (x: x.source) contents; - targets = map (x: x.target) contents; - - exportReferencesGraph = - [ "closure" config.system.build.toplevel ]; - inherit postVM; + { preVM = prepareImage; + buildInputs = with pkgs; [ utillinux e2fsprogs ]; + exportReferencesGraph = [ "closure" metaClosure ]; + postVM = '' + ${if format == "raw" then '' + mv $diskImage $out/nixos.img + diskImage=$out/nixos.img + '' else '' + ${pkgs.qemu}/bin/qemu-img convert -f raw -O qcow2 $diskImage $out/nixos.qcow2 + diskImage=$out/nixos.qcow2 + ''} + ${postVM} + ''; memSize = 1024; } '' ${if partitioned then '' - # Create a single / partition. - parted /dev/vda mklabel msdos - parted /dev/vda -- mkpart primary ext2 1M -1s . /sys/class/block/vda1/uevent mknod /dev/vda1 b $MAJOR $MINOR rootDisk=/dev/vda1 @@ -76,74 +158,34 @@ pkgs.vmTools.runInLinuxVM ( rootDisk=/dev/vda ''} - # Create an empty filesystem and mount it. - mkfs.${fsType} -L nixos $rootDisk - mkdir /mnt - mount $rootDisk /mnt - - # Register the paths in the Nix database. - printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \ - ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group "" - - ${if fixValidity then '' - # Add missing size/hash fields to the database. FIXME: - # exportReferencesGraph should provide these directly. - ${config.nix.package.out}/bin/nix-store --verify --check-contents --option build-users-group "" - '' else ""} - - # In case the bootloader tries to write to /dev/sda… + # Some tools assume these exist ln -s vda /dev/xvda ln -s vda /dev/sda - # Install the closure onto the image - USER=root ${config.system.build.nixos-install}/bin/nixos-install \ - --closure ${config.system.build.toplevel} \ - --no-channel-copy \ - --no-root-passwd \ - ${optionalString (!installBootLoader) "--no-bootloader"} + mountPoint=/mnt + mkdir $mountPoint + mount $rootDisk $mountPoint - # Install a configuration.nix. + # Install a configuration.nix mkdir -p /mnt/etc/nixos ${optionalString (configFile != null) '' cp ${configFile} /mnt/etc/nixos/configuration.nix ''} - # Remove /etc/machine-id so that each machine cloning this image will get its own id - rm -f /mnt/etc/machine-id - - # Copy arbitrary other files into the image - # Semi-shamelessly copied from make-etc.sh. I (@copumpkin) shall factor this stuff out as part of - # https://github.com/NixOS/nixpkgs/issues/23052. - set -f - sources_=($sources) - targets_=($targets) - set +f - - for ((i = 0; i < ''${#targets_[@]}; i++)); do - source="''${sources_[$i]}" - target="''${targets_[$i]}" + mount --rbind /dev $mountPoint/dev + mount --rbind /proc $mountPoint/proc + mount --rbind /sys $mountPoint/sys - if [[ "$source" =~ '*' ]]; then + # Set up core system link, GRUB, etc. + NIXOS_INSTALL_BOOTLOADER=1 chroot $mountPoint /nix/var/nix/profiles/system/bin/switch-to-configuration boot - # If the source name contains '*', perform globbing. - mkdir -p /mnt/$target - for fn in $source; do - rsync -a --no-o --no-g "$fn" /mnt/$target/ - done + # TODO: figure out if I should activate, but for now I won't + # chroot $mountPoint /nix/var/nix/profiles/system/activate - else - - mkdir -p /mnt/$(dirname $target) - if ! [ -e /mnt/$target ]; then - rsync -a --no-o --no-g $source /mnt/$target - else - echo "duplicate entry $target -> $source" - exit 1 - fi - fi - done + # The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images + rm -f $mountPoint/etc/machine-id - umount /mnt + umount -R /mnt # Make sure resize2fs works. Note that resize2fs has stricter criteria for resizing than a normal # mount, so the `-c 0` and `-i 0` don't affect it. Setting it to `now` doesn't produce deterministic diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix index b4190df8335b..cdfac71634d4 100644 --- a/nixos/maintainers/scripts/ec2/amazon-image.nix +++ b/nixos/maintainers/scripts/ec2/amazon-image.nix @@ -6,10 +6,7 @@ let cfg = config.amazonImage; in { - imports = - [ ../../../modules/installer/cd-dvd/channel.nix - ../../../modules/virtualisation/amazon-image.nix - ]; + imports = [ ../../../modules/virtualisation/amazon-image.nix ]; options.amazonImage = { contents = mkOption { diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix index 799f0793c74f..65ef95127805 100644 --- a/nixos/modules/config/i18n.nix +++ b/nixos/modules/config/i18n.nix @@ -2,21 +2,27 @@ with lib; -let - - glibcLocales = pkgs.glibcLocales.override { - allLocales = any (x: x == "all") config.i18n.supportedLocales; - locales = config.i18n.supportedLocales; - }; - -in - { ###### interface options = { i18n = { + glibcLocales = mkOption { + type = types.path; + default = pkgs.glibcLocales.override { + allLocales = any (x: x == "all") config.i18n.supportedLocales; + locales = config.i18n.supportedLocales; + }; + example = literalExample "pkgs.glibcLocales"; + description = '' + Customized pkg.glibcLocales package. + + Changing this option can disable handling of i18n.defaultLocale + and supportedLocale. + ''; + }; + defaultLocale = mkOption { type = types.str; default = "en_US.UTF-8"; @@ -118,7 +124,7 @@ in ''); environment.systemPackages = - optional (config.i18n.supportedLocales != []) glibcLocales; + optional (config.i18n.supportedLocales != []) config.i18n.glibcLocales; environment.sessionVariables = { LANG = config.i18n.defaultLocale; @@ -126,7 +132,7 @@ in }; systemd.globalEnvironment = mkIf (config.i18n.supportedLocales != []) { - LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive"; + LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive"; }; # ‘/etc/locale.conf’ is used by systemd. diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix index 0c9981470d72..833782477199 100644 --- a/nixos/modules/installer/tools/nix-fallback-paths.nix +++ b/nixos/modules/installer/tools/nix-fallback-paths.nix @@ -1,5 +1,5 @@ { - x86_64-linux = "/nix/store/j6q3pb75q1sbk0xsa5x6a629ph98ycdl-nix-1.11.8"; - i686-linux = "/nix/store/4m6ps568l988bbr1p2k3w9raq3rblppi-nix-1.11.8"; - x86_64-darwin = "/nix/store/cc5q944yn3j2hrs8k0kxx9r2mk9mni8a-nix-1.11.8"; + x86_64-linux = "/nix/store/71im965h634iy99zsmlncw6qhx5jcclx-nix-1.11.9"; + i686-linux = "/nix/store/cgvavixkayc36l6kl92i8mxr6k0p2yhy-nix-1.11.9"; + x86_64-darwin = "/nix/store/w1c96v5yxvdmq4nvqlxjvg6kp7xa2lag-nix-1.11.9"; } diff --git a/nixos/modules/installer/tools/nixos-prepare-root.sh b/nixos/modules/installer/tools/nixos-prepare-root.sh index c374330f8464..0bd70d2d349c 100644 --- a/nixos/modules/installer/tools/nixos-prepare-root.sh +++ b/nixos/modules/installer/tools/nixos-prepare-root.sh @@ -37,7 +37,7 @@ mkdir -m 0755 -p $mountPoint/tmp/root mkdir -m 0755 -p $mountPoint/var mkdir -m 0700 -p $mountPoint/root -ln -s /run $mountPoint/var/run +ln -sf /run $mountPoint/var/run # Create the necessary Nix directories on the target device mkdir -m 0755 -p \ @@ -70,7 +70,7 @@ for i in $closures; do rsync -a $j $mountPoint/nix/store/ done - nix-store --register-validity < $i + nix-store --option build-users-group root --register-validity < $i fi done diff --git a/nixos/modules/misc/extra-arguments.nix b/nixos/modules/misc/extra-arguments.nix index 19002b17dace..f4ee94ecc0d7 100644 --- a/nixos/modules/misc/extra-arguments.nix +++ b/nixos/modules/misc/extra-arguments.nix @@ -2,16 +2,6 @@ { _module.args = { - pkgs_i686 = import ../../.. { - system = "i686-linux"; - # FIXME: we enable config.allowUnfree to make packages like - # nvidia-x11 available. This isn't a problem because if the user has - # ‘nixpkgs.config.allowUnfree = false’, then evaluation will fail on - # the 64-bit package anyway. However, it would be cleaner to respect - # nixpkgs.config here. - config.allowUnfree = true; - }; - utils = import ../../lib/utils.nix pkgs; }; } diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index feecee3225be..d217b3452fb5 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -289,6 +289,11 @@ rpc = 271; geoip = 272; fcron = 273; + sonarr = 274; + radarr = 275; + jackett = 276; + aria2 = 277; + clickhouse = 278; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -547,6 +552,11 @@ #rpc = 271; # unused #geoip = 272; # unused fcron = 273; + sonarr = 274; + radarr = 275; + jackett = 276; + aria2 = 277; + clickhouse = 278; # 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 b8824e6374b3..1793c1447d60 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -42,6 +42,8 @@ let merge = lib.mergeOneOption; }; + _pkgs = import ../../.. config.nixpkgs; + in { @@ -97,6 +99,9 @@ in }; config = { - _module.args.pkgs = import ../../.. config.nixpkgs; + _module.args = { + pkgs = _pkgs; + pkgs_i686 = _pkgs.pkgsi686Linux; + }; }; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 530ae1d1cf03..7e523ac483cd 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -71,6 +71,7 @@ ./programs/atop.nix ./programs/bash/bash.nix ./programs/blcr.nix + ./programs/browserpass.nix ./programs/cdemu.nix ./programs/chromium.nix ./programs/command-not-found/command-not-found.nix @@ -88,6 +89,7 @@ ./programs/mtr.nix ./programs/nano.nix ./programs/oblogout.nix + ./programs/qt5ct.nix ./programs/screen.nix ./programs/slock.nix ./programs/shadow.nix @@ -102,7 +104,9 @@ ./programs/wvdial.nix ./programs/xfs_quota.nix ./programs/xonsh.nix + ./programs/zsh/oh-my-zsh.nix ./programs/zsh/zsh.nix + ./programs/zsh/zsh-syntax-highlighting.nix ./rename.nix ./security/acme.nix ./security/apparmor.nix @@ -114,6 +118,7 @@ ./security/duosec.nix ./security/grsecurity.nix ./security/hidepid.nix + ./security/lock-kernel-modules.nix ./security/oath.nix ./security/pam.nix ./security/pam_usb.nix @@ -161,6 +166,7 @@ ./services/continuous-integration/jenkins/slave.nix ./services/databases/4store-endpoint.nix ./services/databases/4store.nix + ./services/databases/clickhouse.nix ./services/databases/couchdb.nix ./services/databases/firebird.nix ./services/databases/hbase.nix @@ -502,6 +508,7 @@ ./services/networking/wpa_supplicant.nix ./services/networking/xinetd.nix ./services/networking/xl2tpd.nix + ./services/networking/xrdp.nix ./services/networking/zerobin.nix ./services/networking/zerotierone.nix ./services/networking/znc.nix @@ -527,8 +534,10 @@ ./services/security/munge.nix ./services/security/oauth2_proxy.nix ./services/security/physlock.nix - ./services/security/torify.nix + ./services/security/shibboleth-sp.nix + ./services/security/sshguard.nix ./services/security/tor.nix + ./services/security/torify.nix ./services/security/torsocks.nix ./services/system/cgmanager.nix ./services/system/cloud-init.nix diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix new file mode 100644 index 000000000000..8bde2e4f4984 --- /dev/null +++ b/nixos/modules/profiles/hardened.nix @@ -0,0 +1,62 @@ +# A profile with most (vanilla) hardening options enabled by default, +# potentially at the cost of features and performance. + +{ config, lib, pkgs, ... }: + +with lib; + +{ + boot.kernelPackages = mkDefault pkgs.linuxPackages_hardened; + + security.hideProcessInformation = mkDefault true; + + security.lockKernelModules = mkDefault true; + + security.apparmor.enable = mkDefault true; + + boot.kernelParams = [ + # Overwrite free'd memory + "page_poison=1" + + # Disable legacy virtual syscalls + "vsyscall=none" + + # Disable hibernation (allows replacing the running kernel) + "nohibernate" + ]; + + # Restrict ptrace() usage to processes with a pre-defined relationship + # (e.g., parent/child) + boot.kernel.sysctl."kernel.yama.ptrace_scope" = mkOverride 500 1; + + # Prevent replacing the running kernel image w/o reboot + boot.kernel.sysctl."kernel.kexec_load_disabled" = mkDefault true; + + # Restrict access to kernel ring buffer (information leaks) + boot.kernel.sysctl."kernel.dmesg_restrict" = mkDefault true; + + # Hide kptrs even for processes with CAP_SYSLOG + boot.kernel.sysctl."kernel.kptr_restrict" = mkOverride 500 2; + + # Unprivileged access to bpf() has been used for privilege escalation in + # the past + boot.kernel.sysctl."kernel.unprivileged_bpf_disabled" = mkDefault true; + + # Disable bpf() JIT (to eliminate spray attacks) + boot.kernel.sysctl."net.core.bpf_jit_enable" = mkDefault false; + + # ... or at least apply some hardening to it + boot.kernel.sysctl."net.core.bpf_jit_harden" = mkDefault true; + + # A recurring problem with user namespaces is that there are + # still code paths where the kernel's permission checking logic + # fails to account for namespacing, instead permitting a + # namespaced process to act outside the namespace with the + # same privileges as it would have inside it. This is particularly + # bad in the common case of running as root within the namespace. + # + # Setting the number of allowed userns to 0 effectively disables + # the feature at runtime. Attempting to create a user namespace + # with unshare will then fail with "no space left on device". + boot.kernel.sysctl."user.max_user_namespaces" = mkDefault 0; +} diff --git a/nixos/modules/programs/browserpass.nix b/nixos/modules/programs/browserpass.nix new file mode 100644 index 000000000000..2b7ec1856431 --- /dev/null +++ b/nixos/modules/programs/browserpass.nix @@ -0,0 +1,26 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + options = { + programs.browserpass = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to install the NativeMessaging configuration for installed browsers. + ''; + }; + }; + }; + + ###### implementation + config = mkIf config.programs.browserpass.enable { + environment.systemPackages = [ pkgs.browserpass ]; + environment.etc."chromium/native-messaging-hosts/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-host.json"; + environment.etc."opt/chrome/native-messaging-hosts/com.dannyvankooten.browserpass.json".source = "${pkgs.browserpass}/etc/chrome-host.json"; + }; +} diff --git a/nixos/modules/programs/command-not-found/command-not-found.nix b/nixos/modules/programs/command-not-found/command-not-found.nix index 6fb926fe1d5f..55529d73cb60 100644 --- a/nixos/modules/programs/command-not-found/command-not-found.nix +++ b/nixos/modules/programs/command-not-found/command-not-found.nix @@ -44,7 +44,7 @@ in '' # This function is called whenever a command is not found. command_not_found_handle() { - local p=${commandNotFound} + local p=${commandNotFound}/bin/command-not-found if [ -x $p -a -f ${cfg.dbPath} ]; then # Run the helper program. $p "$@" @@ -65,7 +65,7 @@ in '' # This function is called whenever a command is not found. command_not_found_handler() { - local p=${commandNotFound} + local p=${commandNotFound}/bin/command-not-found if [ -x $p -a -f ${cfg.dbPath} ]; then # Run the helper program. $p "$@" diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix index a1615c920c02..48a1e2a0a883 100644 --- a/nixos/modules/programs/environment.nix +++ b/nixos/modules/programs/environment.nix @@ -20,6 +20,7 @@ in { NIXPKGS_CONFIG = "/etc/nix/nixpkgs-config.nix"; PAGER = mkDefault "less -R"; EDITOR = mkDefault "nano"; + XCURSOR_PATH = "$HOME/.icons"; }; environment.profiles = @@ -42,6 +43,7 @@ in GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" ]; XDG_CONFIG_DIRS = [ "/etc/xdg" ]; XDG_DATA_DIRS = [ "/share" ]; + XCURSOR_PATH = [ "/share/icons" ]; MOZ_PLUGIN_PATH = [ "/lib/mozilla/plugins" ]; LIBEXEC_PATH = [ "/lib/libexec" ]; }; diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix new file mode 100644 index 000000000000..550634e65be9 --- /dev/null +++ b/nixos/modules/programs/qt5ct.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + meta.maintainers = [ maintainers.romildo ]; + + ###### interface + options = { + programs.qt5ct = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the Qt5 Configuration Tool (qt5ct), a + program that allows users to configure Qt5 settings (theme, + font, icons, etc.) under desktop environments or window + manager without Qt integration. + + Official home page: <link xlink:href="https://sourceforge.net/projects/qt5ct/">https://sourceforge.net/projects/qt5ct/</link> + ''; + }; + }; + }; + + ###### implementation + config = mkIf config.programs.qt5ct.enable { + environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct"; + environment.systemPackages = [ pkgs.qt5ct ]; + }; +} diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix new file mode 100644 index 000000000000..335f596ca80f --- /dev/null +++ b/nixos/modules/programs/zsh/oh-my-zsh.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.zsh.oh-my-zsh; +in + { + options = { + programs.zsh.oh-my-zsh = { + enable = mkOption { + default = false; + description = '' + Enable oh-my-zsh. + ''; + }; + + plugins = mkOption { + default = []; + type = types.listOf(types.str); + description = '' + List of oh-my-zsh plugins + ''; + }; + + custom = mkOption { + default = ""; + type = types.str; + description = '' + Path to a custom oh-my-zsh package to override config of oh-my-zsh. + ''; + }; + + theme = mkOption { + default = ""; + type = types.str; + description = '' + Name of the theme to be used by oh-my-zsh. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ oh-my-zsh ]; + + programs.zsh.interactiveShellInit = with pkgs; with builtins; '' + # oh-my-zsh configuration generated by NixOS + export ZSH=${oh-my-zsh}/share/oh-my-zsh + + ${optionalString (length(cfg.plugins) > 0) + "plugins=(${concatStringsSep " " cfg.plugins})" + } + + ${optionalString (stringLength(cfg.custom) > 0) + "ZSH_CUSTOM=\"${cfg.custom}\"" + } + + ${optionalString (stringLength(cfg.theme) > 0) + "ZSH_THEME=\"${cfg.theme}\"" + } + + source $ZSH/oh-my-zsh.sh + ''; + }; + } diff --git a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix new file mode 100644 index 000000000000..e5246bb4260a --- /dev/null +++ b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix @@ -0,0 +1,81 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.zsh.syntax-highlighting; +in + { + options = { + programs.zsh.syntax-highlighting = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Enable zsh-syntax-highlighting. + ''; + }; + + highlighters = mkOption { + default = [ "main" ]; + + # https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md + type = types.listOf(types.enum([ + "main" + "brackets" + "pattern" + "cursor" + "root" + "line" + ])); + + description = '' + Specifies the highlighters to be used by zsh-syntax-highlighting. + + The following defined options can be found here: + https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md + ''; + }; + + patterns = mkOption { + default = []; + type = types.listOf(types.listOf(types.string)); + + example = literalExample '' + [ + ["rm -rf *" "fg=white,bold,bg=red"] + ] + ''; + + description = '' + Specifies custom patterns to be highlighted by zsh-syntax-highlighting. + + Please refer to the docs for more information about the usage: + https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/pattern.md + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = with pkgs; [ zsh-syntax-highlighting ]; + + programs.zsh.interactiveShellInit = with pkgs; with builtins; '' + source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + + ${optionalString (length(cfg.highlighters) > 0) + "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${concatStringsSep " " cfg.highlighters})" + } + + ${optionalString (length(cfg.patterns) > 0) + (assert(elem "pattern" cfg.highlighters); (foldl ( + a: b: + assert(length(b) == 2); '' + ${a} + ZSH_HIGHLIGHT_PATTERNS+=('${elemAt b 0}' '${elemAt b 1}') + '' + ) "") cfg.patterns) + } + ''; + }; + } diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix index 990e6648e82b..acb3e987aee6 100644 --- a/nixos/modules/programs/zsh/zsh.nix +++ b/nixos/modules/programs/zsh/zsh.nix @@ -84,14 +84,6 @@ in type = types.bool; }; - enableSyntaxHighlighting = mkOption { - default = false; - description = '' - Enable zsh-syntax-highlighting - ''; - type = types.bool; - }; - enableAutosuggestions = mkOption { default = false; description = '' @@ -130,10 +122,6 @@ in ${if cfg.enableCompletion then "autoload -U compinit && compinit" else ""} - ${optionalString (cfg.enableSyntaxHighlighting) - "source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" - } - ${optionalString (cfg.enableAutosuggestions) "source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh" } @@ -143,7 +131,6 @@ in ${cfge.interactiveShellInit} - HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help" ''; @@ -206,8 +193,7 @@ in environment.etc."zinputrc".source = ./zinputrc; environment.systemPackages = [ pkgs.zsh ] - ++ optional cfg.enableCompletion pkgs.nix-zsh-completions - ++ optional cfg.enableSyntaxHighlighting pkgs.zsh-syntax-highlighting; + ++ optional cfg.enableCompletion pkgs.nix-zsh-completions; environment.pathsToLink = optional cfg.enableCompletion "/share/zsh"; diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 0174fe544e35..8a313f6c7fca 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -204,5 +204,8 @@ with lib; "Set the option `services.xserver.displayManager.sddm.package' instead.") (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "") (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "") + + # ZSH + (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntax-highlighting" "enable" ]) ]; } diff --git a/nixos/modules/security/grsecurity.nix b/nixos/modules/security/grsecurity.nix index 657b059faf2e..d23c7f2e86de 100644 --- a/nixos/modules/security/grsecurity.nix +++ b/nixos/modules/security/grsecurity.nix @@ -13,7 +13,7 @@ in { meta = { - maintainers = with maintainers; [ joachifm ]; + maintainers = with maintainers; [ ]; doc = ./grsecurity.xml; }; diff --git a/nixos/modules/security/grsecurity.xml b/nixos/modules/security/grsecurity.xml index 620e8f653f99..0a884b3f9b55 100644 --- a/nixos/modules/security/grsecurity.xml +++ b/nixos/modules/security/grsecurity.xml @@ -26,9 +26,11 @@ <link xlink:href="https://wiki.archlinux.org/index.php/Grsecurity">Arch Linux wiki page on grsecurity</link>. - <note><para>grsecurity/PaX is only available for the latest linux -stable - kernel; patches against older kernels are available from upstream only for - a fee.</para></note> + <warning><para>Upstream has ceased free support for grsecurity/PaX. See + <link xlink:href="https://grsecurity.net/passing_the_baton.php"> + the announcement</link> for more information. Consequently, NixOS + support for grsecurity/PaX also must cease. Enabling this module will + result in a build error.</para></warning> <note><para>We standardise on a desktop oriented configuration primarily due to lack of resources. The grsecurity/PaX configuration state space is huge and each configuration requires quite a bit of testing to ensure that the diff --git a/nixos/modules/security/lock-kernel-modules.nix b/nixos/modules/security/lock-kernel-modules.nix new file mode 100644 index 000000000000..260ec3fc9464 --- /dev/null +++ b/nixos/modules/security/lock-kernel-modules.nix @@ -0,0 +1,36 @@ +{ config, lib, ... }: + +with lib; + +{ + options = { + security.lockKernelModules = mkOption { + type = types.bool; + default = false; + description = '' + Disable kernel module loading once the system is fully initialised. + Module loading is disabled until the next reboot. Problems caused + by delayed module loading can be fixed by adding the module(s) in + question to <option>boot.kernelModules</option>. + ''; + }; + }; + + config = mkIf config.security.lockKernelModules { + systemd.services.disable-kernel-module-loading = rec { + description = "Disable kernel module loading"; + + wantedBy = [ config.systemd.defaultUnit ]; + after = [ "systemd-udev-settle.service" "firewall.service" "systemd-modules-load.service" ] ++ wantedBy; + + script = "echo -n 1 > /proc/sys/kernel/modules_disabled"; + + unitConfig.ConditionPathIsReadWrite = "/proc/sys/kernel"; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + }; +} diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index 67112343c335..7c9dedb67ad2 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -230,6 +230,14 @@ in Download bandwidth rate limit in bytes. ''; }; + + verbose = mkOption { + type = types.bool; + default = false; + description = '' + Whether to produce verbose logging output. + ''; + }; }; } )); @@ -293,7 +301,10 @@ in ''; script = - let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" -c -f "${name}-$(date +"%Y%m%d%H%M%S")" ${concatStringsSep " " cfg.directories}''; + let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \ + -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \ + ${optionalString cfg.verbose "-v"} \ + ${concatStringsSep " " cfg.directories}''; in if (cfg.cachedir != null) then '' mkdir -p ${cfg.cachedir} chmod 0700 ${cfg.cachedir} diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix index 648089f90b7b..35c0308c9dc8 100644 --- a/nixos/modules/services/backup/znapzend.nix +++ b/nixos/modules/services/backup/znapzend.nix @@ -20,15 +20,12 @@ in description = "ZnapZend - ZFS Backup System"; after = [ "zfs.target" ]; - path = with pkgs; [ znapzend zfs mbuffer openssh ]; + path = with pkgs; [ zfs mbuffer openssh ]; - script = '' - znapzend - ''; - - reload = '' - /bin/kill -HUP $MAINPID - ''; + serviceConfig = { + ExecStart = "${pkgs.znapzend}/bin/znapzend"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; }; }; diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix index 57f592a2e550..c515622d11a0 100644 --- a/nixos/modules/services/continuous-integration/hydra/default.nix +++ b/nixos/modules/services/continuous-integration/hydra/default.nix @@ -233,6 +233,7 @@ in hydra_logo ${cfg.logo} ''} gc_roots_dir ${cfg.gcRootsDir} + use-substitutes = ${if cfg.useSubstitutes then "1" else "0"} ''; environment.systemPackages = [ cfg.package ]; diff --git a/nixos/modules/services/databases/clickhouse.nix b/nixos/modules/services/databases/clickhouse.nix new file mode 100644 index 000000000000..631d7f8cba79 --- /dev/null +++ b/nixos/modules/services/databases/clickhouse.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.clickhouse; + confDir = "/etc/clickhouse-server"; + stateDir = "/var/lib/clickhouse"; +in +with lib; +{ + + ###### interface + + options = { + + services.clickhouse = { + + enable = mkOption { + default = false; + description = "Whether to enable ClickHouse database server."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.extraUsers.clickhouse = { + name = "clickhouse"; + uid = config.ids.uids.clickhouse; + group = "clickhouse"; + description = "ClickHouse server user"; + }; + + users.extraGroups.clickhouse.gid = config.ids.gids.clickhouse; + + systemd.services.clickhouse = { + description = "ClickHouse server"; + + wantedBy = [ "multi-user.target" ]; + + after = [ "network.target" ]; + + preStart = '' + mkdir -p ${stateDir} + chown clickhouse:clickhouse ${confDir} ${stateDir} + ''; + + script = '' + cd "${confDir}" + exec ${pkgs.clickhouse}/bin/clickhouse-server + ''; + + serviceConfig = { + User = "clickhouse"; + Group = "clickhouse"; + PermissionsStartOnly = true; + }; + }; + + environment.etc = { + "clickhouse-server/config.xml" = { + source = "${pkgs.clickhouse}/etc/clickhouse-server/config.xml"; + }; + + "clickhouse-server/users.xml" = { + source = "${pkgs.clickhouse}/etc/clickhouse-server/users.xml"; + }; + }; + + }; + +} diff --git a/nixos/modules/services/hardware/amd-hybrid-graphics.nix b/nixos/modules/services/hardware/amd-hybrid-graphics.nix index 087bd0e04098..b0f9ff56d1b2 100644 --- a/nixos/modules/services/hardware/amd-hybrid-graphics.nix +++ b/nixos/modules/services/hardware/amd-hybrid-graphics.nix @@ -25,15 +25,22 @@ path = [ pkgs.bash ]; description = "Disable AMD Card"; after = [ "sys-kernel-debug.mount" ]; - requires = [ "sys-kernel-debug.mount" ]; - wantedBy = [ "multi-user.target" ]; + 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; exit 0'"; - ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch; exit 0'"; + ExecStart = "${pkgs.bash}/bin/sh -c 'echo -e \"IGD\\nOFF\" > /sys/kernel/debug/vgaswitcheroo/switch'"; + ExecStop = "${pkgs.bash}/bin/sh -c 'echo ON >/sys/kernel/debug/vgaswitcheroo/switch'"; }; }; + systemd.paths."vgaswitcheroo" = { + pathConfig = { + PathExists = "/sys/kernel/debug/vgaswitcheroo/switch"; + Unit = "amd-hybrid-graphics.service"; + }; + wantedBy = ["multi-user.target"]; + }; }; } diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix index 52719222db66..4d78cddcb54f 100644 --- a/nixos/modules/services/misc/bepasty.nix +++ b/nixos/modules/services/misc/bepasty.nix @@ -21,7 +21,7 @@ in configure a number of bepasty servers which will be started with gunicorn. ''; - type = with types ; attrsOf (submodule ({ + type = with types ; attrsOf (submodule ({ config, ... } : { options = { @@ -34,7 +34,6 @@ in default = "127.0.0.1:8000"; }; - dataDir = mkOption { type = types.str; description = '' @@ -73,10 +72,28 @@ in type = types.str; description = '' server secret for safe session cookies, must be set. + + Warning: this secret is stored in the WORLD-READABLE Nix store! + + It's recommended to use <option>secretKeyFile</option> + which takes precedence over <option>secretKey</option>. ''; default = ""; }; + secretKeyFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + A file that contains the server secret for safe session cookies, must be set. + + <option>secretKeyFile</option> takes precedence over <option>secretKey</option>. + + Warning: when <option>secretKey</option> is non-empty <option>secretKeyFile</option> + defaults to a file in the WORLD-READABLE Nix store containing that secret. + ''; + }; + workDir = mkOption { type = types.str; description = '' @@ -87,11 +104,22 @@ in }; }; + config = { + secretKeyFile = mkDefault ( + if config.secretKey != "" + then toString (pkgs.writeTextFile { + name = "bepasty-secret-key"; + text = config.secretKey; + }) + else null + ); + }; })); }; }; config = mkIf cfg.enable { + environment.systemPackages = [ bepasty ]; # creates gunicorn systemd service for each configured server @@ -115,7 +143,7 @@ in serviceConfig = { Type = "simple"; PrivateTmp = true; - ExecStartPre = assert server.secretKey != ""; pkgs.writeScript "bepasty-server.${name}-init" '' + ExecStartPre = assert !isNull server.secretKeyFile; pkgs.writeScript "bepasty-server.${name}-init" '' #!/bin/sh mkdir -p "${server.workDir}" mkdir -p "${server.dataDir}" @@ -123,7 +151,7 @@ in cat > ${server.workDir}/bepasty-${name}.conf <<EOF SITENAME="${name}" STORAGE_FILESYSTEM_DIRECTORY="${server.dataDir}" - SECRET_KEY="${server.secretKey}" + SECRET_KEY="$(cat "${server.secretKeyFile}")" DEFAULT_PERMISSIONS="${server.defaultPermissions}" ${server.extraConfig} EOF diff --git a/nixos/modules/services/misc/gogs.nix b/nixos/modules/services/misc/gogs.nix index ca8fc06e4835..ad2e36d04d53 100644 --- a/nixos/modules/services/misc/gogs.nix +++ b/nixos/modules/services/misc/gogs.nix @@ -14,7 +14,7 @@ let HOST = ${cfg.database.host}:${toString cfg.database.port} NAME = ${cfg.database.name} USER = ${cfg.database.user} - PASSWD = ${cfg.database.password} + PASSWD = #dbpass# PATH = ${cfg.database.path} [repository] @@ -26,6 +26,10 @@ let HTTP_PORT = ${toString cfg.httpPort} ROOT_URL = ${cfg.rootUrl} + [session] + COOKIE_NAME = session + COOKIE_SECURE = ${boolToString cfg.cookieSecure} + [security] SECRET_KEY = #secretkey# INSTALL_LOCK = true @@ -102,7 +106,21 @@ in password = mkOption { type = types.str; default = ""; - description = "Database password."; + description = '' + The password corresponding to <option>database.user</option>. + Warning: this is stored in cleartext in the Nix store! + Use <option>database.passwordFile</option> instead. + ''; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/gogs-dbpassword"; + description = '' + A file containing the password corresponding to + <option>database.user</option>. + ''; }; path = mkOption { @@ -148,6 +166,15 @@ in description = "HTTP listen port."; }; + cookieSecure = mkOption { + type = types.bool; + default = false; + description = '' + Marks session cookies as "secure" as a hint for browsers to only send + them via HTTPS. This option is recommend, if Gogs is being served over HTTPS. + ''; + }; + extraConfig = mkOption { type = types.str; default = ""; @@ -164,13 +191,25 @@ in wantedBy = [ "multi-user.target" ]; path = [ pkgs.gogs.bin ]; - preStart = '' + preStart = let + runConfig = "${cfg.stateDir}/custom/conf/app.ini"; + secretKey = "${cfg.stateDir}/custom/conf/secret_key"; + in '' # copy custom configuration and generate a random secret key if needed ${optionalString (cfg.useWizard == false) '' mkdir -p ${cfg.stateDir}/custom/conf - cp -f ${configFile} ${cfg.stateDir}/custom/conf/app.ini - KEY=$(head -c 16 /dev/urandom | tr -dc A-Za-z0-9) - sed -i "s,#secretkey#,$KEY,g" ${cfg.stateDir}/custom/conf/app.ini + cp -f ${configFile} ${runConfig} + + if [ ! -e ${secretKey} ]; then + head -c 16 /dev/urandom | base64 > ${secretKey} + fi + + KEY=$(head -n1 ${secretKey}) + DBPASS=$(head -n1 ${cfg.database.passwordFile}) + sed -e "s,#secretkey#,$KEY,g" \ + -e "s,#dbpass#,$DBPASS,g" \ + -i ${runConfig} + chmod 440 ${runConfig} ${secretKey} ''} mkdir -p ${cfg.repositoryRoot} @@ -212,5 +251,16 @@ in }; extraGroups.gogs.gid = config.ids.gids.gogs; }; + + warnings = optional (cfg.database.password != "") + ''config.services.gogs.database.password will be stored as plaintext + in the Nix store. Use database.passwordFile instead.''; + + # Create database passwordFile default when password is configured. + services.gogs.database.passwordFile = mkIf (cfg.database.password != "") + (mkDefault (toString (pkgs.writeTextFile { + name = "gogs-database-password"; + text = cfg.database.password; + }))); }; } diff --git a/nixos/modules/services/misc/jackett.nix b/nixos/modules/services/misc/jackett.nix index e467e7ee85b8..87a41ee70b54 100644 --- a/nixos/modules/services/misc/jackett.nix +++ b/nixos/modules/services/misc/jackett.nix @@ -22,14 +22,14 @@ in echo "Creating jackett data directory in /var/lib/jackett/" mkdir -p /var/lib/jackett/ } - chown -R jackett /var/lib/jackett/ + chown -R jackett:jackett /var/lib/jackett/ chmod 0700 /var/lib/jackett/ ''; serviceConfig = { Type = "simple"; User = "jackett"; - Group = "nogroup"; + Group = "jackett"; PermissionsStartOnly = "true"; ExecStart = "${pkgs.jackett}/bin/Jackett"; Restart = "on-failure"; @@ -37,8 +37,11 @@ in }; users.extraUsers.jackett = { + uid = config.ids.uids.jackett; home = "/var/lib/jackett"; + group = "jackett"; }; + users.extraGroups.jackett.gid = config.ids.gids.jackett; }; } diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix index 9c0bea8d3bff..ecd9a6f52da2 100644 --- a/nixos/modules/services/misc/plex.nix +++ b/nixos/modules/services/misc/plex.nix @@ -91,7 +91,7 @@ in # Copy the database skeleton files to /var/lib/plex/.skeleton # See the the Nix expression for Plex's package for more information on # why this is done. - test -d "${cfg.dataDir}/.skeleton" || mkdir "${cfg.dataDir}/.skeleton" + install --owner ${cfg.user} --group ${cfg.group} -d "${cfg.dataDir}/.skeleton" for db in "com.plexapp.plugins.library.db"; do if [ ! -e "${cfg.dataDir}/.skeleton/$db" ]; then cp "${cfg.package}/usr/lib/plexmediaserver/Resources/base_$db" "${cfg.dataDir}/.skeleton/$db" diff --git a/nixos/modules/services/misc/radarr.nix b/nixos/modules/services/misc/radarr.nix index cc5efffca448..245ad9f9a6df 100644 --- a/nixos/modules/services/misc/radarr.nix +++ b/nixos/modules/services/misc/radarr.nix @@ -22,14 +22,14 @@ in echo "Creating radarr data directory in /var/lib/radarr/" mkdir -p /var/lib/radarr/ } - chown -R radarr /var/lib/radarr/ + chown -R radarr:radarr /var/lib/radarr/ chmod 0700 /var/lib/radarr/ ''; serviceConfig = { Type = "simple"; User = "radarr"; - Group = "nogroup"; + Group = "radarr"; PermissionsStartOnly = "true"; ExecStart = "${pkgs.radarr}/bin/Radarr"; Restart = "on-failure"; @@ -37,8 +37,11 @@ in }; users.extraUsers.radarr = { + uid = config.ids.uids.radarr; home = "/var/lib/radarr"; + group = "radarr"; }; + users.extraGroups.radarr.gid = config.ids.gids.radarr; }; } diff --git a/nixos/modules/services/misc/sonarr.nix b/nixos/modules/services/misc/sonarr.nix index 6d96daa6c3d4..ecde2c33bfa9 100644 --- a/nixos/modules/services/misc/sonarr.nix +++ b/nixos/modules/services/misc/sonarr.nix @@ -22,14 +22,14 @@ in echo "Creating sonarr data directory in /var/lib/sonarr/" mkdir -p /var/lib/sonarr/ } - chown -R sonarr /var/lib/sonarr/ + chown -R sonarr:sonarr /var/lib/sonarr/ chmod 0700 /var/lib/sonarr/ ''; serviceConfig = { Type = "simple"; User = "sonarr"; - Group = "nogroup"; + Group = "sonarr"; PermissionsStartOnly = "true"; ExecStart = "${pkgs.sonarr}/bin/NzbDrone --no-browser"; Restart = "on-failure"; @@ -37,8 +37,11 @@ in }; users.extraUsers.sonarr = { + uid = config.ids.uids.sonarr; home = "/var/lib/sonarr"; + group = "sonarr"; }; + users.extraGroups.sonarr.gid = config.ids.gids.sonarr; }; } diff --git a/nixos/modules/services/monitoring/cadvisor.nix b/nixos/modules/services/monitoring/cadvisor.nix index 8ae8b12056ce..6ca420a05b23 100644 --- a/nixos/modules/services/monitoring/cadvisor.nix +++ b/nixos/modules/services/monitoring/cadvisor.nix @@ -54,7 +54,29 @@ in { storageDriverPassword = mkOption { default = "root"; type = types.str; - description = "Cadvisor storage driver password."; + description = '' + Cadvisor storage driver password. + + Warning: this password is stored in the world-readable Nix store. It's + recommended to use the <option>storageDriverPasswordFile</option> option + since that gives you control over the security of the password. + <option>storageDriverPasswordFile</option> also takes precedence over <option>storageDriverPassword</option>. + ''; + }; + + storageDriverPasswordFile = mkOption { + type = types.str; + description = '' + File that contains the cadvisor storage driver password. + + <option>storageDriverPasswordFile</option> takes precedence over <option>storageDriverPassword</option> + + Warning: when <option>storageDriverPassword</option> is non-empty this defaults to a file in the + world-readable Nix store that contains the value of <option>storageDriverPassword</option>. + + It's recommended to override this with a path not in the Nix store. + Tip: use <link xlink:href='https://nixos.org/nixops/manual/#idm140737318306400'>nixops key management</link> + ''; }; storageDriverSecure = mkOption { @@ -65,35 +87,44 @@ in { }; }; - config = mkIf cfg.enable { - systemd.services.cadvisor = { - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" "docker.service" "influxdb.service" ]; - - postStart = mkBefore '' - until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/containers/'; do - sleep 1; - done - ''; - - serviceConfig = { - ExecStart = ''${pkgs.cadvisor}/bin/cadvisor \ - -logtostderr=true \ - -listen_ip=${cfg.listenAddress} \ - -port=${toString cfg.port} \ - ${optionalString (cfg.storageDriver != null) '' - -storage_driver ${cfg.storageDriver} \ - -storage_driver_user ${cfg.storageDriverHost} \ - -storage_driver_db ${cfg.storageDriverDb} \ - -storage_driver_user ${cfg.storageDriverUser} \ - -storage_driver_password ${cfg.storageDriverPassword} \ - ${optionalString cfg.storageDriverSecure "-storage_driver_secure"} - ''} + config = mkMerge [ + { services.cadvisor.storageDriverPasswordFile = mkIf (cfg.storageDriverPassword != "") ( + mkDefault (toString (pkgs.writeTextFile { + name = "cadvisor-storage-driver-password"; + text = cfg.storageDriverPassword; + })) + ); + } + + (mkIf cfg.enable { + systemd.services.cadvisor = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "docker.service" "influxdb.service" ]; + + postStart = mkBefore '' + until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/containers/'; do + sleep 1; + done ''; - TimeoutStartSec=300; - }; - }; - virtualisation.docker.enable = mkDefault true; - }; + script = '' + exec ${pkgs.cadvisor}/bin/cadvisor \ + -logtostderr=true \ + -listen_ip="${cfg.listenAddress}" \ + -port="${toString cfg.port}" \ + ${optionalString (cfg.storageDriver != null) '' + -storage_driver "${cfg.storageDriver}" \ + -storage_driver_user "${cfg.storageDriverHost}" \ + -storage_driver_db "${cfg.storageDriverDb}" \ + -storage_driver_user "${cfg.storageDriverUser}" \ + -storage_driver_password "$(cat "${cfg.storageDriverPasswordFile}")" \ + ${optionalString cfg.storageDriverSecure "-storage_driver_secure"} + ''} + ''; + + serviceConfig.TimeoutStartSec=300; + }; + virtualisation.docker.enable = mkDefault true; + }) + ]; } diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix index 98931e65bb56..6b24ac2c7c62 100644 --- a/nixos/modules/services/monitoring/graphite.nix +++ b/nixos/modules/services/monitoring/graphite.nix @@ -400,7 +400,8 @@ in { mkdir -p ${cfg.dataDir}/whisper chmod 0700 ${cfg.dataDir}/whisper - chown -R graphite:graphite ${cfg.dataDir} + chown graphite:graphite ${cfg.dataDir} + chown graphite:graphite ${cfg.dataDir}/whisper ''; }; }) @@ -487,9 +488,11 @@ in { # create index ${pkgs.python27Packages.graphite_web}/bin/build-index.sh - touch ${dataDir}/db-created + chown graphite:graphite ${cfg.dataDir} + chown graphite:graphite ${cfg.dataDir}/whisper + chown -R graphite:graphite ${cfg.dataDir}/log - chown -R graphite:graphite ${cfg.dataDir} + touch ${dataDir}/db-created fi ''; }; @@ -526,9 +529,10 @@ in { mkdir -p ${dataDir}/cache/ chmod 0700 ${dataDir}/cache/ - touch ${dataDir}/db-created + chown graphite:graphite ${cfg.dataDir} + chown -R graphite:graphite ${cfg.dataDir}/cache - chown -R graphite:graphite ${cfg.dataDir} + touch ${dataDir}/db-created fi ''; }; @@ -549,7 +553,7 @@ in { preStart = '' if ! test -e ${dataDir}/db-created; then mkdir -p ${dataDir} - chown -R graphite:graphite ${dataDir} + chown graphite:graphite ${dataDir} fi ''; }; diff --git a/nixos/modules/services/monitoring/longview.nix b/nixos/modules/services/monitoring/longview.nix index 770d56e60efb..9c38956f9ba8 100644 --- a/nixos/modules/services/monitoring/longview.nix +++ b/nixos/modules/services/monitoring/longview.nix @@ -5,22 +5,10 @@ with lib; let cfg = config.services.longview; - pidFile = "/run/longview.pid"; - - apacheConf = optionalString (cfg.apacheStatusUrl != "") '' - location ${cfg.apacheStatusUrl}?auto - ''; - mysqlConf = optionalString (cfg.mysqlUser != "") '' - username ${cfg.mysqlUser} - password ${cfg.mysqlPassword} - ''; - nginxConf = optionalString (cfg.nginxStatusUrl != "") '' - location ${cfg.nginxStatusUrl} - ''; - -in - -{ + runDir = "/run/longview"; + configsDir = "${runDir}/longview.d"; + +in { options = { services.longview = { @@ -35,10 +23,27 @@ in apiKey = mkOption { type = types.str; + default = ""; example = "01234567-89AB-CDEF-0123456789ABCDEF"; description = '' Longview API key. To get this, look in Longview settings which are found at https://manager.linode.com/longview/. + + Warning: this secret is stored in the world-readable Nix store! + Use <option>apiKeyFile</option> instead. + ''; + }; + + apiKeyFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/longview-api-key"; + description = '' + A file containing the Longview API key. + To get this, look in Longview settings which + are found at https://manager.linode.com/longview/. + + <option>apiKeyFile</option> takes precedence over <option>apiKey</option>. ''; }; @@ -77,11 +82,23 @@ in mysqlPassword = mkOption { type = types.str; + default = ""; description = '' - The password corresponding to mysqlUser. Warning: this is - stored in cleartext in the Nix store! + The password corresponding to <option>mysqlUser</option>. + Warning: this is stored in cleartext in the Nix store! + Use <option>mysqlPasswordFile</option> instead. ''; }; + + mysqlPasswordFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/keys/dbpassword"; + description = '' + A file containing the password corresponding to <option>mysqlUser</option>. + ''; + }; + }; }; @@ -94,25 +111,50 @@ in serviceConfig.Type = "forking"; serviceConfig.ExecStop = "-${pkgs.coreutils}/bin/kill -TERM $MAINPID"; serviceConfig.ExecReload = "-${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - serviceConfig.PIDFile = pidFile; + serviceConfig.PIDFile = "${runDir}/longview.pid"; serviceConfig.ExecStart = "${pkgs.longview}/bin/longview"; + preStart = '' + umask 077 + mkdir -p ${configsDir} + '' + (optionalString (cfg.apiKeyFile != null) '' + cp --no-preserve=all "${cfg.apiKeyFile}" ${runDir}/longview.key + '') + (optionalString (cfg.apacheStatusUrl != "") '' + cat > ${configsDir}/Apache.conf <<EOF + location ${cfg.apacheStatusUrl}?auto + EOF + '') + (optionalString (cfg.mysqlUser != "" && cfg.mysqlPasswordFile != null) '' + cat > ${configsDir}/MySQL.conf <<EOF + username ${cfg.mysqlUser} + password `head -n1 "${cfg.mysqlPasswordFile}"` + EOF + '') + (optionalString (cfg.nginxStatusUrl != "") '' + cat > ${configsDir}/Nginx.conf <<EOF + location ${cfg.nginxStatusUrl} + EOF + ''); }; - environment.etc."linode/longview.key" = { - mode = "0400"; - text = cfg.apiKey; - }; - environment.etc."linode/longview.d/Apache.conf" = { - mode = "0400"; - text = apacheConf; - }; - environment.etc."linode/longview.d/MySQL.conf" = { - mode = "0400"; - text = mysqlConf; - }; - environment.etc."linode/longview.d/Nginx.conf" = { - mode = "0400"; - text = nginxConf; - }; + warnings = let warn = k: optional (cfg.${k} != "") + "config.services.longview.${k} is insecure. Use ${k}File instead."; + in concatMap warn [ "apiKey" "mysqlPassword" ]; + + assertions = [ + { assertion = cfg.apiKeyFile != null; + message = "Longview needs an API key configured"; + } + ]; + + # Create API key file if not configured. + services.longview.apiKeyFile = mkIf (cfg.apiKey != "") + (mkDefault (toString (pkgs.writeTextFile { + name = "longview.key"; + text = cfg.apiKey; + }))); + + # Create MySQL password file if not configured. + services.longview.mysqlPasswordFile = mkDefault (toString (pkgs.writeTextFile { + name = "mysql-password-file"; + text = cfg.mysqlPassword; + })); }; } diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix index b8c26a5c89ba..b26bcba64059 100644 --- a/nixos/modules/services/monitoring/munin.nix +++ b/nixos/modules/services/monitoring/munin.nix @@ -34,7 +34,7 @@ let cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file) wrapProgram $file \ - --set PATH "/run/wrappers/bin:/run/current-system/sw/bin:/run/current-system/sw/bin" \ + --set PATH "/run/wrappers/bin:/run/current-system/sw/bin" \ --set MUNIN_LIBDIR "${pkgs.munin}/lib" \ --set MUNIN_PLUGSTATE "/var/run/munin" @@ -184,7 +184,7 @@ in mkdir -p /etc/munin/plugins rm -rf /etc/munin/plugins/* - PATH="/run/wrappers/bin:/run/current-system/sw/bin:/run/current-system/sw/bin" ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash + PATH="/run/wrappers/bin:/run/current-system/sw/bin" ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash ''; serviceConfig = { ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/"; diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix index c4c4ed227b35..29dc68f90cc9 100644 --- a/nixos/modules/services/monitoring/ups.nix +++ b/nixos/modules/services/monitoring/ups.nix @@ -80,7 +80,7 @@ let }; config = { - directives = mkHeader ([ + directives = mkOrder 10 ([ "driver = ${config.driver}" "port = ${config.port}" ''desc = "${config.description}"'' diff --git a/nixos/modules/services/networking/aria2.nix b/nixos/modules/services/networking/aria2.nix new file mode 100644 index 000000000000..ad4ac9bf45e3 --- /dev/null +++ b/nixos/modules/services/networking/aria2.nix @@ -0,0 +1,135 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.aria2; + + homeDir = "/var/lib/aria2"; + + 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} + listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)} + rpc-listen-port=${toString cfg.rpcListenPort} + rpc-secret=${cfg.rpcSecret} + ''; + +in +{ + options = { + services.aria2 = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable the headless Aria2 daemon service. + + Aria2 daemon can be controlled via the RPC interface using + one of many WebUI (http://localhost:6800/ by default). + + Targets are downloaded to ${downloadDir} by default and are + accessible to users in the "aria2" group. + ''; + }; + openPorts = mkOption { + type = types.bool; + default = false; + description = '' + Open listen and RPC ports found in listenPortRange and rpcListenPort + options in the firewall. + ''; + }; + downloadDir = mkOption { + type = types.string; + default = "${downloadDir}"; + description = '' + Directory to store downloaded files. + ''; + }; + listenPortRange = mkOption { + type = types.listOf types.attrs; + default = [ { from = 6881; to = 6999; } ]; + description = '' + Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker. + ''; + }; + rpcListenPort = mkOption { + type = types.int; + default = 6800; + description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535"; + }; + rpcSecret = mkOption { + type = types.string; + default = "aria2rpc"; + description = '' + Set RPC secret authorization token. + Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used. + ''; + }; + extraArguments = mkOption { + type = types.string; + example = "--rpc-listen-all --remote-time=true"; + default = ""; + description = '' + Additional arguments to be passed to Aria2. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + # Need to open ports for proper functioning + networking.firewall = mkIf cfg.openPorts { + allowedUDPPortRanges = config.services.aria2.listenPortRange; + allowedTCPPorts = [ config.services.aria2.rpcListenPort ]; + }; + + users.extraUsers.aria2 = { + group = "aria2"; + uid = config.ids.uids.aria2; + description = "aria2 user"; + home = homeDir; + createHome = false; + }; + + users.extraGroups.aria2.gid = config.ids.gids.aria2; + + systemd.services.aria2 = { + description = "aria2 Service"; + after = [ "local-fs.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + mkdir -m 0770 -p "${homeDir}" + chown aria2:aria2 "${homeDir}" + if [[ ! -d "${config.services.aria2.downloadDir}" ]] + then + mkdir -m 0770 -p "${config.services.aria2.downloadDir}" + chown aria2:aria2 "${config.services.aria2.downloadDir}" + fi + if [[ ! -e "${sessionFile}" ]] + then + touch "${sessionFile}" + chown aria2:aria2 "${sessionFile}" + fi + cp -f "${settingsFile}" "${settingsDir}/aria2.conf" + ''; + + serviceConfig = { + Restart = "on-abort"; + ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = "aria2"; + Group = "aria2"; + PermissionsStartOnly = true; + }; + }; + }; +} \ No newline at end of file diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix index 3985f8561d35..f4d0fc822dea 100644 --- a/nixos/modules/services/networking/avahi-daemon.nix +++ b/nixos/modules/services/networking/avahi-daemon.nix @@ -33,6 +33,9 @@ let publish-hinfo=${yesNo publish.hinfo} publish-workstation=${yesNo publish.workstation} publish-domain=${yesNo publish.domain} + + [reflector] + enable-reflector=${yesNo reflector} ''; in @@ -113,6 +116,11 @@ in description = ''Whether to enable wide-area service discovery.''; }; + reflector = mkOption { + default = false; + description = ''Reflect incoming mDNS requests to all allowed network interfaces.''; + }; + publish = { enable = mkOption { default = false; diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix index 24a3196bed46..4e176353fc28 100644 --- a/nixos/modules/services/networking/i2pd.nix +++ b/nixos/modules/services/networking/i2pd.nix @@ -8,8 +8,6 @@ let homeDir = "/var/lib/i2pd"; - extip = "EXTIP=\$(${pkgs.curl.bin}/bin/curl -sLf \"http://jsonip.com\" | ${pkgs.gawk}/bin/awk -F'\"' '{print $4}')"; - mkEndpointOpt = name: addr: port: { enable = mkEnableOption name; name = mkOption { @@ -152,9 +150,8 @@ let i2pdSh = pkgs.writeScriptBin "i2pd" '' #!/bin/sh - ${if isNull cfg.extIp then extip else ""} ${pkgs.i2pd}/bin/i2pd \ - --host=${if isNull cfg.extIp then "$EXTIP" else cfg.extIp} \ + ${if isNull cfg.extIp then "" else "--host="+cfg.extIp} \ --conf=${i2pdConf} \ --tunconf=${i2pdTunnelConf} ''; diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix index 220107a24118..0c12b2c1dfd4 100644 --- a/nixos/modules/services/networking/networkmanager.nix +++ b/nixos/modules/services/networking/networkmanager.nix @@ -12,6 +12,7 @@ let configFile = writeText "NetworkManager.conf" '' [main] plugins=keyfile + dns=${if cfg.useDnsmasq then "dnsmasq" else "default"} [keyfile] ${optionalString (config.networking.hostName != "") @@ -158,6 +159,17 @@ in { ethernet.macAddress = macAddressOpt; wifi.macAddress = macAddressOpt; + useDnsmasq = mkOption { + type = types.bool; + default = false; + description = '' + Enable NetworkManager's dnsmasq integration. NetworkManager will run + dnsmasq as a local caching nameserver, using a "split DNS" + configuration if you are connected to a VPN, and then update + resolv.conf to point to the local nameserver. + ''; + }; + dispatcherScripts = mkOption { type = types.listOf (types.submodule { options = { diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix index f9300fdabc57..ef860e7e5df4 100644 --- a/nixos/modules/services/networking/radicale.nix +++ b/nixos/modules/services/networking/radicale.nix @@ -57,4 +57,6 @@ in serviceConfig.Group = "radicale"; }; }; + + meta.maintainers = with lib.maintainers; [ aneeshusa ]; } diff --git a/nixos/modules/services/networking/xrdp.nix b/nixos/modules/services/networking/xrdp.nix new file mode 100644 index 000000000000..bf59130ce5b9 --- /dev/null +++ b/nixos/modules/services/networking/xrdp.nix @@ -0,0 +1,153 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.xrdp; + confDir = pkgs.runCommand "xrdp.conf" { } '' + mkdir $out + + cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out + + cat > $out/startwm.sh <<EOF + #!/bin/sh + . /etc/profile + ${cfg.defaultWindowManager} + EOF + chmod +x $out/startwm.sh + + substituteInPlace $out/xrdp.ini \ + --replace "#rsakeys_ini=" "rsakeys_ini=/var/run/xrdp/rsakeys.ini" \ + --replace "certificate=" "certificate=${cfg.sslCert}" \ + --replace "key_file=" "key_file=${cfg.sslKey}" \ + --replace LogFile=xrdp.log LogFile=/dev/null \ + --replace EnableSyslog=true EnableSyslog=false + + substituteInPlace $out/sesman.ini \ + --replace LogFile=xrdp-sesman.log LogFile=/dev/null \ + --replace EnableSyslog=1 EnableSyslog=0 + ''; +in +{ + + ###### interface + + options = { + + services.xrdp = { + + enable = mkEnableOption "Whether xrdp should be run on startup."; + + package = mkOption { + type = types.package; + default = pkgs.xrdp; + defaultText = "pkgs.xrdp"; + description = '' + The package to use for the xrdp daemon's binary. + ''; + }; + + port = mkOption { + type = types.int; + default = 3389; + description = '' + Specifies on which port the xrdp daemon listens. + ''; + }; + + sslKey = mkOption { + type = types.str; + default = "/etc/xrdp/key.pem"; + example = "/path/to/your/key.pem"; + description = '' + ssl private key path + A self-signed certificate will be generated if file not exists. + ''; + }; + + sslCert = mkOption { + type = types.str; + default = "/etc/xrdp/cert.pem"; + example = "/path/to/your/cert.pem"; + description = '' + ssl certificate path + A self-signed certificate will be generated if file not exists. + ''; + }; + + defaultWindowManager = mkOption { + type = types.str; + default = "xterm"; + example = "xfce4-session"; + description = '' + The script to run when user log in, usually a window manager, e.g. "icewm", "xfce4-session" + This is per-user overridable, if file ~/startwm.sh exists it will be used instead. + ''; + }; + + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd = { + services.xrdp = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "xrdp daemon"; + requires = [ "xrdp-sesman.service" ]; + preStart = '' + # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp) + mkdir -p /tmp/.xrdp || true + chown xrdp:xrdp /tmp/.xrdp + chmod 3777 /tmp/.xrdp + + # generate a self-signed certificate + if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then + mkdir -p $(dirname ${cfg.sslCert}) || true + mkdir -p $(dirname ${cfg.sslKey}) || true + ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \ + -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \ + -config ${cfg.package}/share/xrdp/openssl.conf \ + -keyout ${cfg.sslKey} -out ${cfg.sslCert} + chown root:xrdp ${cfg.sslKey} ${cfg.sslCert} + chmod 440 ${cfg.sslKey} ${cfg.sslCert} + fi + if [ ! -s /var/run/xrdp/rsakeys.ini ]; then + mkdir -p /var/run/xrdp + ${cfg.package}/bin/xrdp-keygen xrdp /var/run/xrdp/rsakeys.ini + fi + ''; + serviceConfig = { + User = "xrdp"; + Group = "xrdp"; + PermissionsStartOnly = true; + ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini"; + }; + }; + + services.xrdp-sesman = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "xrdp session manager"; + serviceConfig = { + ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini"; + }; + }; + + }; + + users.users.xrdp = { + description = "xrdp daemon user"; + isSystemUser = true; + group = "xrdp"; + }; + users.groups.xrdp = {}; + + security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; }; + }; + +} diff --git a/nixos/modules/services/scheduling/fcron.nix b/nixos/modules/services/scheduling/fcron.nix index bc631bdd0447..af4f9f41fd04 100644 --- a/nixos/modules/services/scheduling/fcron.nix +++ b/nixos/modules/services/scheduling/fcron.nix @@ -149,7 +149,7 @@ in --group fcron \ --directory /var/spool/fcron # load system crontab file - #${pkgs.fcron}/bin/fcrontab -u systab ${pkgs.writeText "systab" cfg.systab} + /run/wrappers/bin/fcrontab -u systab ${pkgs.writeText "systab" cfg.systab} ''; serviceConfig = { diff --git a/nixos/modules/services/security/shibboleth-sp.nix b/nixos/modules/services/security/shibboleth-sp.nix new file mode 100644 index 000000000000..07acf27f0f63 --- /dev/null +++ b/nixos/modules/services/security/shibboleth-sp.nix @@ -0,0 +1,73 @@ +{pkgs, config, lib, ...}: + +with lib; +let + cfg = config.services.shibboleth-sp; +in { + options = { + services.shibboleth-sp = { + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the shibboleth service"; + }; + + configFile = mkOption { + type = types.path; + example = "${pkgs.shibboleth-sp}/etc/shibboleth/shibboleth2.xml"; + description = "Path to shibboleth config file"; + }; + + fastcgi.enable = mkOption { + type = types.bool; + default = false; + description = "Whether to include the shibauthorizer and shibresponder FastCGI processes"; + }; + + fastcgi.shibAuthorizerPort = mkOption { + type = types.int; + default = 9100; + description = "Port for shibauthorizer FastCGI proccess to bind to"; + }; + + fastcgi.shibResponderPort = mkOption { + type = types.int; + default = 9101; + description = "Port for shibauthorizer FastCGI proccess to bind to"; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.shibboleth-sp = { + description = "Provides SSO and federation for web applications"; + after = lib.optionals cfg.fastcgi.enable [ "shibresponder.service" "shibauthorizer.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.shibboleth-sp}/bin/shibd -F -d ${pkgs.shibboleth-sp} -c ${cfg.configFile}"; + }; + }; + + systemd.services.shibresponder = mkIf cfg.fastcgi.enable { + description = "Provides SSO through Shibboleth via FastCGI"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ "${pkgs.spawn_fcgi}" ]; + environment.SHIBSP_CONFIG = "${cfg.configFile}"; + serviceConfig = { + ExecStart = "${pkgs.spawn_fcgi}/bin/spawn-fcgi -n -p ${toString cfg.fastcgi.shibResponderPort} ${pkgs.shibboleth-sp}/lib/shibboleth/shibresponder"; + }; + }; + + systemd.services.shibauthorizer = mkIf cfg.fastcgi.enable { + description = "Provides SSO through Shibboleth via FastCGI"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ "${pkgs.spawn_fcgi}" ]; + environment.SHIBSP_CONFIG = "${cfg.configFile}"; + serviceConfig = { + ExecStart = "${pkgs.spawn_fcgi}/bin/spawn-fcgi -n -p ${toString cfg.fastcgi.shibAuthorizerPort} ${pkgs.shibboleth-sp}/lib/shibboleth/shibauthorizer"; + }; + }; + }; +} diff --git a/nixos/modules/services/security/sshguard.nix b/nixos/modules/services/security/sshguard.nix new file mode 100644 index 000000000000..5a183443f71d --- /dev/null +++ b/nixos/modules/services/security/sshguard.nix @@ -0,0 +1,140 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.sshguard; +in { + + ###### interface + + options = { + + services.sshguard = { + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to enable the sshguard service."; + }; + + attack_threshold = mkOption { + default = 30; + type = types.int; + description = '' + Block attackers when their cumulative attack score exceeds threshold. Most attacks have a score of 10. + ''; + }; + + blacklist_threshold = mkOption { + default = null; + example = 120; + type = types.nullOr types.int; + description = '' + Blacklist an attacker when its score exceeds threshold. Blacklisted addresses are loaded from and added to blacklist-file. + ''; + }; + + blacklist_file = mkOption { + default = "/var/lib/sshguard/blacklist.db"; + type = types.path; + description = '' + Blacklist an attacker when its score exceeds threshold. Blacklisted addresses are loaded from and added to blacklist-file. + ''; + }; + + blocktime = mkOption { + default = 120; + type = types.int; + description = '' + Block attackers for initially blocktime seconds after exceeding threshold. Subsequent blocks increase by a factor of 1.5. + + sshguard unblocks attacks at random intervals, so actual block times will be longer. + ''; + }; + + detection_time = mkOption { + default = 1800; + type = types.int; + description = '' + Remember potential attackers for up to detection_time seconds before resetting their score. + ''; + }; + + whitelist = mkOption { + default = [ ]; + example = [ "198.51.100.56" "198.51.100.2" ]; + type = types.listOf types.str; + description = '' + Whitelist a list of addresses, hostnames, or address blocks. + ''; + }; + + services = mkOption { + default = [ "sshd" ]; + example = [ "sshd" "exim" ]; + type = types.listOf types.str; + description = '' + Systemd services sshguard should receive logs of. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.sshguard pkgs.iptables pkgs.ipset ]; + + environment.etc."sshguard.conf".text = let + list_services = ( name: "-t ${name} "); + in '' + BACKEND="${pkgs.sshguard}/libexec/sshg-fw-ipset" + LOGREADER="LANG=C ${pkgs.systemd}/bin/journalctl -afb -p info -n1 ${toString (map list_services cfg.services)} -o cat" + ''; + + systemd.services.sshguard = + { description = "SSHGuard brute-force attacks protection system"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + partOf = optional config.networking.firewall.enable "firewall.service"; + + path = [ pkgs.iptables pkgs.ipset pkgs.iproute pkgs.systemd ]; + + postStart = '' + mkdir -p /var/lib/sshguard + ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard4 hash:ip family inet + ${pkgs.ipset}/bin/ipset -quiet create -exist sshguard6 hash:ip family inet6 + ${pkgs.iptables}/bin/iptables -I INPUT -m set --match-set sshguard4 src -j DROP + ${pkgs.iptables}/bin/ip6tables -I INPUT -m set --match-set sshguard6 src -j DROP + ''; + + preStop = '' + ${pkgs.iptables}/bin/iptables -D INPUT -m set --match-set sshguard4 src -j DROP + ${pkgs.iptables}/bin/ip6tables -D INPUT -m set --match-set sshguard6 src -j DROP + ''; + + unitConfig.Documentation = "man:sshguard(8)"; + + serviceConfig = { + Type = "simple"; + ExecStart = let + list_whitelist = ( name: "-w ${name} "); + in '' + ${pkgs.sshguard}/bin/sshguard -a ${toString cfg.attack_threshold} ${optionalString (cfg.blacklist_threshold != null) "-b ${toString cfg.blacklist_threshold}:${cfg.blacklist_file} "}-i /run/sshguard/sshguard.pid -p ${toString cfg.blocktime} -s ${toString cfg.detection_time} ${toString (map list_whitelist cfg.whitelist)} + ''; + PIDFile = "/run/sshguard/sshguard.pid"; + Restart = "always"; + + ReadOnlyDirectories = "/"; + ReadWriteDirectories = "/run/sshguard /var/lib/sshguard"; + RuntimeDirectory = "sshguard"; + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW"; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixos/modules/services/web-apps/atlassian/confluence.nix index 2d9287577de8..c1d7d4ea06d4 100644 --- a/nixos/modules/services/web-apps/atlassian/confluence.nix +++ b/nixos/modules/services/web-apps/atlassian/confluence.nix @@ -103,7 +103,7 @@ in requires = [ "postgresql.service" ]; after = [ "postgresql.service" ]; - path = [ cfg.jrePackage ]; + path = [ cfg.jrePackage pkgs.bash ]; environment = { CONF_USER = cfg.user; diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix index 1ed489bcb095..405dc0307fac 100644 --- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix +++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix @@ -91,12 +91,16 @@ let }; skins = config.skins; + extensions = config.extensions; buildPhase = '' for skin in $skins; do cp -prvd $skin/* skins/ done + for extension in $extensions; do + cp -prvd $extension/* extensions/ + done ''; # */ installPhase = @@ -287,6 +291,16 @@ in ''; }; + extensions = mkOption { + default = []; + type = types.listOf types.path; + description = + '' + List of paths whose content is copied to the 'extensions' + subdirectory of the MediaWiki installation. + ''; + }; + extraConfig = mkOption { type = types.lines; default = ""; diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index af01f6acad18..d56050c36269 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -8,7 +8,7 @@ let cfg = xcfg.desktopManager; # If desktop manager `d' isn't capable of setting a background and - # the xserver is enabled, the `feh' program is used as a fallback. + # the xserver is enabled, `feh' or `xsetroot' are used as a fallback. needBGCond = d: ! (d ? bgSupport && d.bgSupport) && xcfg.enable; in @@ -44,8 +44,11 @@ in manage = "desktop"; start = d.start + optionalString (needBGCond d) '' - if test -e $HOME/.background-image; then + if [ -e $HOME/.background-image ]; then ${pkgs.feh}/bin/feh --bg-scale $HOME/.background-image + else + # Use a solid black background as fallback + ${pkgs.xorg.xsetroot}/bin/xsetroot -solid black fi ''; }) list; @@ -80,6 +83,6 @@ in config = { services.xserver.displayManager.session = cfg.session.list; environment.systemPackages = - mkIf cfg.session.needBGPackages [ pkgs.feh ]; + mkIf cfg.session.needBGPackages [ pkgs.feh ]; # xsetroot via xserver.enable }; } diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix index d981cd5328e1..2216104be31a 100644 --- a/nixos/modules/services/x11/desktop-managers/plasma5.nix +++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix @@ -183,6 +183,7 @@ in environment.variables = { # Enable GTK applications to load SVG icons GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"; + QT_PLUGIN_PATH = "/run/current-system/sw/lib/qt5/plugins"; }; fonts.fonts = with pkgs; [ noto-fonts hack-font ]; diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 543fd9399147..cf6efb7dae79 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -32,14 +32,32 @@ let '' #! ${pkgs.bash}/bin/bash - # SDDM splits "Exec" line in .desktop file by whitespace and pass script path as $1 - if [[ "$0" = "$1" ]]; then - # remove superfluous $1 again - shift - # join arguments again and evaluate them in a shell context - # to interpret shell quoting - eval exec "$0" "$@" - fi + # Expected parameters: + # $1 = <desktop-manager>+<window-manager> + + # Actual parameters (FIXME): + # SDDM is calling this script like the following: + # $1 = /nix/store/xxx-xsession (= $0) + # $2 = <desktop-manager>+<window-manager> + # SLiM is using the following parameter: + # $1 = /nix/store/xxx-xsession <desktop-manager>+<window-manager> + # LightDM keeps the double quotes: + # $1 = /nix/store/xxx-xsession "<desktop-manager>+<window-manager>" + # The fake/auto display manager doesn't use any parameters and GDM is + # broken. + # If you want to "debug" this script don't print the parameters to stdout + # or stderr because this script will be executed multiple times and the + # output won't be visible in the log when the script is executed for the + # first time (e.g. append them to a file instead)! + + # All of the above cases are handled by the following hack (FIXME). + # Since this line is *very important* for *all display managers* it is + # very important to test changes to the following line with all display + # managers: + if [ "''${1:0:1}" = "/" ]; then eval exec "$1" "$2" ; fi + + # Now it should be safe to assume that the script was called with the + # expected parameters. ${optionalString cfg.displayManager.logToJournal '' if [ -z "$_DID_SYSTEMD_CAT" ]; then @@ -113,15 +131,16 @@ let fi fi - # The session type is "<desktop-manager> + <window-manager>", so - # extract those. - windowManager="''${sessionType##* + }" + # The session type is "<desktop-manager>+<window-manager>", so + # extract those (see: + # http://wiki.bash-hackers.org/syntax/pe#substring_removal). + windowManager="''${sessionType##*+}" : ''${windowManager:=${cfg.windowManager.default}} - desktopManager="''${sessionType% + *}" + desktopManager="''${sessionType%%+*}" : ''${desktopManager:=${cfg.desktopManager.default}} # Start the window manager. - case $windowManager in + case "$windowManager" in ${concatMapStrings (s: '' (${s.name}) ${s.start} @@ -131,7 +150,7 @@ let esac # Start the desktop manager. - case $desktopManager in + case "$desktopManager" in ${concatMapStrings (s: '' (${s.name}) ${s.start} @@ -148,6 +167,9 @@ let exit 0 ''; + # Desktop Entry Specification: + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html mkDesktops = names: pkgs.runCommand "desktops" { # trivial derivation preferLocalBuild = true; @@ -161,7 +183,7 @@ let Version=1.0 Type=XSession TryExec=${cfg.displayManager.session.script} - Exec=${cfg.displayManager.session.script} '${n}' + Exec=${cfg.displayManager.session.script} "${n}" X-GDM-BypassXsession=true Name=${n} Comment= @@ -244,7 +266,7 @@ in wm = filter (s: s.manage == "window") list; dm = filter (s: s.manage == "desktop") list; names = flip concatMap dm - (d: map (w: d.name + optionalString (w.name != "none") (" + " + w.name)) + (d: map (w: d.name + optionalString (w.name != "none") ("+" + w.name)) (filter (w: d.name != "none" || w.name != "none") wm)); desktops = mkDesktops names; script = xsession wm dm; diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index 82b9a2fce5ab..256bfb9ce3f4 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -61,7 +61,7 @@ let let dm = xcfg.desktopManager.default; wm = xcfg.windowManager.default; - in dm + optionalString (wm != "none") (" + " + wm); + in dm + optionalString (wm != "none") ("+" + wm); in { # Note: the order in which lightdm greeter modules are imported diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 99c03ca81c2d..2eb7ddcb1ec0 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -69,7 +69,7 @@ let let dm = xcfg.desktopManager.default; wm = xcfg.windowManager.default; - in dm + optionalString (wm != "none") (" + " + wm); + in dm + optionalString (wm != "none") ("+" + wm); in { diff --git a/nixos/modules/services/x11/display-managers/slim.nix b/nixos/modules/services/x11/display-managers/slim.nix index 05b979eef47f..0c4dd1973b53 100644 --- a/nixos/modules/services/x11/display-managers/slim.nix +++ b/nixos/modules/services/x11/display-managers/slim.nix @@ -17,6 +17,7 @@ let login_cmd exec ${pkgs.stdenv.shell} ${dmcfg.session.script} "%session" halt_cmd ${config.systemd.package}/sbin/shutdown -h now reboot_cmd ${config.systemd.package}/sbin/shutdown -r now + logfile /dev/stderr ${optionalString (cfg.defaultUser != null) ("default_user " + cfg.defaultUser)} ${optionalString (cfg.defaultUser != null) ("focus_password yes")} ${optionalString cfg.autoLogin "auto_login yes"} @@ -128,11 +129,7 @@ in config = mkIf cfg.enable { services.xserver.displayManager.job = - { preStart = - '' - rm -f /var/log/slim.log - ''; - environment = + { environment = { SLIM_CFGFILE = slimConfig; SLIM_THEMESDIR = slimThemesDir; }; diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 8438e6dcc702..09fcdd0b72ba 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -31,18 +31,51 @@ let pkgs.xorg.fontadobe75dpi ]; + xrandrOptions = { + output = mkOption { + type = types.str; + example = "DVI-0"; + description = '' + The output name of the monitor, as shown by <citerefentry> + <refentrytitle>xrandr</refentrytitle> + <manvolnum>1</manvolnum> + </citerefentry> invoked without arguments. + ''; + }; + + primary = mkOption { + type = types.bool; + default = false; + description = '' + Whether this head is treated as the primary monitor, + ''; + }; + + monitorConfig = mkOption { + type = types.lines; + default = ""; + example = '' + DisplaySize 408 306 + Option "DPMS" "false" + ''; + description = '' + Extra lines to append to the <literal>Monitor</literal> section + verbatim. + ''; + }; + }; # Just enumerate all heads without discarding XRandR output information. xrandrHeads = let - mkHead = num: output: { + mkHead = num: config: { name = "multihead${toString num}"; - inherit output; + inherit config; }; in imap mkHead cfg.xrandrHeads; xrandrDeviceSection = let monitors = flip map xrandrHeads (h: '' - Option "monitor-${h.output}" "${h.name}" + Option "monitor-${h.config.output}" "${h.name}" ''); # First option is indented through the space in the config but any # subsequent options aren't so we need to apply indentation to @@ -62,9 +95,13 @@ let value = '' Section "Monitor" Identifier "${current.name}" + ${optionalString (current.config.primary) '' + Option "Primary" "true" + ''} ${optionalString (previous != []) '' Option "RightOf" "${(head previous).name}" ''} + ${current.config.monitorConfig} EndSection ''; } ++ previous; @@ -258,7 +295,7 @@ in type = types.str; default = "us"; description = '' - Keyboard layout. + Keyboard layout, or multiple keyboard layouts separated by commas. ''; }; @@ -329,13 +366,39 @@ in xrandrHeads = mkOption { default = []; - example = [ "HDMI-0" "DVI-0" ]; - type = with types; listOf string; + example = [ + "HDMI-0" + { output = "DVI-0"; primary = true; } + { output = "DVI-1"; monitorConfig = "Option \"Rotate\" \"left\""; } + ]; + type = with types; listOf (coercedTo str (output: { + inherit output; + }) (submodule { options = xrandrOptions; })); + # Set primary to true for the first head if no other has been set + # primary already. + apply = heads: let + hasPrimary = any (x: x.primary) heads; + firstPrimary = head heads // { primary = true; }; + newHeads = singleton firstPrimary ++ tail heads; + in if heads != [] && !hasPrimary then newHeads else heads; description = '' - Simple multiple monitor configuration, just specify a list of XRandR - outputs which will be mapped from left to right in the order of the + Multiple monitor configuration, just specify a list of XRandR + outputs. The individual elements should be either simple strings or + an attribute set of output options. + + If the element is a string, it is denoting the physical output for a + monitor, if it's an attribute set, you must at least provide the + <option>output</option> option. + + The monitors will be mapped from left to right in the order of the list. + By default, the first monitor will be set as the primary monitor if + none of the elements contain an option that has set + <option>primary</option> to <literal>true</literal>. + + <note><para>Only one monitor is allowed to be primary.</para></note> + Be careful using this option with multiple graphic adapters or with drivers that have poor support for XRandR, unexpected things might happen with those. @@ -469,11 +532,18 @@ in nixpkgs.config.xorg = optionalAttrs (elem "vboxvideo" cfg.videoDrivers) { abiCompat = "1.18"; }; - assertions = - [ { assertion = config.security.polkit.enable; - message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’)."; - } - ]; + assertions = [ + { assertion = config.security.polkit.enable; + message = "X11 requires Polkit to be enabled (‘security.polkit.enable = true’)."; + } + (let primaryHeads = filter (x: x.primary) cfg.xrandrHeads; in { + assertion = length primaryHeads < 2; + message = "Only one head is allowed to be primary in " + + "‘services.xserver.xrandrHeads’, but there are " + + "${toString (length primaryHeads)} heads set to primary: " + + concatMapStringsSep ", " (x: x.output) primaryHeads; + }) + ]; environment.etc = (optionals cfg.exportConfiguration @@ -578,6 +648,35 @@ in services.xserver.xkbDir = mkDefault "${pkgs.xkeyboard_config}/etc/X11/xkb"; + system.extraDependencies = [ + (pkgs.runCommand "xkb-layouts-exist" { + layouts=cfg.layout; + } '' + missing=() + while read -d , layout + do + [[ -f "${cfg.xkbDir}/symbols/$layout" ]] || missing+=($layout) + done <<< "$layouts," + if [[ ''${#missing[@]} -eq 0 ]] + then + touch $out + exit 0 + fi + + cat >&2 <<EOF + + Some of the selected keyboard layouts do not exist: + + ''${missing[@]} + + Set services.xserver.layout to the name of an existing keyboard + layout (check ${cfg.xkbDir}/symbols for options). + + EOF + exit -1 + '') + ]; + services.xserver.config = '' Section "ServerFlags" diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index 2d6bf2d58a9e..cf47aed9fa99 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -239,6 +239,12 @@ in menuentry "Windows 7" { chainloader (hd0,4)+1 } + + # GRUB 2 with UEFI example, chainloading another distro + menuentry "Fedora" { + set root=(hd1,1) + chainloader /efi/fedora/grubx64.efi + } ''; description = '' Any additional entries you want added to the GRUB boot menu. diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index c75e637124a9..9a125dcb0aeb 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -154,6 +154,9 @@ for o in $(cat /proc/cmdline); do fi ln -s "$root" /dev/root ;; + copytoram) + copytoram=1 + ;; esac done @@ -474,6 +477,22 @@ while read -u 3 mountPoint; do # doing something with $device right now. udevadm settle + # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs. + if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then + fsType=$(blkid -o value -s TYPE "$device") + fsSize=$(blockdev --getsize64 "$device") + + mkdir -p /tmp-iso + mount -t "$fsType" /dev/root /tmp-iso + mountFS tmpfs /iso size="$fsSize" tmpfs + + cp -r /tmp-iso/* /mnt-root/iso/ + + umount /tmp-iso + rmdir /tmp-iso + continue + fi + mountFS "$device" "$mountPoint" "$options" "$fsType" done diff --git a/nixos/modules/tasks/trackpoint.nix b/nixos/modules/tasks/trackpoint.nix index 32e69dd2bf58..1f8f2891e98c 100644 --- a/nixos/modules/tasks/trackpoint.nix +++ b/nixos/modules/tasks/trackpoint.nix @@ -81,7 +81,7 @@ with lib; services.xserver.inputClassSections = ['' Identifier "Trackpoint Wheel Emulation" - MatchProduct "${if cfg.fakeButtons then "PS/2 Generic Mouse" else "Elantech PS/2 TrackPoint|TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint"}" + MatchProduct "${if cfg.fakeButtons then "PS/2 Generic Mouse" else "ETPS/2 Elantech TrackPoint|Elantech PS/2 TrackPoint|TPPS/2 IBM TrackPoint|DualPoint Stick|Synaptics Inc. Composite TouchPad / TrackPoint|ThinkPad USB Keyboard with TrackPoint|USB Trackpoint pointing device|Composite TouchPad / TrackPoint"}" MatchDevicePath "/dev/input/event*" Option "EmulateWheel" "true" Option "EmulateWheelButton" "2" diff --git a/nixos/modules/virtualisation/azure-image.nix b/nixos/modules/virtualisation/azure-image.nix index 33f84986cac6..cb756842f369 100644 --- a/nixos/modules/virtualisation/azure-image.nix +++ b/nixos/modules/virtualisation/azure-image.nix @@ -2,93 +2,19 @@ with lib; let - diskSize = "30720"; + diskSize = 30720; in { - system.build.azureImage = - pkgs.vmTools.runInLinuxVM ( - pkgs.runCommand "azure-image" - { preVM = - '' - mkdir $out - diskImage=$out/$diskImageBase - - cyl=$(((${diskSize}*1024*1024)/(512*63*255))) - size=$(($cyl*255*63*512)) - roundedsize=$((($size/(1024*1024)+1)*(1024*1024))) - ${pkgs.vmTools.qemu-220}/bin/qemu-img create -f raw $diskImage $roundedsize - mv closure xchg/ - ''; - - postVM = - '' - mkdir -p $out - ${pkgs.vmTools.qemu-220}/bin/qemu-img convert -f raw -o subformat=fixed -O vpc $diskImage $out/disk.vhd - rm $diskImage - ''; - diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw"; - buildInputs = [ pkgs.utillinux pkgs.perl ]; - exportReferencesGraph = - [ "closure" config.system.build.toplevel ]; - } - '' - # Create partition table - ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos - ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 ${diskSize}M - ${pkgs.parted}/sbin/parted /dev/vda print - . /sys/class/block/vda1/uevent - mknod /dev/vda1 b $MAJOR $MINOR - - # Create an empty filesystem and mount it. - ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1 - ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1 - - mkdir /mnt - mount /dev/vda1 /mnt - - # The initrd expects these directories to exist. - mkdir /mnt/dev /mnt/proc /mnt/sys - - mount --bind /proc /mnt/proc - mount --bind /dev /mnt/dev - mount --bind /sys /mnt/sys - - # Copy all paths in the closure to the filesystem. - storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure) - - mkdir -p /mnt/nix/store - echo "copying everything (will take a while)..." - cp -prd $storePaths /mnt/nix/store/ - - echo Register the paths in the Nix database. - printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \ - chroot /mnt ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group "" - - echo Create the system profile to allow nixos-rebuild to work. - chroot /mnt ${config.nix.package.out}/bin/nix-env \ - -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} --option build-users-group "" - - echo nixos-rebuild requires an /etc/NIXOS. - mkdir -p /mnt/etc - touch /mnt/etc/NIXOS - - echo switch-to-configuration requires a /bin/sh - mkdir -p /mnt/bin - ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh - - echo Install a configuration.nix. - mkdir -p /mnt/etc/nixos /mnt/boot/grub - cp ${./azure-config-user.nix} /mnt/etc/nixos/configuration.nix - - echo Generate the GRUB menu. - ln -s vda /dev/sda - chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot - - echo Almost done - umount /mnt/proc /mnt/dev /mnt/sys - umount /mnt - '' - ); + system.build.azureImage = import ../../lib/make-disk-image.nix { + name = "azure-image"; + postVM = '' + ${pkgs.vmTools.qemu-220}/bin/qemu-img convert -f raw -o subformat=fixed -O vpc $diskImage $out/disk.vhd + ''; + configFile = ./azure-config-user.nix; + format = "raw"; + inherit diskSize; + inherit config lib pkgs; + }; imports = [ ./azure-common.nix ]; diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix index f1101d7ea66e..c26cae06cd1d 100644 --- a/nixos/modules/virtualisation/docker.nix +++ b/nixos/modules/virtualisation/docker.nix @@ -7,8 +7,7 @@ with lib; let cfg = config.virtualisation.docker; - pro = config.networking.proxy.default; - proxy_env = optionalAttrs (pro != null) { Environment = "\"http_proxy=${pro}\""; }; + proxy_env = config.networking.proxy.envVars; in @@ -106,6 +105,7 @@ in systemd.services.docker = { wantedBy = optional cfg.enableOnBoot "multi-user.target"; + environment = proxy_env; serviceConfig = { ExecStart = [ "" @@ -122,7 +122,7 @@ in "" "${pkgs.procps}/bin/kill -s HUP $MAINPID" ]; - } // proxy_env; + }; path = [ pkgs.kmod ] ++ (optional (cfg.storageDriver == "zfs") pkgs.zfs); }; diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix index 5673d55b3394..ff39f1bf8dae 100644 --- a/nixos/modules/virtualisation/google-compute-image.nix +++ b/nixos/modules/virtualisation/google-compute-image.nix @@ -2,7 +2,7 @@ with lib; let - diskSize = "1G"; + diskSize = 1024; # MB in { imports = [ ../profiles/headless.nix ../profiles/qemu-guest.nix ./grow-partition.nix ]; @@ -10,89 +10,21 @@ in # https://cloud.google.com/compute/docs/tutorials/building-images networking.firewall.enable = mkDefault false; - system.build.googleComputeImage = - pkgs.vmTools.runInLinuxVM ( - pkgs.runCommand "google-compute-image" - { preVM = - '' - mkdir $out - diskImage=$out/$diskImageBase - truncate $diskImage --size ${diskSize} - mv closure xchg/ - ''; - - postVM = - '' - PATH=$PATH:${pkgs.stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]} - pushd $out - mv $diskImageBase disk.raw - tar -Szcf $diskImageBase.tar.gz disk.raw - rm $out/disk.raw - popd - ''; - diskImageBase = "nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw"; - buildInputs = [ pkgs.utillinux pkgs.perl ]; - exportReferencesGraph = - [ "closure" config.system.build.toplevel ]; - } - '' - # Create partition table - ${pkgs.parted}/sbin/parted /dev/vda mklabel msdos - ${pkgs.parted}/sbin/parted /dev/vda mkpart primary ext4 1 ${diskSize} - ${pkgs.parted}/sbin/parted /dev/vda print - . /sys/class/block/vda1/uevent - mknod /dev/vda1 b $MAJOR $MINOR - - # Create an empty filesystem and mount it. - ${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1 - ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1 - - mkdir /mnt - mount /dev/vda1 /mnt - - # The initrd expects these directories to exist. - mkdir /mnt/dev /mnt/proc /mnt/sys - - mount --bind /proc /mnt/proc - mount --bind /dev /mnt/dev - mount --bind /sys /mnt/sys - - # Copy all paths in the closure to the filesystem. - storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure) - - mkdir -p /mnt/nix/store - echo "copying everything (will take a while)..." - ${pkgs.rsync}/bin/rsync -a $storePaths /mnt/nix/store/ - - # Register the paths in the Nix database. - printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \ - chroot /mnt ${config.nix.package.out}/bin/nix-store --load-db --option build-users-group "" - - # Create the system profile to allow nixos-rebuild to work. - chroot /mnt ${config.nix.package.out}/bin/nix-env \ - -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} \ - --option build-users-group "" - - # `nixos-rebuild' requires an /etc/NIXOS. - mkdir -p /mnt/etc - touch /mnt/etc/NIXOS - - # `switch-to-configuration' requires a /bin/sh - mkdir -p /mnt/bin - ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh - - # Install a configuration.nix. - mkdir -p /mnt/etc/nixos /mnt/boot/grub - cp ${./google-compute-config.nix} /mnt/etc/nixos/configuration.nix - - # Generate the GRUB menu. - ln -s vda /dev/sda - chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot - - umount /mnt/proc /mnt/dev /mnt/sys - umount /mnt - '' - ); + system.build.googleComputeImage = import ../../lib/make-disk-image.nix { + name = "google-compute-image"; + postVM = '' + PATH=$PATH:${pkgs.stdenv.lib.makeBinPath [ pkgs.gnutar pkgs.gzip ]} + pushd $out + mv $diskImage disk.raw + tar -Szcf nixos-image-${config.system.nixosLabel}-${pkgs.stdenv.system}.raw.tar.gz disk.raw + rm $out/disk.raw + popd + ''; + configFile = ./google-compute-config.nix; + format = "raw"; + inherit diskSize; + inherit config lib pkgs; + }; fileSystems."/" = { device = "/dev/disk/by-label/nixos"; diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index 1933f11d1fff..c75edfcd8cf6 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -75,6 +75,7 @@ let exec ${qemu}/bin/qemu-kvm \ -name ${vmName} \ -m ${toString config.virtualisation.memorySize} \ + -smp ${toString config.virtualisation.cores} \ ${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \ ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \ -virtfs local,path=/nix/store,security_model=none,mount_tag=store \ @@ -244,6 +245,18 @@ in ''; }; + virtualisation.cores = + mkOption { + default = 1; + type = types.int; + description = + '' + Specify the number of cores the guest is permitted to use. + The number can be higher than the available cores on the + host system. + ''; + }; + virtualisation.pathsInNixDB = mkOption { default = []; diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index 5c1112a1c6d6..4217f5940ec6 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -94,6 +94,7 @@ in rec { (all nixos.tests.proxy) (all nixos.tests.sddm.default) (all nixos.tests.simple) + (all nixos.tests.slim) (all nixos.tests.udisks2) (all nixos.tests.xfce) diff --git a/nixos/release.nix b/nixos/release.nix index 95b284cb7056..aaf23d7ffb79 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -248,7 +248,7 @@ in rec { tests.gocd-server = callTest tests/gocd-server.nix {}; tests.gnome3 = callTest tests/gnome3.nix {}; tests.gnome3-gdm = callTest tests/gnome3-gdm.nix {}; - tests.grsecurity = callTest tests/grsecurity.nix {}; + tests.hardened = callTest tests/hardened.nix { }; tests.hibernate = callTest tests/hibernate.nix {}; tests.hound = callTest tests/hound.nix {}; tests.i3wm = callTest tests/i3wm.nix {}; @@ -299,6 +299,7 @@ in rec { tests.samba = callTest tests/samba.nix {}; tests.sddm = callSubTests tests/sddm.nix {}; tests.simple = callTest tests/simple.nix {}; + tests.slim = callTest tests/slim.nix {}; tests.smokeping = callTest tests/smokeping.nix {}; tests.taskserver = callTest tests/taskserver.nix {}; tests.tomcat = callTest tests/tomcat.nix {}; diff --git a/nixos/tests/grsecurity.nix b/nixos/tests/grsecurity.nix deleted file mode 100644 index d4a419fd0e39..000000000000 --- a/nixos/tests/grsecurity.nix +++ /dev/null @@ -1,46 +0,0 @@ -# Basic test to make sure grsecurity works - -import ./make-test.nix ({ pkgs, ...} : { - name = "grsecurity"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ copumpkin joachifm ]; - }; - - machine = { config, pkgs, ... }: - { security.grsecurity.enable = true; - boot.kernel.sysctl."kernel.grsecurity.audit_mount" = 0; - boot.kernel.sysctl."kernel.grsecurity.deter_bruteforce" = 0; - networking.useDHCP = false; - }; - - testScript = '' - subtest "grsec-lock", sub { - $machine->succeed("systemctl is-active grsec-lock"); - $machine->succeed("grep -Fq 1 /proc/sys/kernel/grsecurity/grsec_lock"); - $machine->fail("echo -n 0 >/proc/sys/kernel/grsecurity/grsec_lock"); - }; - - subtest "paxtest", sub { - # TODO: running paxtest blackhat hangs the vm - my @pax_mustkill = ( - "anonmap", "execbss", "execdata", "execheap", "execstack", - "mprotanon", "mprotbss", "mprotdata", "mprotheap", "mprotstack", - ); - foreach my $name (@pax_mustkill) { - my $paxtest = "${pkgs.paxtest}/lib/paxtest/" . $name; - $machine->succeed($paxtest) =~ /Killed/ or die - } - }; - - # tcc -run executes run-time generated code and so allows us to test whether - # paxmark actually works (otherwise, the process should be terminated) - subtest "tcc", sub { - $machine->execute("echo -e '#include <stdio.h>\nint main(void) { puts(\"hello\"); return 0; }' >main.c"); - $machine->succeed("${pkgs.tinycc}/bin/tcc -run main.c"); - }; - - subtest "RBAC", sub { - $machine->succeed("[ -c /dev/grsec ]"); - }; - ''; -}) diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix new file mode 100644 index 000000000000..1d9a9043e03a --- /dev/null +++ b/nixos/tests/hardened.nix @@ -0,0 +1,36 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "hardened"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ joachifm ]; + }; + + machine = + { config, lib, pkgs, ... }: + with lib; + { users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; }; + users.users.sybil = { isNormalUser = true; group = "wheel"; }; + imports = [ ../modules/profiles/hardened.nix ]; + }; + + testScript = + '' + # Test hidepid + subtest "hidepid", sub { + $machine->succeed("grep -Fq hidepid=2 /proc/mounts"); + $machine->succeed("[ `su - sybil -c 'pgrep -c -u root'` = 0 ]"); + $machine->succeed("[ `su - alice -c 'pgrep -c -u root'` != 0 ]"); + }; + + # Test kernel module hardening + subtest "lock-modules", sub { + $machine->waitForUnit("multi-user.target"); + # note: this better a be module we normally wouldn't load ... + $machine->fail("modprobe dccp"); + }; + + # Test userns + subtest "userns", sub { + $machine->fail("unshare --user"); + }; + ''; +}) diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix index cd4086cb8f62..b926a62194b4 100644 --- a/nixos/tests/misc.nix +++ b/nixos/tests/misc.nix @@ -25,8 +25,6 @@ import ./make-test.nix ({ pkgs, ...} : { }; users.users.sybil = { isNormalUser = true; group = "wheel"; }; security.sudo = { enable = true; wheelNeedsPassword = false; }; - security.hideProcessInformation = true; - users.users.alice = { isNormalUser = true; extraGroups = [ "proc" ]; }; }; testScript = @@ -119,12 +117,5 @@ import ./make-test.nix ({ pkgs, ...} : { subtest "sudo", sub { $machine->succeed("su - sybil -c 'sudo true'"); }; - - # Test hidepid - subtest "hidepid", sub { - $machine->succeed("grep -Fq hidepid=2 /etc/mtab"); - $machine->succeed("[ `su - sybil -c 'pgrep -c -u root'` = 0 ]"); - $machine->succeed("[ `su - alice -c 'pgrep -c -u root'` != 0 ]"); - }; ''; }) diff --git a/nixos/tests/mysql-replication.nix b/nixos/tests/mysql-replication.nix index b20bce8edce6..75c6d793febc 100644 --- a/nixos/tests/mysql-replication.nix +++ b/nixos/tests/mysql-replication.nix @@ -56,11 +56,19 @@ in testScript = '' $master->start; $master->waitForUnit("mysql"); + $master->waitForOpenPort(3306); $slave1->start; $slave2->start; $slave1->waitForUnit("mysql"); + $slave1->waitForOpenPort(3306); $slave2->waitForUnit("mysql"); - $slave2->sleep(100); # Hopefully this is long enough!! + $slave2->waitForOpenPort(3306); $slave2->succeed("echo 'use testdb; select * from tests' | mysql -u root -N | grep 4"); + $slave2->succeed("systemctl stop mysql"); + $master->succeed("echo 'insert into testdb.tests values (123, 456);' | mysql -u root -N"); + $slave2->succeed("systemctl start mysql"); + $slave2->waitForUnit("mysql"); + $slave2->waitForOpenPort(3306); + $slave2->succeed("echo 'select * from testdb.tests where Id = 123;' | mysql -u root -N | grep 456"); ''; }) diff --git a/nixos/tests/radicale.nix b/nixos/tests/radicale.nix new file mode 100644 index 000000000000..4c2ed8456ddd --- /dev/null +++ b/nixos/tests/radicale.nix @@ -0,0 +1,80 @@ +let + port = 5232; + radicaleOverlay = self: super: { + radicale = super.radicale.overrideAttrs (oldAttrs: { + propagatedBuildInputs = with self.pythonPackages; + (oldAttrs.propagatedBuildInputs or []) ++ [ + passlib + ]; + }); + }; + common = { config, pkgs, ...}: { + services.radicale = { + enable = true; + config = let home = config.users.extraUsers.radicale.home; in '' + [server] + hosts = 127.0.0.1:${builtins.toString port} + daemon = False + [encoding] + [well-known] + [auth] + type = htpasswd + htpasswd_filename = /etc/radicale/htpasswd + htpasswd_encryption = bcrypt + [git] + [rights] + [storage] + type = filesystem + filesystem_folder = ${home}/collections + [logging] + [headers] + ''; + }; + # WARNING: DON'T DO THIS IN PRODUCTION! + # This puts secrets (albeit hashed) directly into the Nix store for ease of testing. + environment.etc."radicale/htpasswd".source = with pkgs; let + py = python.withPackages(ps: with ps; [ passlib ]); + in runCommand "htpasswd" {} '' + ${py}/bin/python -c " +from passlib.apache import HtpasswdFile +ht = HtpasswdFile( + '$out', + new=True, + default_scheme='bcrypt' +) +ht.set_password('someuser', 'really_secret_password') +ht.save() +" + ''; + }; + +in import ./make-test.nix ({ lib, ... }: { + name = "radicale"; + meta.maintainers = with lib.maintainers; [ aneeshusa ]; + + # Test radicale with bcrypt-based htpasswd authentication + nodes = { + py2 = { config, pkgs, ... }@args: (common args) // { + nixpkgs.overlays = [ + radicaleOverlay + ]; + }; + py3 = { config, pkgs, ... }@args: (common args) // { + nixpkgs.overlays = [ + (self: super: { + python = self.python3; + pythonPackages = self.python3.pkgs; + }) + radicaleOverlay + ]; + }; + }; + + testScript = '' + for my $machine ($py2, $py3) { + $machine->waitForUnit('radicale.service'); + $machine->waitForOpenPort(${builtins.toString port}); + $machine->succeed('curl -s http://someuser:really_secret_password@127.0.0.1:${builtins.toString port}/someuser/calendar.ics/'); + } + ''; +}) diff --git a/nixos/tests/slim.nix b/nixos/tests/slim.nix new file mode 100644 index 000000000000..7b939d836381 --- /dev/null +++ b/nixos/tests/slim.nix @@ -0,0 +1,66 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "slim"; + + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ aszlig ]; + }; + + machine = { pkgs, lib, ... }: { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.windowManager.default = "icewm"; + services.xserver.windowManager.icewm.enable = true; + services.xserver.desktopManager.default = "none"; + services.xserver.displayManager.slim = { + enable = true; + + # Use a custom theme in order to get best OCR results + theme = pkgs.runCommand "slim-theme-ocr" { + nativeBuildInputs = [ pkgs.imagemagick ]; + } '' + mkdir "$out" + convert -size 1x1 xc:white "$out/background.jpg" + convert -size 200x100 xc:white "$out/panel.jpg" + cat > "$out/slim.theme" <<EOF + background_color #ffffff + background_style tile + + input_fgcolor #000000 + msg_color #000000 + + session_color #000000 + session_font Verdana:size=16:bold + + username_msg Username: + username_font Verdana:size=16:bold + username_color #000000 + username_x 50% + username_y 40% + + password_msg Password: + password_x 50% + password_y 40% + EOF + ''; + }; + }; + + enableOCR = true; + + testScript = { nodes, ... }: let + user = nodes.machine.config.users.extraUsers.alice; + in '' + startAll; + $machine->waitForText(qr/Username:/); + $machine->sendChars("${user.name}\n"); + $machine->waitForText(qr/Password:/); + $machine->sendChars("${user.password}\n"); + + $machine->waitForFile('${user.home}/.Xauthority'); + $machine->succeed('xauth merge ${user.home}/.Xauthority'); + $machine->waitForWindow('^IceWM '); + + # Make sure SLiM doesn't create a log file + $machine->fail('test -e /var/log/slim.log'); + ''; +}) diff --git a/nixos/tests/xrdp.nix b/nixos/tests/xrdp.nix new file mode 100644 index 000000000000..c997e36cc442 --- /dev/null +++ b/nixos/tests/xrdp.nix @@ -0,0 +1,45 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "xrdp"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ volth ]; + }; + + nodes = { + server = { lib, pkgs, ... }: { + imports = [ ./common/user-account.nix ]; + services.xrdp.enable = true; + services.xrdp.defaultWindowManager = "${pkgs.xterm}/bin/xterm"; + networking.firewall.allowedTCPPorts = [ 3389 ]; + }; + + client = { lib, pkgs, ... }: { + imports = [ ./common/x11.nix ./common/user-account.nix ]; + services.xserver.displayManager.auto.user = "alice"; + environment.systemPackages = [ pkgs.freerdp ]; + services.xrdp.enable = true; + services.xrdp.defaultWindowManager = "${pkgs.icewm}/bin/icewm"; + }; + }; + + testScript = { nodes, ... }: '' + startAll; + + $client->waitForX; + $client->waitForFile("/home/alice/.Xauthority"); + $client->succeed("xauth merge ~alice/.Xauthority"); + + $client->sleep(5); + + $client->execute("xterm &"); + $client->sleep(1); + $client->sendChars("xfreerdp /cert-tofu /w:640 /h:480 /v:127.0.0.1 /u:alice /p:foobar\n"); + $client->sleep(5); + $client->screenshot("localrdp"); + + $client->execute("xterm &"); + $client->sleep(1); + $client->sendChars("xfreerdp /cert-tofu /w:640 /h:480 /v:server /u:alice /p:foobar\n"); + $client->sleep(5); + $client->screenshot("remoterdp"); + ''; +}) |