diff options
author | Matthias Berndt <matthias_berndt@gmx.de> | 2023-05-17 21:47:19 -0400 |
---|---|---|
committer | Matthias Berndt <matthias_berndt@gmx.de> | 2023-05-17 21:47:19 -0400 |
commit | cb410a8c5919ae8fab94874d644cb6abe35487a6 (patch) | |
tree | 77bbd6082433f8e9e571614ab2baeef4fdfa6a36 /nixos/modules | |
parent | 92814241a8b992c18accbb939360654369eb2cc4 (diff) | |
parent | 285aa1f48e62932fed2089ddb04768172ae4a625 (diff) |
Merge remote-tracking branch 'upstream/master' into mberndt123/stratis-rootfs
Diffstat (limited to 'nixos/modules')
92 files changed, 2936 insertions, 802 deletions
diff --git a/nixos/modules/config/gnu.nix b/nixos/modules/config/gnu.nix index d06b479e2af53..a47d299b226b5 100644 --- a/nixos/modules/config/gnu.nix +++ b/nixos/modules/config/gnu.nix @@ -29,7 +29,6 @@ # GNU GRUB, where available. boot.loader.grub.enable = !pkgs.stdenv.isAarch32; - boot.loader.grub.version = 2; # GNU lsh. services.openssh.enable = false; diff --git a/nixos/modules/config/malloc.nix b/nixos/modules/config/malloc.nix index b740ebfccb20d..ae0661f472f69 100644 --- a/nixos/modules/config/malloc.nix +++ b/nixos/modules/config/malloc.nix @@ -30,7 +30,7 @@ let systemPlatform = platformMap.${pkgs.stdenv.hostPlatform.system} or (throw "scudo not supported on ${pkgs.stdenv.hostPlatform.system}"); in { - libPath = "${pkgs.llvmPackages_latest.compiler-rt}/lib/linux/libclang_rt.scudo-${systemPlatform}.so"; + libPath = "${pkgs.llvmPackages_14.compiler-rt}/lib/linux/libclang_rt.scudo-${systemPlatform}.so"; description = '' A user-mode allocator based on LLVM Sanitizer’s CombinedAllocator, which aims at providing additional mitigations against heap based diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix index 3ebe2fa9f1647..dac09bdf468a9 100644 --- a/nixos/modules/config/no-x-libs.nix +++ b/nixos/modules/config/no-x-libs.nix @@ -47,7 +47,7 @@ with lib; libva = super.libva-minimal; limesuite = super.limesuite.override { withGui = false; }; mc = super.mc.override { x11Support = false; }; - mpv-unwrapped = super.mpv-unwrapped.override { sdl2Support = false; x11Support = false; }; + mpv-unwrapped = super.mpv-unwrapped.override { sdl2Support = false; x11Support = false; waylandSupport = false; }; msmtp = super.msmtp.override { withKeyring = false; }; neofetch = super.neofetch.override { x11Support = false; }; networkmanager-fortisslvpn = super.networkmanager-fortisslvpn.override { withGnome = false; }; @@ -59,6 +59,7 @@ with lib; networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; }; pango = super.pango.override { x11Support = false; }; pinentry = super.pinentry.override { enabledFlavors = [ "curses" "tty" "emacs" ]; withLibsecret = false; }; + pipewire = super.pipewire.override { x11Support = false; }; qemu = super.qemu.override { gtkSupport = false; spiceSupport = false; sdlSupport = false; }; qrencode = super.qrencode.overrideAttrs (_: { doCheck = false; }); qt5 = super.qt5.overrideScope (const (super': { diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix index 2c9c4c9c1a2d8..0a7e45bffb267 100644 --- a/nixos/modules/config/swap.nix +++ b/nixos/modules/config/swap.nix @@ -38,6 +38,34 @@ let ''; }; + keySize = mkOption { + default = null; + example = "512"; + type = types.nullOr types.int; + description = lib.mdDoc '' + Set the encryption key size for the plain device. + + If not specified, the amount of data to read from `source` will be + determined by cryptsetup. + + See `cryptsetup-open(8)` for details. + ''; + }; + + sectorSize = mkOption { + default = null; + example = "4096"; + type = types.nullOr types.int; + description = lib.mdDoc '' + Set the sector size for the plain encrypted device type. + + If not specified, the default sector size is determined from the + underlying block device. + + See `cryptsetup-open(8)` for details. + ''; + }; + source = mkOption { default = "/dev/urandom"; example = "/dev/random"; @@ -157,11 +185,11 @@ let }; - config = rec { + config = { device = mkIf options.label.isDefined "/dev/disk/by-label/${config.label}"; deviceName = lib.replaceStrings ["\\"] [""] (escapeSystemdPath config.device); - realDevice = if config.randomEncryption.enable then "/dev/mapper/${deviceName}" else config.device; + realDevice = if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device; }; }; @@ -247,7 +275,11 @@ in ''} ${optionalString sw.randomEncryption.enable '' cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \ - ${optionalString sw.randomEncryption.allowDiscards "--allow-discards"} ${sw.device} ${sw.deviceName} + ${concatStringsSep " \\\n" (flatten [ + (optional (sw.randomEncryption.sectorSize != null) "--sector-size=${toString sw.randomEncryption.sectorSize}") + (optional (sw.randomEncryption.keySize != null) "--key-size=${toString sw.randomEncryption.keySize}") + (optional sw.randomEncryption.allowDiscards "--allow-discards") + ])} ${sw.device} ${sw.deviceName} mkswap ${sw.realDevice} ''} ''; diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix index ea17e2a705ede..e22bb866927ba 100644 --- a/nixos/modules/installer/cd-dvd/iso-image.nix +++ b/nixos/modules/installer/cd-dvd/iso-image.nix @@ -473,7 +473,7 @@ in }; isoImage.squashfsCompression = mkOption { - default = with pkgs.stdenv.targetPlatform; "xz -Xdict-size 100% " + default = with pkgs.stdenv.hostPlatform; "xz -Xdict-size 100% " + lib.optionalString isx86 "-Xbcj x86" # Untested but should also reduce size for these platforms + lib.optionalString isAarch "-Xbcj arm" @@ -483,6 +483,7 @@ in Compression settings to use for the squashfs nix store. ''; example = "zstd -Xcompression-level 6"; + type = types.str; }; isoImage.edition = mkOption { @@ -693,8 +694,6 @@ in } ]; - boot.loader.grub.version = 2; - # Don't build the GRUB menu builder script, since we don't need it # here and it causes a cyclic dependency. boot.loader.grub.enable = false; diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix index 03bb529cd8510..a55c0ab2d6557 100644 --- a/nixos/modules/installer/netboot/netboot.nix +++ b/nixos/modules/installer/netboot/netboot.nix @@ -8,6 +8,20 @@ with lib; { options = { + netboot.squashfsCompression = mkOption { + default = with pkgs.stdenv.hostPlatform; "xz -Xdict-size 100% " + + lib.optionalString isx86 "-Xbcj x86" + # Untested but should also reduce size for these platforms + + lib.optionalString isAarch "-Xbcj arm" + + lib.optionalString (isPower && is32bit && isBigEndian) "-Xbcj powerpc" + + lib.optionalString (isSparc) "-Xbcj sparc"; + description = lib.mdDoc '' + Compression settings to use for the squashfs nix store. + ''; + example = "zstd -Xcompression-level 6"; + type = types.str; + }; + netboot.storeContents = mkOption { example = literalExpression "[ pkgs.stdenv ]"; description = lib.mdDoc '' @@ -77,6 +91,7 @@ with lib; # Create the squashfs image that contains the Nix store. system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix { storeContents = config.netboot.storeContents; + comp = config.netboot.squashfsCompression; }; diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl index c822ea61200d3..c2a5ecbe9e2ea 100644 --- a/nixos/modules/installer/tools/nixos-generate-config.pl +++ b/nixos/modules/installer/tools/nixos-generate-config.pl @@ -668,7 +668,6 @@ EOF $bootLoaderConfig = <<EOF; # Use the GRUB 2 boot loader. boot.loader.grub.enable = true; - boot.loader.grub.version = 2; # boot.loader.grub.efiSupport = true; # boot.loader.grub.efiInstallAsRemovable = true; # boot.loader.efi.efiSysMountPoint = "/boot/efi"; diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 7f44c3f6f3f0e..55ec08acf4453 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -49,10 +49,10 @@ let merge = lib.mergeOneOption; }; - pkgsType = mkOptionType { - name = "nixpkgs"; + pkgsType = types.pkgs // { + # This type is only used by itself, so let's elaborate the description a bit + # for the purpose of documentation. description = "An evaluation of Nixpkgs; the top level attribute set of packages"; - check = builtins.isAttrs; }; # Whether `pkgs` was constructed by this module - not if nixpkgs.pkgs or diff --git a/nixos/modules/misc/nixpkgs/read-only.nix b/nixos/modules/misc/nixpkgs/read-only.nix new file mode 100644 index 0000000000000..2a783216a9d54 --- /dev/null +++ b/nixos/modules/misc/nixpkgs/read-only.nix @@ -0,0 +1,74 @@ +# A replacement for the traditional nixpkgs module, such that none of the modules +# can add their own configuration. This ensures that the Nixpkgs configuration is +# exactly as the user intends. +# This may also be used as a performance optimization when evaluating multiple +# configurations at once, with a shared `pkgs`. + +# This is a separate module, because merging this logic into the nixpkgs module +# is too burdensome, considering that it is already burdened with legacy. +# Moving this logic into a module does not lose any composition benefits, because +# its purpose is not something that composes anyway. + +{ lib, config, ... }: + +let + cfg = config.nixpkgs; + inherit (lib) mkOption types; + +in +{ + disabledModules = [ + ../nixpkgs.nix + ]; + options = { + nixpkgs = { + pkgs = mkOption { + type = lib.types.pkgs; + description = lib.mdDoc ''The pkgs module argument.''; + }; + config = mkOption { + internal = true; + type = types.unique { message = "nixpkgs.config is set to read-only"; } types.anything; + description = lib.mdDoc '' + The Nixpkgs `config` that `pkgs` was initialized with. + ''; + }; + overlays = mkOption { + internal = true; + type = types.unique { message = "nixpkgs.overlays is set to read-only"; } types.anything; + description = lib.mdDoc '' + The Nixpkgs overlays that `pkgs` was initialized with. + ''; + }; + hostPlatform = mkOption { + internal = true; + readOnly = true; + description = lib.mdDoc '' + The platform of the machine that is running the NixOS configuration. + ''; + }; + buildPlatform = mkOption { + internal = true; + readOnly = true; + description = lib.mdDoc '' + The platform of the machine that built the NixOS configuration. + ''; + }; + # NOTE: do not add the legacy options such as localSystem here. Let's keep + # this module simple and let module authors upgrade their code instead. + }; + }; + config = { + _module.args.pkgs = + # find mistaken definitions + builtins.seq cfg.config + builtins.seq cfg.overlays + builtins.seq cfg.hostPlatform + builtins.seq cfg.buildPlatform + cfg.pkgs; + nixpkgs.config = cfg.pkgs.config; + nixpkgs.overlays = cfg.pkgs.overlays; + nixpkgs.hostPlatform = cfg.pkgs.stdenv.hostPlatform; + nixpkgs.buildPlatform = cfg.pkgs.stdenv.buildPlatform; + }; +} diff --git a/nixos/modules/misc/nixpkgs/test.nix b/nixos/modules/misc/nixpkgs/test.nix index a6d8877ae0700..0536cfc9624a2 100644 --- a/nixos/modules/misc/nixpkgs/test.nix +++ b/nixos/modules/misc/nixpkgs/test.nix @@ -1,3 +1,5 @@ +# [nixpkgs]$ nix-build -A nixosTests.nixpkgs --show-trace + { evalMinimalConfig, pkgs, lib, stdenv }: let eval = mod: evalMinimalConfig { @@ -27,6 +29,47 @@ let let uncheckedEval = lib.evalModules { modules = [ ../nixpkgs.nix module ]; }; in map (ass: ass.message) (lib.filter (ass: !ass.assertion) uncheckedEval.config.assertions); + + readOnlyUndefined = evalMinimalConfig { + imports = [ ./read-only.nix ]; + }; + + readOnlyBad = evalMinimalConfig { + imports = [ ./read-only.nix ]; + nixpkgs.pkgs = { }; + }; + + readOnly = evalMinimalConfig { + imports = [ ./read-only.nix ]; + nixpkgs.pkgs = pkgs; + }; + + readOnlyBadConfig = evalMinimalConfig { + imports = [ ./read-only.nix ]; + nixpkgs.pkgs = pkgs; + nixpkgs.config.allowUnfree = true; # do in pkgs instead! + }; + + readOnlyBadOverlays = evalMinimalConfig { + imports = [ ./read-only.nix ]; + nixpkgs.pkgs = pkgs; + nixpkgs.overlays = [ (_: _: {}) ]; # do in pkgs instead! + }; + + readOnlyBadHostPlatform = evalMinimalConfig { + imports = [ ./read-only.nix ]; + nixpkgs.pkgs = pkgs; + nixpkgs.hostPlatform = "foo-linux"; # do in pkgs instead! + }; + + readOnlyBadBuildPlatform = evalMinimalConfig { + imports = [ ./read-only.nix ]; + nixpkgs.pkgs = pkgs; + nixpkgs.buildPlatform = "foo-linux"; # do in pkgs instead! + }; + + throws = x: ! (builtins.tryEval x).success; + in lib.recurseIntoAttrs { invokeNixpkgsSimple = @@ -65,5 +108,21 @@ lib.recurseIntoAttrs { nixpkgs.pkgs = pkgs; } == []; + + # Tests for the read-only.nix module + assert readOnly._module.args.pkgs.stdenv.hostPlatform.system == pkgs.stdenv.hostPlatform.system; + assert throws readOnlyBad._module.args.pkgs.stdenv; + assert throws readOnlyUndefined._module.args.pkgs.stdenv; + assert throws readOnlyBadConfig._module.args.pkgs.stdenv; + assert throws readOnlyBadOverlays._module.args.pkgs.stdenv; + assert throws readOnlyBadHostPlatform._module.args.pkgs.stdenv; + assert throws readOnlyBadBuildPlatform._module.args.pkgs.stdenv; + # read-only.nix does not provide legacy options, for the sake of simplicity + # If you're bothered by this, upgrade your configs to use the new *Platform + # options. + assert !readOnly.options.nixpkgs?system; + assert !readOnly.options.nixpkgs?localSystem; + assert !readOnly.options.nixpkgs?crossSystem; + pkgs.emptyFile; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 4821b4033fdd5..94b6461b7843b 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -153,6 +153,7 @@ ./programs/cnping.nix ./programs/command-not-found/command-not-found.nix ./programs/criu.nix + ./programs/darling.nix ./programs/dconf.nix ./programs/digitalbitbox/default.nix ./programs/dmrconfig.nix @@ -234,6 +235,7 @@ ./programs/singularity.nix ./programs/skim.nix ./programs/slock.nix + ./programs/sniffnet.nix ./programs/spacefm.nix ./programs/ssh.nix ./programs/starship.nix @@ -247,6 +249,7 @@ ./programs/thunar.nix ./programs/tmux.nix ./programs/traceroute.nix + ./programs/trippy.nix ./programs/tsm-client.nix ./programs/turbovnc.nix ./programs/udevil.nix @@ -306,6 +309,7 @@ ./services/audio/alsa.nix ./services/audio/botamusique.nix ./services/audio/gmediarender.nix + ./services/audio/gonic.nix ./services/audio/hqplayerd.nix ./services/audio/icecast.nix ./services/audio/jack.nix @@ -371,6 +375,7 @@ ./services/continuous-integration/buildbot/master.nix ./services/continuous-integration/buildbot/worker.nix ./services/continuous-integration/buildkite-agents.nix + ./services/continuous-integration/gitea-actions-runner.nix ./services/continuous-integration/github-runner.nix ./services/continuous-integration/github-runners.nix ./services/continuous-integration/gitlab-runner.nix @@ -684,6 +689,7 @@ ./services/misc/ripple-data-api.nix ./services/misc/rippled.nix ./services/misc/rmfakecloud.nix + ./services/misc/rshim.nix ./services/misc/safeeyes.nix ./services/misc/sdrplay.nix ./services/misc/serviio.nix @@ -801,6 +807,7 @@ ./services/network-filesystems/yandex-disk.nix ./services/networking/3proxy.nix ./services/networking/adguardhome.nix + ./services/networking/alice-lg.nix ./services/networking/amuled.nix ./services/networking/antennas.nix ./services/networking/aria2.nix @@ -815,6 +822,7 @@ ./services/networking/bind.nix ./services/networking/bird-lg.nix ./services/networking/bird.nix + ./services/networking/birdwatcher.nix ./services/networking/bitcoind.nix ./services/networking/bitlbee.nix ./services/networking/blockbook-frontend.nix @@ -873,6 +881,7 @@ ./services/networking/gobgpd.nix ./services/networking/gvpe.nix ./services/networking/hans.nix + ./services/networking/harmonia.nix ./services/networking/haproxy.nix ./services/networking/headscale.nix ./services/networking/hostapd.nix @@ -1173,6 +1182,7 @@ ./services/web-apps/gerrit.nix ./services/web-apps/gotify-server.nix ./services/web-apps/grocy.nix + ./services/web-apps/pixelfed.nix ./services/web-apps/healthchecks.nix ./services/web-apps/hedgedoc.nix ./services/web-apps/hledger-web.nix @@ -1189,6 +1199,7 @@ ./services/web-apps/komga.nix ./services/web-apps/lemmy.nix ./services/web-apps/limesurvey.nix + ./services/web-apps/mainsail.nix ./services/web-apps/mastodon.nix ./services/web-apps/matomo.nix ./services/web-apps/mattermost.nix @@ -1203,6 +1214,7 @@ ./services/web-apps/nifi.nix ./services/web-apps/node-red.nix ./services/web-apps/onlyoffice.nix + ./services/web-apps/openvscode-server.nix ./services/web-apps/openwebrx.nix ./services/web-apps/outline.nix ./services/web-apps/peering-manager.nix @@ -1368,6 +1380,7 @@ ./tasks/filesystems/cifs.nix ./tasks/filesystems/ecryptfs.nix ./tasks/filesystems/envfs.nix + ./tasks/filesystems/erofs.nix ./tasks/filesystems/exfat.nix ./tasks/filesystems/ext.nix ./tasks/filesystems/f2fs.nix diff --git a/nixos/modules/programs/darling.nix b/nixos/modules/programs/darling.nix new file mode 100644 index 0000000000000..c4e1c73b5c29a --- /dev/null +++ b/nixos/modules/programs/darling.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.darling; +in { + options = { + programs.darling = { + enable = lib.mkEnableOption (lib.mdDoc "Darling, a Darwin/macOS compatibility layer for Linux"); + package = lib.mkPackageOptionMD pkgs "darling" {}; + }; + }; + + config = lib.mkIf cfg.enable { + security.wrappers.darling = { + source = lib.getExe cfg.package; + owner = "root"; + group = "root"; + setuid = true; + }; + }; +} diff --git a/nixos/modules/programs/fzf.nix b/nixos/modules/programs/fzf.nix index 4442d88941c66..7c4f338e29b30 100644 --- a/nixos/modules/programs/fzf.nix +++ b/nixos/modules/programs/fzf.nix @@ -26,7 +26,7 @@ in source ${pkgs.fzf}/share/fzf/key-bindings.zsh ''); - programs.zsh.ohMyZsh.plugins = optional (cfg.keybindings || cfg.fuzzyCompletion) [ "fzf" ]; + programs.zsh.ohMyZsh.plugins = lib.mkIf (cfg.keybindings || cfg.fuzzyCompletion) [ "fzf" ]; }; meta.maintainers = with maintainers; [ laalsaas ]; } diff --git a/nixos/modules/programs/miriway.nix b/nixos/modules/programs/miriway.nix index 52b5f84762220..a67e1a17a7e60 100644 --- a/nixos/modules/programs/miriway.nix +++ b/nixos/modules/programs/miriway.nix @@ -8,7 +8,7 @@ in { Miriway, a Mir based Wayland compositor. You can manually launch Miriway by executing "exec miriway" on a TTY, or launch it from a display manager. Copy /etc/xdg/xdg-miriway/miriway-shell.config to ~/.config/miriway-shell.config - to modify the default configuration. See <https://github.com/Miriway/Miriway>, + to modify the system-wide configuration on a per-user basis. See <https://github.com/Miriway/Miriway>, and "miriway --help" for more information''); config = lib.mkOption { @@ -19,6 +19,15 @@ in { ctrl-alt=t:miriway-terminal # Default "terminal emulator finder" shell-component=dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY + + meta=Left:@dock-left + meta=Right:@dock-right + meta=Space:@toggle-maximized + meta=Home:@workspace-begin + meta=End:@workspace-end + meta=Page_Up:@workspace-up + meta=Page_Down:@workspace-down + ctrl-alt=BackSpace:@exit ''; example = '' idle-timeout=300 @@ -31,6 +40,15 @@ in { shell-component=wbg Pictures/wallpaper shell-meta=a:synapse + + meta=Left:@dock-left + meta=Right:@dock-right + meta=Space:@toggle-maximized + meta=Home:@workspace-begin + meta=End:@workspace-end + meta=Page_Up:@workspace-up + meta=Page_Down:@workspace-down + ctrl-alt=BackSpace:@exit ''; description = lib.mdDoc '' Miriway's config. This will be installed system-wide. diff --git a/nixos/modules/programs/neovim.nix b/nixos/modules/programs/neovim.nix index 3f0e9fc173bdf..1b53b9b5d9194 100644 --- a/nixos/modules/programs/neovim.nix +++ b/nixos/modules/programs/neovim.nix @@ -138,7 +138,8 @@ in }; source = mkOption { - type = types.path; + default = null; + type = types.nullOr types.path; description = lib.mdDoc "Path of the source file."; }; @@ -160,9 +161,11 @@ in environment.etc = listToAttrs (attrValues (mapAttrs (name: value: { name = "xdg/nvim/${name}"; - value = value // { - target = "xdg/nvim/${value.target}"; - }; + value = removeAttrs + (value // { + target = "xdg/nvim/${value.target}"; + }) + (optionals (isNull value.source) [ "source" ]); }) cfg.runtime)); diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix index fab809f279a36..35267acd6bb74 100644 --- a/nixos/modules/programs/shadow.nix +++ b/nixos/modules/programs/shadow.nix @@ -41,6 +41,8 @@ let # This should be made configurable. #CHFN_RESTRICT frwh + # The default crypt() method, keep in sync with the PAM default + ENCRYPT_METHOD YESCRYPT ''; mkSetuidRoot = source: diff --git a/nixos/modules/programs/sniffnet.nix b/nixos/modules/programs/sniffnet.nix new file mode 100644 index 0000000000000..98e9f628a9bce --- /dev/null +++ b/nixos/modules/programs/sniffnet.nix @@ -0,0 +1,24 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.sniffnet; +in + +{ + options = { + programs.sniffnet = { + enable = lib.mkEnableOption (lib.mdDoc "sniffnet"); + }; + }; + + config = lib.mkIf cfg.enable { + security.wrappers.sniffnet = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw,cap_net_admin=eip"; + source = "${pkgs.sniffnet}/bin/sniffnet"; + }; + }; + + meta.maintainers = with lib.maintainers; [ figsoda ]; +} diff --git a/nixos/modules/programs/trippy.nix b/nixos/modules/programs/trippy.nix new file mode 100644 index 0000000000000..6e31aea43e750 --- /dev/null +++ b/nixos/modules/programs/trippy.nix @@ -0,0 +1,24 @@ +{ lib, config, pkgs, ... }: + +let + cfg = config.programs.trippy; +in + +{ + options = { + programs.trippy = { + enable = lib.mkEnableOption (lib.mdDoc "trippy"); + }; + }; + + config = lib.mkIf cfg.enable { + security.wrappers.trip = { + owner = "root"; + group = "root"; + capabilities = "cap_net_raw+p"; + source = lib.getExe pkgs.trippy; + }; + }; + + meta.maintainers = with lib.maintainers; [ figsoda ]; +} diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 45a27029dff19..a4a2c316fd6c1 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -1,7 +1,10 @@ { lib, pkgs, ... }: -with lib; - +let + inherit (lib) + mkAliasOptionModuleMD + mkRemovedOptionModule; +in { imports = [ /* diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix index ea94b54312cfb..8aee71c864d0a 100644 --- a/nixos/modules/security/acme/default.nix +++ b/nixos/modules/security/acme/default.nix @@ -323,7 +323,7 @@ let } fi ''); - } // optionalAttrs (data.listenHTTP != null && toInt (elemAt (splitString ":" data.listenHTTP) 1) < 1024) { + } // optionalAttrs (data.listenHTTP != null && toInt (last (splitString ":" data.listenHTTP)) < 1024) { CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; }; diff --git a/nixos/modules/security/apparmor/includes.nix b/nixos/modules/security/apparmor/includes.nix index f290e95a296da..adfca04426cac 100644 --- a/nixos/modules/security/apparmor/includes.nix +++ b/nixos/modules/security/apparmor/includes.nix @@ -22,7 +22,7 @@ in # some may even be completely useless. config.security.apparmor.includes = { # This one is included by <tunables/global> - # which is usualy included before any profile. + # which is usually included before any profile. "abstractions/tunables/alias" = '' alias /bin -> /run/current-system/sw/bin, alias /lib/modules -> /run/current-system/kernel/lib/modules, diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 6e8be412de83c..eac67cfdec5a6 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -446,6 +446,15 @@ let }; }; + zfs = mkOption { + default = config.security.pam.zfs.enable; + defaultText = literalExpression "config.security.pam.zfs.enable"; + type = types.bool; + description = lib.mdDoc '' + Enable unlocking and mounting of encrypted ZFS home dataset at login. + ''; + }; + text = mkOption { type = types.nullOr types.lines; description = lib.mdDoc "Contents of the PAM service file."; @@ -556,7 +565,8 @@ let || cfg.googleAuthenticator.enable || cfg.gnupg.enable || cfg.failDelay.enable - || cfg.duoSecurity.enable)) + || cfg.duoSecurity.enable + || cfg.zfs)) ( optionalString config.services.homed.enable '' auth optional ${config.systemd.package}/lib/security/pam_systemd_home.so @@ -570,6 +580,9 @@ let optionalString config.security.pam.enableFscrypt '' auth optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' + + optionalString cfg.zfs '' + auth optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + '' + optionalString cfg.pamMount '' auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive '' + @@ -628,6 +641,9 @@ let optionalString config.security.pam.enableFscrypt '' password optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' + + optionalString cfg.zfs '' + password optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} + '' + optionalString cfg.pamMount '' password optional ${pkgs.pam_mount}/lib/security/pam_mount.so '' + @@ -638,7 +654,7 @@ let password sufficient ${pkgs.pam_mysql}/lib/security/pam_mysql.so config_file=/etc/security/pam_mysql.conf '' + optionalString config.services.sssd.enable '' - password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok + password sufficient ${pkgs.sssd}/lib/security/pam_sss.so '' + optionalString config.security.pam.krb5.enable '' password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass @@ -685,6 +701,10 @@ let session [success=1 default=ignore] pam_succeed_if.so service = systemd-user session optional ${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so '' + + optionalString cfg.zfs '' + session [success=1 default=ignore] pam_succeed_if.so service = systemd-user + session optional ${config.boot.zfs.package}/lib/security/pam_zfs_key.so homes=${config.security.pam.zfs.homes} ${optionalString config.security.pam.zfs.noUnmount "nounmount"} + '' + optionalString cfg.pamMount '' session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive '' + @@ -1202,6 +1222,34 @@ in }; }; + security.pam.zfs = { + enable = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Enable unlocking and mounting of encrypted ZFS home dataset at login. + ''; + }; + + homes = mkOption { + example = "rpool/home"; + default = "rpool/home"; + type = types.str; + description = lib.mdDoc '' + Prefix of home datasets. This value will be concatenated with + `"/" + <username>` in order to determine the home dataset to unlock. + ''; + }; + + noUnmount = mkOption { + default = false; + type = types.bool; + description = lib.mdDoc '' + Do not unmount home dataset on logout. + ''; + }; + }; + security.pam.enableEcryptfs = mkEnableOption (lib.mdDoc "eCryptfs PAM module (mounting ecryptfs home directory on login)"); security.pam.enableFscrypt = mkEnableOption (lib.mdDoc '' Enables fscrypt to automatically unlock directories with the user's login password. @@ -1238,6 +1286,12 @@ in Only one of users.motd and users.motdFile can be set. ''; } + { + assertion = config.security.pam.zfs.enable -> (config.boot.zfs.enabled || config.boot.zfs.enableUnstable); + message = '' + `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled` or `boot.zfs.enableUnstable`). + ''; + } ]; environment.systemPackages = @@ -1378,7 +1432,10 @@ in mr ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so, '' + optionalString config.virtualisation.lxc.lxcfs.enable '' - mr ${pkgs.lxc}/lib/security/pam_cgfs.so + mr ${pkgs.lxc}/lib/security/pam_cgfs.so, + '' + + optionalString (isEnabled (cfg: cfg.zfs)) '' + mr ${config.boot.zfs.package}/lib/security/pam_zfs_key.so, '' + optionalString config.services.homed.enable '' mr ${config.systemd.package}/lib/security/pam_systemd_home.so diff --git a/nixos/modules/security/tpm2.nix b/nixos/modules/security/tpm2.nix index 5a023cec48ee0..708c3a69d1740 100644 --- a/nixos/modules/security/tpm2.nix +++ b/nixos/modules/security/tpm2.nix @@ -3,7 +3,7 @@ let cfg = config.security.tpm2; # This snippet is taken from tpm2-tss/dist/tpm-udev.rules, but modified to allow custom user/groups - # The idea is that the tssUser is allowed to acess the TPM and kernel TPM resource manager, while + # The idea is that the tssUser is allowed to access the TPM and kernel TPM resource manager, while # the tssGroup is only allowed to access the kernel resource manager # Therefore, if either of the two are null, the respective part isn't generated udevRules = tssUser: tssGroup: '' diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix index 4b62abd658a42..12255d8392fe9 100644 --- a/nixos/modules/security/wrappers/default.nix +++ b/nixos/modules/security/wrappers/default.nix @@ -283,7 +283,7 @@ in ''; ###### wrappers consistency checks - system.extraDependencies = lib.singleton (pkgs.runCommandLocal + system.checks = lib.singleton (pkgs.runCommandLocal "ensure-all-wrappers-paths-exist" { } '' # make sure we produce output diff --git a/nixos/modules/services/audio/gonic.nix b/nixos/modules/services/audio/gonic.nix new file mode 100644 index 0000000000000..65cf10f2c4b46 --- /dev/null +++ b/nixos/modules/services/audio/gonic.nix @@ -0,0 +1,89 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.gonic; + settingsFormat = pkgs.formats.keyValue { + mkKeyValue = lib.generators.mkKeyValueDefault { } " "; + listsAsDuplicateKeys = true; + }; +in +{ + options = { + services.gonic = { + + enable = mkEnableOption (lib.mdDoc "Gonic music server"); + + settings = mkOption rec { + type = settingsFormat.type; + apply = recursiveUpdate default; + default = { + listen-addr = "127.0.0.1:4747"; + cache-path = "/var/cache/gonic"; + tls-cert = null; + tls-key = null; + }; + example = { + music-path = [ "/mnt/music" ]; + podcast-path = "/mnt/podcasts"; + }; + description = lib.mdDoc '' + Configuration for Gonic, see <https://github.com/sentriz/gonic#configuration-options> for supported values. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + systemd.services.gonic = { + description = "Gonic Media Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = + let + # these values are null by default but should not appear in the final config + filteredSettings = filterAttrs (n: v: !((n == "tls-cert" || n == "tls-key") && v == null)) cfg.settings; + in + "${pkgs.gonic}/bin/gonic -config-path ${settingsFormat.generate "gonic" filteredSettings}"; + DynamicUser = true; + StateDirectory = "gonic"; + CacheDirectory = "gonic"; + WorkingDirectory = "/var/lib/gonic"; + RuntimeDirectory = "gonic"; + RootDirectory = "/run/gonic"; + ReadWritePaths = ""; + BindReadOnlyPaths = [ + # gonic can access scrobbling services + "-/etc/ssl/certs/ca-certificates.crt" + builtins.storeDir + cfg.settings.podcast-path + ] ++ cfg.settings.music-path + ++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert + ++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key; + CapabilityBoundingSet = ""; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + UMask = "0066"; + ProtectHostname = true; + }; + }; + }; + + meta.maintainers = [ maintainers.autrimpo ]; +} diff --git a/nixos/modules/services/backup/automysqlbackup.nix b/nixos/modules/services/backup/automysqlbackup.nix index d0237f196a807..27bbff813b105 100644 --- a/nixos/modules/services/backup/automysqlbackup.nix +++ b/nixos/modules/services/backup/automysqlbackup.nix @@ -3,7 +3,7 @@ let inherit (lib) concatMapStringsSep concatStringsSep isInt isList literalExpression; - inherit (lib) mapAttrs mapAttrsToList mkDefault mkEnableOption mkIf mkOption optional types; + inherit (lib) mapAttrs mapAttrsToList mkDefault mkEnableOption mkIf mkOption mkRenamedOptionModule optional types; cfg = config.services.automysqlbackup; pkg = pkgs.automysqlbackup; @@ -26,6 +26,10 @@ let in { + imports = [ + (mkRenamedOptionModule [ "services" "automysqlbackup" "config" ] [ "services" "automysqlbackup" "settings" ]) + ]; + # interface options = { services.automysqlbackup = { @@ -40,7 +44,7 @@ in ''; }; - config = mkOption { + settings = mkOption { type = with types; attrsOf (oneOf [ str int bool (listOf str) ]); default = {}; description = lib.mdDoc '' @@ -112,7 +116,18 @@ in services.mysql.ensureUsers = optional (config.services.mysql.enable && cfg.config.mysql_dump_host == "localhost") { name = user; - ensurePermissions = { "*.*" = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT"; }; + ensurePermissions = { + "*.*" = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT"; + + # https://forums.mysql.com/read.php?10,668311,668315#msg-668315 + "function sys.extract_table_from_file_name" = "execute"; + "function sys.format_path" = "execute"; + "function sys.format_statement" = "execute"; + "function sys.extract_schema_from_file_name" = "execute"; + "function sys.ps_thread_account" = "execute"; + "function sys.format_time" = "execute"; + "function sys.format_bytes" = "execute"; + }; }; }; diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix index 08a2967e9c7fc..0da70112d48dd 100644 --- a/nixos/modules/services/backup/borgbackup.nix +++ b/nixos/modules/services/backup/borgbackup.nix @@ -109,7 +109,7 @@ let }; environment = { BORG_REPO = cfg.repo; - inherit (cfg) extraArgs extraInitArgs extraCreateArgs extraPruneArgs; + inherit (cfg) extraArgs extraInitArgs extraCreateArgs extraPruneArgs extraCompactArgs; } // (mkPassEnv cfg) // cfg.environment; }; diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix index 8cc0c084d659f..3a951f7cbc834 100644 --- a/nixos/modules/services/backup/restic.nix +++ b/nixos/modules/services/backup/restic.nix @@ -145,6 +145,7 @@ in type = types.attrsOf unitOption; default = { OnCalendar = "daily"; + Persistent = true; }; description = lib.mdDoc '' When to run the backup. See {manpage}`systemd.timer(5)` for details. @@ -152,6 +153,7 @@ in example = { OnCalendar = "00:05"; RandomizedDelaySec = "5h"; + Persistent = true; }; }; diff --git a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix new file mode 100644 index 0000000000000..4b9046c98e8b6 --- /dev/null +++ b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix @@ -0,0 +1,237 @@ +{ config +, lib +, pkgs +, utils +, ... +}: + +let + inherit (lib) + any + attrValues + concatStringsSep + escapeShellArg + hasInfix + hasSuffix + optionalAttrs + optionals + literalExpression + mapAttrs' + mkEnableOption + mkOption + mkPackageOptionMD + mkIf + nameValuePair + types + ; + + inherit (utils) + escapeSystemdPath + ; + + cfg = config.services.gitea-actions-runner; + + # Check whether any runner instance label requires a container runtime + # Empty label strings result in the upstream defined defaultLabels, which require docker + # https://gitea.com/gitea/act_runner/src/tag/v0.1.5/internal/app/cmd/register.go#L93-L98 + hasDockerScheme = instance: + instance.labels == [] || any (label: hasInfix ":docker:" label) instance.labels; + wantsContainerRuntime = any hasDockerScheme (attrValues cfg.instances); + + hasHostScheme = instance: any (label: hasSuffix ":host" label) instance.labels; + + # provide shorthands for whether container runtimes are enabled + hasDocker = config.virtualisation.docker.enable; + hasPodman = config.virtualisation.podman.enable; + + tokenXorTokenFile = instance: + (instance.token == null && instance.tokenFile != null) || + (instance.token != null && instance.tokenFile == null); +in +{ + meta.maintainers = with lib.maintainers; [ + hexa + ]; + + options.services.gitea-actions-runner = with types; { + package = mkPackageOptionMD pkgs "gitea-actions-runner" { }; + + instances = mkOption { + default = {}; + description = lib.mdDoc '' + Gitea Actions Runner instances. + ''; + type = attrsOf (submodule { + options = { + enable = mkEnableOption (lib.mdDoc "Gitea Actions Runner instance"); + + name = mkOption { + type = str; + example = literalExpression "config.networking.hostName"; + description = lib.mdDoc '' + The name identifying the runner instance towards the Gitea/Forgejo instance. + ''; + }; + + url = mkOption { + type = str; + example = "https://forge.example.com"; + description = lib.mdDoc '' + Base URL of your Gitea/Forgejo instance. + ''; + }; + + token = mkOption { + type = nullOr str; + default = null; + description = lib.mdDoc '' + Plain token to register at the configured Gitea/Forgejo instance. + ''; + }; + + tokenFile = mkOption { + type = nullOr (either str path); + default = null; + description = lib.mdDoc '' + Path to an environment file, containing the `TOKEN` environment + variable, that holds a token to register at the configured + Gitea/Forgejo instance. + ''; + }; + + labels = mkOption { + type = listOf str; + example = literalExpression '' + [ + # provide a debian base with nodejs for actions + "debian-latest:docker://node:18-bullseye" + # fake the ubuntu name, because node provides no ubuntu builds + "ubuntu-latest:docker://node:18-bullseye" + # provide native execution on the host + #"native:host" + ] + ''; + description = lib.mdDoc '' + Labels used to map jobs to their runtime environment. Changing these + labels currently requires a new registration token. + + Many common actions require bash, git and nodejs, as well as a filesystem + that follows the filesystem hierarchy standard. + ''; + }; + + hostPackages = mkOption { + type = listOf package; + default = with pkgs; [ + bash + coreutils + curl + gawk + gitMinimal + gnused + nodejs + wget + ]; + defaultText = literalExpression '' + with pkgs; [ + bash + coreutils + curl + gawk + gitMinimal + gnused + nodejs + wget + ] + ''; + description = lib.mdDoc '' + List of packages, that are available to actions, when the runner is configured + with a host execution label. + ''; + }; + }; + }); + }; + }; + + config = mkIf (cfg.instances != {}) { + assertions = [ { + assertion = any tokenXorTokenFile (attrValues cfg.instances); + message = "Instances of gitea-actions-runner can have `token` or `tokenFile`, not both."; + } { + assertion = wantsContainerRuntime -> hasDocker || hasPodman; + message = "Label configuration on gitea-actions-runner instance requires either docker or podman."; + } ]; + + systemd.services = let + mkRunnerService = name: instance: let + wantsContainerRuntime = hasDockerScheme instance; + wantsHost = hasHostScheme instance; + wantsDocker = wantsContainerRuntime && config.virtualisation.docker.enable; + wantsPodman = wantsContainerRuntime && config.virtualisation.podman.enable; + in + nameValuePair "gitea-runner-${escapeSystemdPath name}" { + inherit (instance) enable; + description = "Gitea Actions Runner"; + after = [ + "network-online.target" + ] ++ optionals (wantsDocker) [ + "docker.service" + ] ++ optionals (wantsPodman) [ + "podman.service" + ]; + wantedBy = [ + "multi-user.target" + ]; + environment = optionalAttrs (instance.token != null) { + TOKEN = "${instance.token}"; + } // optionalAttrs (wantsPodman) { + DOCKER_HOST = "unix:///run/podman/podman.sock"; + }; + path = with pkgs; [ + coreutils + ] ++ lib.optionals wantsHost instance.hostPackages; + serviceConfig = { + DynamicUser = true; + User = "gitea-runner"; + StateDirectory = "gitea-runner"; + WorkingDirectory = "-/var/lib/gitea-runner/${name}"; + ExecStartPre = pkgs.writeShellScript "gitea-register-runner-${name}" '' + export INSTANCE_DIR="$STATE_DIRECTORY/${name}" + mkdir -vp "$INSTANCE_DIR" + cd "$INSTANCE_DIR" + + # force reregistration on changed labels + export LABELS_FILE="$INSTANCE_DIR/.labels" + export LABELS_WANTED="$(echo ${escapeShellArg (concatStringsSep "\n" instance.labels)} | sort)" + export LABELS_CURRENT="$(cat $LABELS_FILE 2>/dev/null || echo 0)" + + if [ ! -e "$INSTANCE_DIR/.runner" ] || [ "$LABELS_WANTED" != "$LABELS_CURRENT" ]; then + # remove existing registration file, so that changing the labels forces a re-registation + rm -v "$INSTANCE_DIR/.runner" || true + + # perform the registration + ${cfg.package}/bin/act_runner register --no-interactive \ + --instance ${escapeShellArg instance.url} \ + --token "$TOKEN" \ + --name ${escapeShellArg instance.name} \ + --labels ${escapeShellArg (concatStringsSep "," instance.labels)} + + # and write back the configured labels + echo "$LABELS_WANTED" > "$LABELS_FILE" + fi + + ''; + ExecStart = "${cfg.package}/bin/act_runner daemon"; + SupplementaryGroups = optionals (wantsDocker) [ + "docker" + ] ++ optionals (wantsPodman) [ + "podman" + ]; + } // optionalAttrs (instance.tokenFile != null) { + EnvironmentFile = instance.tokenFile; + }; + }; + in mapAttrs' mkRunnerService cfg.instances; + }; +} diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 3d55995aba055..a7016bbee3a86 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -489,7 +489,7 @@ in "/share/postgresql" ]; - system.extraDependencies = lib.optional (cfg.checkConfig && pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) configFileCheck; + system.checks = lib.optional (cfg.checkConfig && pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) configFileCheck; systemd.services.postgresql = { description = "PostgreSQL Server"; diff --git a/nixos/modules/services/development/zammad.nix b/nixos/modules/services/development/zammad.nix index 0faeb4c0e9fab..7dd143eebf12f 100644 --- a/nixos/modules/services/development/zammad.nix +++ b/nixos/modules/services/development/zammad.nix @@ -254,7 +254,7 @@ in preStart = '' # Blindly copy the whole project here. chmod -R +w . - rm -rf ./public/assets/* + rm -rf ./public/assets/ rm -rf ./tmp/* rm -rf ./log/* cp -r --no-preserve=owner ${cfg.package}/* . diff --git a/nixos/modules/services/games/asf.nix b/nixos/modules/services/games/asf.nix index 7585d56b2d78f..f15d7077d965c 100644 --- a/nixos/modules/services/games/asf.nix +++ b/nixos/modules/services/games/asf.nix @@ -245,7 +245,7 @@ in rm -f www ${optionalString cfg.web-ui.enable '' - ln -s ${cfg.web-ui.package}/lib/dist www + ln -s ${cfg.web-ui.package}/ www ''} ''; }; diff --git a/nixos/modules/services/hardware/keyd.nix b/nixos/modules/services/hardware/keyd.nix index 64c769405fabc..d17b0e4303ef7 100644 --- a/nixos/modules/services/hardware/keyd.nix +++ b/nixos/modules/services/hardware/keyd.nix @@ -76,7 +76,9 @@ in ExecStart = "${pkgs.keyd}/bin/keyd"; Restart = "always"; - DynamicUser = true; + # TODO investigate why it doesn't work propeprly with DynamicUser + # See issue: https://github.com/NixOS/nixpkgs/issues/226346 + # DynamicUser = true; SupplementaryGroups = [ config.users.groups.input.name config.users.groups.uinput.name @@ -96,6 +98,7 @@ in ProtectHostname = true; PrivateUsers = true; PrivateMounts = true; + PrivateTmp = true; RestrictNamespaces = true; ProtectKernelLogs = true; ProtectKernelModules = true; @@ -104,7 +107,18 @@ in MemoryDenyWriteExecute = true; RestrictRealtime = true; LockPersonality = true; - ProtectProc = "noaccess"; + ProtectProc = "invisible"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + RestrictAddressFamilies = [ "AF_UNIX" ]; + RestrictSUIDSGID = true; + IPAddressDeny = [ "any" ]; + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProcSubset = "pid"; UMask = "0077"; }; }; diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix index 70c3ca50888c7..1eb51c50ff79c 100644 --- a/nixos/modules/services/logging/graylog.nix +++ b/nixos/modules/services/logging/graylog.nix @@ -37,8 +37,8 @@ in package = mkOption { type = types.package; - default = pkgs.graylog; - defaultText = literalExpression "pkgs.graylog"; + default = if versionOlder config.system.stateVersion "23.05" then pkgs.graylog-3_3 else pkgs.graylog-5_0; + defaultText = literalExpression (if versionOlder config.system.stateVersion "23.05" then "pkgs.graylog-3_3" else "pkgs.graylog-5_0"); description = lib.mdDoc "Graylog package to use."; }; diff --git a/nixos/modules/services/logging/vector.nix b/nixos/modules/services/logging/vector.nix index a923373d18612..f2edeabfc06f0 100644 --- a/nixos/modules/services/logging/vector.nix +++ b/nixos/modules/services/logging/vector.nix @@ -8,6 +8,8 @@ in options.services.vector = { enable = mkEnableOption (lib.mdDoc "Vector"); + package = mkPackageOptionMD pkgs "vector" { }; + journaldAccess = mkOption { type = types.bool; default = false; @@ -47,7 +49,7 @@ in ''; in { - ExecStart = "${pkgs.vector}/bin/vector --config ${validateConfig conf}"; + ExecStart = "${getExe cfg.package} --config ${validateConfig conf}"; DynamicUser = true; Restart = "no"; StateDirectory = "vector"; diff --git a/nixos/modules/services/mail/maddy.nix b/nixos/modules/services/mail/maddy.nix index e11a18cc14289..701d57f18e0c8 100644 --- a/nixos/modules/services/mail/maddy.nix +++ b/nixos/modules/services/mail/maddy.nix @@ -206,7 +206,7 @@ in { Server configuration, see [https://maddy.email](https://maddy.email) for more information. The default configuration of this module will setup - minimal maddy instance for mail transfer without TLS encryption. + minimal Maddy instance for mail transfer without TLS encryption. ::: {.note} This should not be used in a production environment. @@ -216,13 +216,24 @@ in { tls = { loader = mkOption { - type = with types; nullOr (enum [ "file" "off" ]); + type = with types; nullOr (enum [ "off" "file" "acme" ]); default = "off"; description = lib.mdDoc '' TLS certificates are obtained by modules called "certificate - loaders". Currently only the file loader is supported which reads - certificates from files specifying the options `keyPaths` and - `certPaths`. + loaders". + + The `file` loader module reads certificates from files specified by + the `certificates` option. + + Alternatively the `acme` module can be used to automatically obtain + certificates using the ACME protocol. + + Module configuration is done via the `tls.extraConfig` option. + + Secrets such as API keys or passwords should not be supplied in + plaintext. Instead the `secrets` option can be used to read secrets + at runtime as environment variables. Secrets can be referenced with + `{env:VAR}`. ''; }; @@ -261,11 +272,13 @@ in { extraConfig = mkOption { type = with types; nullOr lines; description = lib.mdDoc '' - Arguments for the specific certificate loader. Note that Maddy uses - secure defaults for the TLS configuration so there is no need to - change anything in most cases. - See [upstream manual](https://maddy.email/reference/tls/) for - available options. + Arguments for the specified certificate loader. + + In case the `tls` loader is set, the defaults are considered secure + and there is no need to change anything in most cases. + For available options see [upstream manual](https://maddy.email/reference/tls/). + + For ACME configuration, see [following page](https://maddy.email/reference/tls-acme). ''; default = ""; }; @@ -321,20 +334,41 @@ in { }); }; + secrets = lib.mkOption { + type = lib.types.path; + description = lib.mdDoc '' + A file containing the various secrets. Should be in the format + expected by systemd's `EnvironmentFile` directory. Secrets can be + referenced in the format `{env:VAR}`. + ''; + }; + }; }; config = mkIf cfg.enable { - assertions = [{ - assertion = cfg.tls.loader == "file" -> cfg.tls.certificates != []; - message = '' - If maddy is configured to use TLS, tls.certificates with attribute sets - of certPath and keyPath must be provided. - Read more about obtaining TLS certificates here: - https://maddy.email/tutorials/setting-up/#tls-certificates - ''; - }]; + assertions = [ + { + assertion = cfg.tls.loader == "file" -> cfg.tls.certificates != []; + message = '' + If Maddy is configured to use TLS, tls.certificates with attribute sets + of certPath and keyPath must be provided. + Read more about obtaining TLS certificates here: + https://maddy.email/tutorials/setting-up/#tls-certificates + ''; + } + { + assertion = cfg.tls.loader == "acme" -> cfg.tls.extraConfig != ""; + message = '' + If Maddy is configured to obtain TLS certificates using the ACME + loader, extra configuration options must be supplied via + tls.extraConfig option. + See upstream documentation for more details: + https://maddy.email/reference/tls-acme + ''; + } + ]; systemd = { @@ -345,6 +379,7 @@ in { User = cfg.user; Group = cfg.group; StateDirectory = [ "maddy" ]; + EnvironmentFile = lib.mkIf (cfg.secrets != null) "${cfg.secrets}"; }; restartTriggers = [ config.environment.etc."maddy/maddy.conf".source ]; wantedBy = [ "multi-user.target" ]; @@ -391,6 +426,12 @@ in { ) cfg.tls.certificates)} ${optionalString (cfg.tls.extraConfig != "") '' { ${cfg.tls.extraConfig} } ''} + '' else if (cfg.tls.loader == "acme") then '' + tls { + loader acme { + ${cfg.tls.extraConfig} + } + } '' else if (cfg.tls.loader == "off") then '' tls off '' else ""} diff --git a/nixos/modules/services/matrix/mjolnir.nix b/nixos/modules/services/matrix/mjolnir.nix index b6a3e5e8c7308..0824be663340b 100644 --- a/nixos/modules/services/matrix/mjolnir.nix +++ b/nixos/modules/services/matrix/mjolnir.nix @@ -30,7 +30,7 @@ let # these config files will be merged one after the other to build the final config configFiles = [ - "${pkgs.mjolnir}/share/mjolnir/config/default.yaml" + "${pkgs.mjolnir}/libexec/mjolnir/deps/mjolnir/config/default.yaml" moduleConfigFile ]; @@ -200,7 +200,7 @@ in wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = ''${pkgs.mjolnir}/bin/mjolnir''; + ExecStart = ''${pkgs.mjolnir}/bin/mjolnir --mjolnir-config ./config/default.yaml''; ExecStartPre = [ generateConfig ]; WorkingDirectory = cfg.dataPath; StateDirectory = "mjolnir"; diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix index 3343e94778a2b..17a7cca917f24 100644 --- a/nixos/modules/services/misc/etcd.nix +++ b/nixos/modules/services/misc/etcd.nix @@ -167,10 +167,11 @@ in { ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls; ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls; ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls; + ETCD_PEER_CLIENT_CERT_AUTH = toString cfg.peerClientCertAuth; ETCD_PEER_TRUSTED_CA_FILE = cfg.peerTrustedCaFile; ETCD_PEER_CERT_FILE = cfg.peerCertFile; ETCD_PEER_KEY_FILE = cfg.peerKeyFile; - ETCD_CLIENT_CERT_AUTH = toString cfg.peerClientCertAuth; + ETCD_CLIENT_CERT_AUTH = toString cfg.clientCertAuth; ETCD_TRUSTED_CA_FILE = cfg.trustedCaFile; ETCD_CERT_FILE = cfg.certFile; ETCD_KEY_FILE = cfg.keyFile; diff --git a/nixos/modules/services/misc/klipper.nix b/nixos/modules/services/misc/klipper.nix index 9f8539980aaab..ad881d4462a3c 100644 --- a/nixos/modules/services/misc/klipper.nix +++ b/nixos/modules/services/misc/klipper.nix @@ -23,6 +23,16 @@ in description = lib.mdDoc "The Klipper package."; }; + logFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/lib/klipper/klipper.log"; + description = lib.mdDoc '' + Path of the file Klipper should log to. + If `null`, it logs to stdout, which is not recommended by upstream. + ''; + }; + inputTTY = mkOption { type = types.path; default = "/run/klipper/tty"; @@ -151,7 +161,9 @@ in systemd.services.klipper = let klippyArgs = "--input-tty=${cfg.inputTTY}" - + optionalString (cfg.apiSocket != null) " --api-server=${cfg.apiSocket}"; + + optionalString (cfg.apiSocket != null) " --api-server=${cfg.apiSocket}" + + optionalString (cfg.logFile != null) " --logfile=${cfg.logFile}" + ; printerConfigPath = if cfg.mutableConfig then cfg.mutableConfigFolder + "/printer.cfg" @@ -177,7 +189,7 @@ in ''; serviceConfig = { - ExecStart = "${cfg.package}/lib/klipper/klippy.py ${klippyArgs} ${printerConfigPath}"; + ExecStart = "${cfg.package}/bin/klippy ${klippyArgs} ${printerConfigPath}"; RuntimeDirectory = "klipper"; StateDirectory = "klipper"; SupplementaryGroups = [ "dialout" ]; diff --git a/nixos/modules/services/misc/moonraker.nix b/nixos/modules/services/misc/moonraker.nix index 53638ded29634..7e306d718e082 100644 --- a/nixos/modules/services/misc/moonraker.nix +++ b/nixos/modules/services/misc/moonraker.nix @@ -72,7 +72,7 @@ in { example = { authorization = { trusted_clients = [ "10.0.0.0/24" ]; - cors_domains = [ "https://app.fluidd.xyz" ]; + cors_domains = [ "https://app.fluidd.xyz" "https://my.mainsail.xyz" ]; }; }; description = lib.mdDoc '' diff --git a/nixos/modules/services/misc/rshim.nix b/nixos/modules/services/misc/rshim.nix new file mode 100644 index 0000000000000..0fef2cc228c91 --- /dev/null +++ b/nixos/modules/services/misc/rshim.nix @@ -0,0 +1,99 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.rshim; + + rshimCommand = [ "${cfg.package}/bin/rshim" ] + ++ lib.optionals (cfg.backend != null) [ "--backend ${cfg.backend}" ] + ++ lib.optionals (cfg.device != null) [ "--device ${cfg.device}" ] + ++ lib.optionals (cfg.index != null) [ "--index ${builtins.toString cfg.index}" ] + ++ [ "--log-level ${builtins.toString cfg.log-level}" ] + ; +in +{ + options.services.rshim = { + enable = lib.mkEnableOption (lib.mdDoc "User-space rshim driver for the BlueField SoC"); + + package = lib.mkPackageOptionMD pkgs "rshim-user-space" { }; + + backend = lib.mkOption { + type = with lib.types; nullOr (enum [ "usb" "pcie" "pcie_lf" ]); + description = lib.mdDoc '' + Specify the backend to attach. If not specified, the driver will scan + all rshim backends unless the `device` option is given with a device + name specified. + ''; + default = null; + example = "pcie"; + }; + + device = lib.mkOption { + type = with lib.types; nullOr str; + description = lib.mdDoc '' + Specify the device name to attach. The backend driver can be deduced + from the device name, thus the `backend` option is not needed. + ''; + default = null; + example = "pcie-04:00.2"; + }; + + index = lib.mkOption { + type = with lib.types; nullOr int; + description = lib.mdDoc '' + Specify the index to create device path `/dev/rshim<index>`. It's also + used to create network interface name `tmfifo_net<index>`. This option + is needed when multiple rshim instances are running. + ''; + default = null; + example = 1; + }; + + log-level = lib.mkOption { + type = lib.types.int; + description = lib.mdDoc '' + Specify the log level (0:none, 1:error, 2:warning, 3:notice, 4:debug). + ''; + default = 2; + example = 4; + }; + + config = lib.mkOption { + type = with lib.types; attrsOf (oneOf [ int str ]); + description = lib.mdDoc '' + Structural setting for the rshim configuration file + (`/etc/rshim.conf`). It can be used to specify the static mapping + between rshim devices and rshim names. It can also be used to ignore + some rshim devices. + ''; + default = { }; + example = { + DISPLAY_LEVEL = 0; + rshim0 = "usb-2-1.7"; + none = "usb-1-1.4"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.etc = lib.mkIf (cfg.config != { }) { + "rshim.conf".text = lib.generators.toKeyValue + { mkKeyValue = lib.generators.mkKeyValueDefault { } " "; } + cfg.config; + }; + + systemd.services.rshim = { + after = [ "network.target" ]; + serviceConfig = { + Restart = "always"; + Type = "forking"; + ExecStart = [ + (lib.concatStringsSep " \\\n" rshimCommand) + ]; + KillMode = "control-group"; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; + + meta.maintainers = with lib.maintainers; [ nikstur ]; +} diff --git a/nixos/modules/services/misc/snapper.nix b/nixos/modules/services/misc/snapper.nix index 30ed30e7a8fa3..569433c3c71d1 100644 --- a/nixos/modules/services/misc/snapper.nix +++ b/nixos/modules/services/misc/snapper.nix @@ -4,6 +4,81 @@ with lib; let cfg = config.services.snapper; + + mkValue = v: + if isList v then "\"${concatMapStringsSep " " (escape [ "\\" " " ]) v}\"" + else if v == true then "yes" + else if v == false then "no" + else if isString v then "\"${v}\"" + else builtins.toJSON v; + + mkKeyValue = k: v: "${k}=${mkValue v}"; + + # "it's recommended to always specify the filesystem type" -- man snapper-configs + defaultOf = k: if k == "FSTYPE" then null else configOptions.${k}.default or null; + + safeStr = types.strMatching "[^\n\"]*" // { + description = "string without line breaks or quotes"; + descriptionClass = "conjunction"; + }; + + configOptions = { + SUBVOLUME = mkOption { + type = types.path; + description = lib.mdDoc '' + Path of the subvolume or mount point. + This path is a subvolume and has to contain a subvolume named + .snapshots. + See also man:snapper(8) section PERMISSIONS. + ''; + }; + + FSTYPE = mkOption { + type = types.enum [ "btrfs" ]; + default = "btrfs"; + description = lib.mdDoc '' + Filesystem type. Only btrfs is stable and tested. + ''; + }; + + ALLOW_GROUPS = mkOption { + type = types.listOf safeStr; + default = []; + description = lib.mdDoc '' + List of groups allowed to operate with the config. + + Also see the PERMISSIONS section in man:snapper(8). + ''; + }; + + ALLOW_USERS = mkOption { + type = types.listOf safeStr; + default = []; + example = [ "alice" ]; + description = lib.mdDoc '' + List of users allowed to operate with the config. "root" is always + implicitly included. + + Also see the PERMISSIONS section in man:snapper(8). + ''; + }; + + TIMELINE_CLEANUP = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Defines whether the timeline cleanup algorithm should be run for the config. + ''; + }; + + TIMELINE_CREATE = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Defines whether hourly snapshots should be created. + ''; + }; + }; in { @@ -52,49 +127,23 @@ in example = literalExpression '' { home = { - subvolume = "/home"; - extraConfig = ''' - ALLOW_USERS="alice" - TIMELINE_CREATE=yes - TIMELINE_CLEANUP=yes - '''; + SUBVOLUME = "/home"; + ALLOW_USERS = [ "alice" ]; + TIMELINE_CREATE = true; + TIMELINE_CLEANUP = true; }; } ''; description = lib.mdDoc '' - Subvolume configuration + Subvolume configuration. Any option mentioned in man:snapper-configs(5) + is valid here, even if NixOS doesn't document it. ''; type = types.attrsOf (types.submodule { - options = { - subvolume = mkOption { - type = types.path; - description = lib.mdDoc '' - Path of the subvolume or mount point. - This path is a subvolume and has to contain a subvolume named - .snapshots. - See also man:snapper(8) section PERMISSIONS. - ''; - }; - - fstype = mkOption { - type = types.enum [ "btrfs" ]; - default = "btrfs"; - description = lib.mdDoc '' - Filesystem type. Only btrfs is stable and tested. - ''; - }; + freeformType = types.attrsOf (types.oneOf [ (types.listOf safeStr) types.bool safeStr types.number ]); - extraConfig = mkOption { - type = types.lines; - default = ""; - description = lib.mdDoc '' - Additional configuration next to SUBVOLUME and FSTYPE. - See man:snapper-configs(5). - ''; - }; - }; + options = configOptions; }); }; }; @@ -117,11 +166,7 @@ in } // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({ - text = '' - ${subvolume.extraConfig} - FSTYPE="${subvolume.fstype}" - SUBVOLUME="${subvolume.subvolume}" - ''; + text = lib.generators.toKeyValue { inherit mkKeyValue; } (filterAttrs (k: v: v != defaultOf k) subvolume); })) cfg.configs) // (lib.optionalAttrs (cfg.filters != null) { "snapper/filters/default.txt".text = cfg.filters; @@ -181,5 +226,28 @@ in unitConfig.ConditionPathExists = "/etc/snapper/configs/root"; }; + assertions = + concatMap + (name: + let + sub = cfg.configs.${name}; + in + [ { assertion = !(sub ? extraConfig); + message = '' + The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it. + The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'. + ''; + } + ] ++ + map + (attr: { + assertion = !(hasAttr attr sub); + message = '' + The option definition `services.snapper.configs.${name}.${attr}' has been renamed to `services.snapper.configs.${name}.${toUpper attr}'. + ''; + }) + [ "fstype" "subvolume" ] + ) + (attrNames cfg.configs); }); } diff --git a/nixos/modules/services/monitoring/grafana-agent.nix b/nixos/modules/services/monitoring/grafana-agent.nix index a462e760f748b..13604ff77c686 100644 --- a/nixos/modules/services/monitoring/grafana-agent.nix +++ b/nixos/modules/services/monitoring/grafana-agent.nix @@ -65,7 +65,6 @@ in agent.enabled = true; agent.scrape_integration = true; node_exporter.enabled = true; - replace_instance_label = true; }; } ''; @@ -122,7 +121,6 @@ in agent.enabled = mkDefault true; agent.scrape_integration = mkDefault true; node_exporter.enabled = mkDefault true; - replace_instance_label = mkDefault true; }; }; diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix index 92c870bb23f1d..bd0dea83e1a89 100644 --- a/nixos/modules/services/monitoring/netdata.nix +++ b/nixos/modules/services/monitoring/netdata.nix @@ -169,6 +169,20 @@ in { See: <https://learn.netdata.cloud/docs/agent/anonymous-statistics> ''; }; + + deadlineBeforeStopSec = mkOption { + type = types.int; + default = 120; + description = lib.mdDoc '' + In order to detect when netdata is misbehaving, we run a concurrent task pinging netdata (wait-for-netdata-up) + in the systemd unit. + + If after a while, this task does not succeed, we stop the unit and mark it as failed. + + You can control this deadline in seconds with this option, it's useful to bump it + if you have (1) a lot of data (2) doing upgrades (3) have low IOPS/throughput. + ''; + }; }; }; @@ -205,7 +219,7 @@ in { while [ "$(${pkgs.netdata}/bin/netdatacli ping)" != pong ]; do sleep 0.5; done ''; - TimeoutStopSec = 60; + TimeoutStopSec = cfg.deadlineBeforeStopSec; Restart = "on-failure"; # User and group User = cfg.user; diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix index 0c0931d3d295a..987f17c2c6e68 100644 --- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix +++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix @@ -6,10 +6,12 @@ let cfg = config.services.prometheus.alertmanager; mkConfigFile = pkgs.writeText "alertmanager.yml" (builtins.toJSON cfg.configuration); - checkedConfig = file: pkgs.runCommand "checked-config" { buildInputs = [ cfg.package ]; } '' - ln -s ${file} $out - amtool check-config $out - ''; + checkedConfig = file: + if cfg.checkConfig then + pkgs.runCommand "checked-config" { buildInputs = [ cfg.package ]; } '' + ln -s ${file} $out + amtool check-config $out + '' else file; alertmanagerYml = let yml = if cfg.configText != null then @@ -70,6 +72,20 @@ in { ''; }; + checkConfig = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Check configuration with `amtool check-config`. The call to `amtool` is + subject to sandboxing by Nix. + + If you use credentials stored in external files + (`environmentFile`, etc), + they will not be visible to `amtool` + and it will report errors, despite a correct configuration. + ''; + }; + logFormat = mkOption { type = types.nullOr types.str; default = null; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.md b/nixos/modules/services/monitoring/prometheus/exporters.md index c085e46d20d7d..34fadecadc749 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.md +++ b/nixos/modules/services/monitoring/prometheus/exporters.md @@ -76,7 +76,7 @@ example: directory, which will be called postfix.nix and contains all exporter specific options and configuration: ``` - # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix + # nixpkgs/nixos/modules/services/prometheus/exporters/postfix.nix { config, lib, pkgs, options }: with lib; diff --git a/nixos/modules/services/monitoring/uptime-kuma.nix b/nixos/modules/services/monitoring/uptime-kuma.nix index 5f803d57b5e97..7027046b24253 100644 --- a/nixos/modules/services/monitoring/uptime-kuma.nix +++ b/nixos/modules/services/monitoring/uptime-kuma.nix @@ -43,6 +43,8 @@ in services.uptime-kuma.settings = { DATA_DIR = "/var/lib/uptime-kuma/"; NODE_ENV = mkDefault "production"; + HOST = mkDefault "127.0.0.1"; + PORT = mkDefault "3001"; }; systemd.services.uptime-kuma = { diff --git a/nixos/modules/services/network-filesystems/webdav-server-rs.nix b/nixos/modules/services/network-filesystems/webdav-server-rs.nix index 9ea304111819b..34e717025e645 100644 --- a/nixos/modules/services/network-filesystems/webdav-server-rs.nix +++ b/nixos/modules/services/network-filesystems/webdav-server-rs.nix @@ -28,6 +28,12 @@ in description = lib.mdDoc "Group to run under when setuid is not enabled."; }; + debug = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Enable debug mode."; + }; + settings = mkOption { type = format.type; default = { }; @@ -111,7 +117,7 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${pkgs.webdav-server-rs}/bin/webdav-server -c ${cfg.configFile}"; + ExecStart = "${pkgs.webdav-server-rs}/bin/webdav-server ${lib.optionalString cfg.debug "--debug"} -c ${cfg.configFile}"; CapabilityBoundingSet = [ "CAP_SETUID" diff --git a/nixos/modules/services/networking/alice-lg.nix b/nixos/modules/services/networking/alice-lg.nix new file mode 100644 index 0000000000000..06b9ac89f12fc --- /dev/null +++ b/nixos/modules/services/networking/alice-lg.nix @@ -0,0 +1,101 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.alice-lg; + settingsFormat = pkgs.formats.ini { }; +in +{ + options = { + services.alice-lg = { + enable = mkEnableOption (lib.mdDoc "Alice Looking Glass"); + + package = mkPackageOptionMD pkgs "alice-lg" { }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = lib.mdDoc '' + alice-lg configuration, for configuration options see the example on [github](https://github.com/alice-lg/alice-lg/blob/main/etc/alice-lg/alice.example.conf) + ''; + example = literalExpression '' + { + server = { + # configures the built-in webserver and provides global application settings + listen_http = "127.0.0.1:7340"; + enable_prefix_lookup = true; + asn = 9033; + store_backend = postgres; + routes_store_refresh_parallelism = 5; + neighbors_store_refresh_parallelism = 10000; + routes_store_refresh_interval = 5; + neighbors_store_refresh_interval = 5; + }; + postgres = { + url = "postgres://postgres:postgres@localhost:5432/alice"; + min_connections = 2; + max_connections = 128; + }; + pagination = { + routes_filtered_page_size = 250; + routes_accepted_page_size = 250; + routes_not_exported_page_size = 250; + }; + } + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment = { + etc."alice-lg/alice.conf".source = settingsFormat.generate "alice-lg.conf" cfg.settings; + }; + systemd.services = { + alice-lg = { + wants = [ "network.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + description = "Alice Looking Glass"; + serviceConfig = { + DynamicUser = true; + Type = "simple"; + Restart = "on-failure"; + RestartSec = 15; + ExecStart = "${cfg.package}/bin/alice-lg"; + StateDirectoryMode = "0700"; + UMask = "0007"; + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + ProtectSystem = "strict"; + PrivateTmp = true; + PrivateDevices = true; + PrivateUsers = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_INET AF_INET6" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + PrivateMounts = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "~@clock @privileged @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap"; + BindReadOnlyPaths = [ + "-/etc/resolv.conf" + "-/etc/nsswitch.conf" + "-/etc/ssl/certs" + "-/etc/static/ssl/certs" + "-/etc/hosts" + "-/etc/localtime" + ]; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/birdwatcher.nix b/nixos/modules/services/networking/birdwatcher.nix new file mode 100644 index 0000000000000..a129b7a2b4cf5 --- /dev/null +++ b/nixos/modules/services/networking/birdwatcher.nix @@ -0,0 +1,129 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.birdwatcher; +in +{ + options = { + services.birdwatcher = { + package = mkOption { + type = types.package; + default = pkgs.birdwatcher; + defaultText = literalExpression "pkgs.birdwatcher"; + description = lib.mdDoc "The Birdwatcher package to use."; + }; + enable = mkEnableOption (lib.mdDoc "Birdwatcher"); + flags = mkOption { + default = [ ]; + type = types.listOf types.str; + example = [ "-worker-pool-size 16" "-6" ]; + description = lib.mdDoc '' + Flags to append to the program call + ''; + }; + + settings = mkOption { + type = types.lines; + default = { }; + description = lib.mdDoc '' + birdwatcher configuration, for configuration options see the example on [github](https://github.com/alice-lg/birdwatcher/blob/master/etc/birdwatcher/birdwatcher.conf) + ''; + example = literalExpression '' + [server] + allow_from = [] + allow_uncached = false + modules_enabled = ["status", + "protocols", + "protocols_bgp", + "protocols_short", + "routes_protocol", + "routes_peer", + "routes_table", + "routes_table_filtered", + "routes_table_peer", + "routes_filtered", + "routes_prefixed", + "routes_noexport", + "routes_pipe_filtered_count", + "routes_pipe_filtered" + ] + + [status] + reconfig_timestamp_source = "bird" + reconfig_timestamp_match = "# created: (.*)" + + filter_fields = [] + + [bird] + listen = "0.0.0.0:29184" + config = "/etc/bird/bird2.conf" + birdc = "''${pkgs.bird}/bin/birdc" + ttl = 5 # time to live (in minutes) for caching of cli output + + [parser] + filter_fields = [] + + [cache] + use_redis = false # if not using redis cache, activate housekeeping to save memory! + + [housekeeping] + interval = 5 + force_release_memory = true + ''; + }; + }; + }; + + config = + let flagsStr = escapeShellArgs cfg.flags; + in lib.mkIf cfg.enable { + environment.etc."birdwatcher/birdwatcher.conf".source = pkgs.writeTextFile { + name = "birdwatcher.conf"; + text = cfg.settings; + }; + systemd.services = { + birdwatcher = { + wants = [ "network.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + description = "Birdwatcher"; + serviceConfig = { + Type = "simple"; + Restart = "on-failure"; + RestartSec = 15; + ExecStart = "${cfg.package}/bin/birdwatcher"; + StateDirectoryMode = "0700"; + UMask = "0117"; + NoNewPrivileges = true; + ProtectSystem = "strict"; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + PrivateMounts = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "~@clock @privileged @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap"; + BindReadOnlyPaths = [ + "-/etc/resolv.conf" + "-/etc/nsswitch.conf" + "-/etc/ssl/certs" + "-/etc/static/ssl/certs" + "-/etc/hosts" + "-/etc/localtime" + ]; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/harmonia.nix b/nixos/modules/services/networking/harmonia.nix new file mode 100644 index 0000000000000..144fa6c708e28 --- /dev/null +++ b/nixos/modules/services/networking/harmonia.nix @@ -0,0 +1,88 @@ +{ config, pkgs, lib, ... }: +let + cfg = config.services.harmonia; + format = pkgs.formats.toml { }; +in +{ + options = { + services.harmonia = { + enable = lib.mkEnableOption (lib.mdDoc "Harmonia: Nix binary cache written in Rust"); + + signKeyPath = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = lib.mdDoc "Path to the signing key that will be used for signing the cache"; + }; + + package = lib.mkPackageOptionMD pkgs "harmonia" { }; + + settings = lib.mkOption { + inherit (format) type; + default = { }; + description = lib.mdDoc '' + Settings to merge with the default configuration. + For the list of the default configuration, see <https://github.com/nix-community/harmonia/tree/master#configuration>. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.harmonia = { + description = "harmonia binary cache service"; + + requires = [ "nix-daemon.socket" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + CONFIG_FILE = format.generate "harmonia.toml" cfg.settings; + SIGN_KEY_PATH = lib.mkIf (cfg.signKeyPath != null) "%d/sign-key"; + # Note: it's important to set this for nix-store, because it wants to use + # $HOME in order to use a temporary cache dir. bizarre failures will occur + # otherwise + HOME = "/run/harmonia"; + }; + + serviceConfig = { + ExecStart = lib.getExe cfg.package; + User = "harmonia"; + Group = "harmonia"; + DynamicUser = true; + PrivateUsers = true; + DeviceAllow = [ "" ]; + UMask = "0066"; + RuntimeDirectory = "harmonia"; + LoadCredential = lib.mkIf (cfg.signKeyPath != null) [ "sign-key:${cfg.signKeyPath}" ]; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + CapabilityBoundingSet = ""; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectKernelLogs = true; + ProtectHostname = true; + ProtectClock = true; + RestrictRealtime = true; + MemoryDenyWriteExecute = true; + ProcSubset = "pid"; + ProtectProc = "invisible"; + RestrictNamespaces = true; + SystemCallArchitectures = "native"; + PrivateNetwork = false; + PrivateTmp = true; + PrivateDevices = true; + PrivateMounts = true; + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + LockPersonality = true; + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + LimitNOFILE = 65536; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index 89ddf82152993..fd9650a058c2f 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -365,9 +365,6 @@ in "hmac-sha2-512-etm@openssh.com" "hmac-sha2-256-etm@openssh.com" "umac-128-etm@openssh.com" - "hmac-sha2-512" - "hmac-sha2-256" - "umac-128@openssh.com" ]; description = lib.mdDoc '' Allowed MACs diff --git a/nixos/modules/services/networking/syncplay.nix b/nixos/modules/services/networking/syncplay.nix index 726f65671072d..0a66d93bf153a 100644 --- a/nixos/modules/services/networking/syncplay.nix +++ b/nixos/modules/services/networking/syncplay.nix @@ -8,7 +8,8 @@ let cmdArgs = [ "--port" cfg.port ] ++ optionals (cfg.salt != null) [ "--salt" cfg.salt ] - ++ optionals (cfg.certDir != null) [ "--tls" cfg.certDir ]; + ++ optionals (cfg.certDir != null) [ "--tls" cfg.certDir ] + ++ cfg.extraArgs; in { @@ -33,7 +34,22 @@ in default = null; description = lib.mdDoc '' Salt to allow room operator passwords generated by this server - instance to still work when the server is restarted. + instance to still work when the server is restarted. The salt will be + readable in the nix store and the processlist. If this is not + intended use `saltFile` instead. Mutually exclusive with + <option>services.syncplay.saltFile</option>. + ''; + }; + + saltFile = mkOption { + type = types.nullOr types.path; + default = null; + description = lib.mdDoc '' + Path to the file that contains the server salt. This allows room + operator passwords generated by this server instance to still work + when the server is restarted. `null`, the server doesn't load the + salt from a file. Mutually exclusive with + <option>services.syncplay.salt</option>. ''; }; @@ -46,6 +62,14 @@ in ''; }; + extraArgs = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Additional arguments to be passed to the service. + ''; + }; + user = mkOption { type = types.str; default = "nobody"; @@ -74,21 +98,31 @@ in }; config = mkIf cfg.enable { + assertions = [ + { + assertion = cfg.salt == null || cfg.saltFile == null; + message = "services.syncplay.salt and services.syncplay.saltFile are mutually exclusive."; + } + ]; systemd.services.syncplay = { description = "Syncplay Service"; - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; serviceConfig = { User = cfg.user; Group = cfg.group; - LoadCredential = lib.mkIf (cfg.passwordFile != null) "password:${cfg.passwordFile}"; + LoadCredential = lib.optional (cfg.passwordFile != null) "password:${cfg.passwordFile}" + ++ lib.optional (cfg.saltFile != null) "salt:${cfg.saltFile}"; }; script = '' ${lib.optionalString (cfg.passwordFile != null) '' export SYNCPLAY_PASSWORD=$(cat "''${CREDENTIALS_DIRECTORY}/password") ''} + ${lib.optionalString (cfg.saltFile != null) '' + export SYNCPLAY_SALT=$(cat "''${CREDENTIALS_DIRECTORY}/salt") + ''} exec ${pkgs.syncplay-nogui}/bin/syncplay-server ${escapeShellArgs cmdArgs} ''; }; diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 3d41fe4013ea9..a8e6b0fcf6fbe 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -7,25 +7,24 @@ let opt = options.services.syncthing; defaultUser = "syncthing"; defaultGroup = defaultUser; + settingsFormat = pkgs.formats.json { }; - devices = mapAttrsToList (name: device: { + devices = mapAttrsToList (_: device: device // { deviceID = device.id; - inherit (device) name addresses introducer autoAcceptFolders; - }) cfg.devices; - - folders = mapAttrsToList ( _: folder: { - inherit (folder) path id label type; - devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices; - rescanIntervalS = folder.rescanInterval; - fsWatcherEnabled = folder.watch; - fsWatcherDelayS = folder.watchDelay; - ignorePerms = folder.ignorePerms; - ignoreDelete = folder.ignoreDelete; - versioning = folder.versioning; - }) (filterAttrs ( - _: folder: - folder.enable - ) cfg.folders); + }) cfg.settings.devices; + + folders = mapAttrsToList (_: folder: folder // + throwIf (folder?rescanInterval || folder?watch || folder?watchDelay) '' + The options services.syncthing.settings.folders.<name>.{rescanInterval,watch,watchDelay} + were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead. + '' { + devices = map (device: + if builtins.isString device then + { deviceId = cfg.settings.devices.${device}.id; } + else + device + ) folder.devices; + }) cfg.settings.folders; updateConfig = pkgs.writers.writeDash "merge-syncthing-config" '' set -efu @@ -54,10 +53,10 @@ let old_cfg=$(curl ${cfg.guiAddress}/rest/config) # generate the new config by merging with the NixOS config options - new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * { - "devices": (${builtins.toJSON devices}${optionalString (cfg.devices == {} || ! cfg.overrideDevices) " + .devices"}), - "folders": (${builtins.toJSON folders}${optionalString (cfg.folders == {} || ! cfg.overrideFolders) " + .folders"}) - } * ${builtins.toJSON cfg.extraOptions}') + new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c ${escapeShellArg ''. * ${builtins.toJSON cfg.settings} * { + "devices": (${builtins.toJSON devices}${optionalString (cfg.settings.devices == {} || ! cfg.overrideDevices) " + .devices"}), + "folders": (${builtins.toJSON folders}${optionalString (cfg.settings.folders == {} || ! cfg.overrideFolders) " + .folders"}) + }''}) # send the new config curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config @@ -99,287 +98,282 @@ in { default = true; description = mdDoc '' Whether to delete the devices which are not configured via the - [devices](#opt-services.syncthing.devices) option. + [devices](#opt-services.syncthing.settings.devices) option. If set to `false`, devices added via the web interface will persist and will have to be deleted manually. ''; }; - devices = mkOption { - default = {}; - description = mdDoc '' - Peers/devices which Syncthing should communicate with. - - Note that you can still add devices manually, but those changes - will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices) - is enabled. - ''; - example = { - bigbox = { - id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; - addresses = [ "tcp://192.168.0.10:51820" ]; - }; - }; - type = types.attrsOf (types.submodule ({ name, ... }: { - options = { - - name = mkOption { - type = types.str; - default = name; - description = lib.mdDoc '' - The name of the device. - ''; - }; - - addresses = mkOption { - type = types.listOf types.str; - default = []; - description = lib.mdDoc '' - The addresses used to connect to the device. - If this is left empty, dynamic configuration is attempted. - ''; - }; - - id = mkOption { - type = types.str; - description = mdDoc '' - The device ID. See <https://docs.syncthing.net/dev/device-ids.html>. - ''; - }; - - introducer = mkOption { - type = types.bool; - default = false; - description = mdDoc '' - Whether the device should act as an introducer and be allowed - to add folders on this computer. - See <https://docs.syncthing.net/users/introducer.html>. - ''; - }; - - autoAcceptFolders = mkOption { - type = types.bool; - default = false; - description = mdDoc '' - Automatically create or share folders that this device advertises at the default path. - See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>. - ''; - }; - - }; - })); - }; - overrideFolders = mkOption { type = types.bool; default = true; description = mdDoc '' Whether to delete the folders which are not configured via the - [folders](#opt-services.syncthing.folders) option. + [folders](#opt-services.syncthing.settings.folders) option. If set to `false`, folders added via the web interface will persist and will have to be deleted manually. ''; }; - folders = mkOption { - default = {}; - description = mdDoc '' - Folders which should be shared by Syncthing. - - Note that you can still add folders manually, but those changes - will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders) - is enabled. - ''; - example = literalExpression '' - { - "/home/user/sync" = { - id = "syncme"; - devices = [ "bigbox" ]; - }; - } - ''; - type = types.attrsOf (types.submodule ({ name, ... }: { + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type; options = { - - enable = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - Whether to share this folder. - This option is useful when you want to define all folders - in one place, but not every machine should share all folders. + # global options + options = mkOption { + default = {}; + description = mdDoc '' + The options element contains all other global configuration options ''; - }; + type = types.submodule ({ name, ... }: { + freeformType = settingsFormat.type; + options = { + localAnnounceEnabled = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to send announcements to the local LAN, also use such announcements to find other devices. + ''; + }; - path = mkOption { - # TODO for release 23.05: allow relative paths again and set - # working directory to cfg.dataDir - type = types.str // { - check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/"); - description = types.str.description + " starting with / or ~/"; - }; - default = name; - description = lib.mdDoc '' - The path to the folder which should be shared. - Only absolute paths (starting with `/`) and paths relative to - the [user](#opt-services.syncthing.user)'s home directory - (starting with `~/`) are allowed. - ''; - }; + localAnnouncePort = mkOption { + type = types.int; + default = 21027; + description = lib.mdDoc '' + The port on which to listen and send IPv4 broadcast announcements to. + ''; + }; - id = mkOption { - type = types.str; - default = name; - description = lib.mdDoc '' - The ID of the folder. Must be the same on all devices. - ''; - }; + relaysEnabled = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + When true, relays will be connected to and potentially used for device to device connections. + ''; + }; - label = mkOption { - type = types.str; - default = name; - description = lib.mdDoc '' - The label of the folder. - ''; + urAccepted = mkOption { + type = types.int; + default = 0; + description = lib.mdDoc '' + Whether the user has accepted to submit anonymous usage data. + The default, 0, mean the user has not made a choice, and Syncthing will ask at some point in the future. + "-1" means no, a number above zero means that that version of usage reporting has been accepted. + ''; + }; + + limitBandwidthInLan = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to apply bandwidth limits to devices in the same broadcast domain as the local device. + ''; + }; + + maxFolderConcurrency = mkOption { + type = types.int; + default = 0; + description = lib.mdDoc '' + This option controls how many folders may concurrently be in I/O-intensive operations such as syncing or scanning. + The mechanism is described in detail in a [separate chapter](https://docs.syncthing.net/advanced/option-max-concurrency.html). + ''; + }; + }; + }); }; + # device settings devices = mkOption { - type = types.listOf types.str; - default = []; + default = {}; description = mdDoc '' - The devices this folder should be shared with. Each device must - be defined in the [devices](#opt-services.syncthing.devices) option. - ''; - }; + Peers/devices which Syncthing should communicate with. - versioning = mkOption { - default = null; - description = mdDoc '' - How to keep changed/deleted files with Syncthing. - There are 4 different types of versioning with different parameters. - See <https://docs.syncthing.net/users/versioning.html>. - ''; - example = literalExpression '' - [ - { - versioning = { - type = "simple"; - params.keep = "10"; - }; - } - { - versioning = { - type = "trashcan"; - params.cleanoutDays = "1000"; - }; - } - { - versioning = { - type = "staggered"; - fsPath = "/syncthing/backup"; - params = { - cleanInterval = "3600"; - maxAge = "31536000"; - }; - }; - } - { - versioning = { - type = "external"; - params.versionsPath = pkgs.writers.writeBash "backup" ''' - folderpath="$1" - filepath="$2" - rm -rf "$folderpath/$filepath" - '''; - }; - } - ] + Note that you can still add devices manually, but those changes + will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices) + is enabled. ''; - type = with types; nullOr (submodule { + example = { + bigbox = { + id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; + addresses = [ "tcp://192.168.0.10:51820" ]; + }; + }; + type = types.attrsOf (types.submodule ({ name, ... }: { + freeformType = settingsFormat.type; options = { - type = mkOption { - type = enum [ "external" "simple" "staggered" "trashcan" ]; - description = mdDoc '' - The type of versioning. - See <https://docs.syncthing.net/users/versioning.html>. + + name = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + The name of the device. ''; }; - fsPath = mkOption { - default = ""; - type = either str path; + + id = mkOption { + type = types.str; description = mdDoc '' - Path to the versioning folder. - See <https://docs.syncthing.net/users/versioning.html>. + The device ID. See <https://docs.syncthing.net/dev/device-ids.html>. ''; }; - params = mkOption { - type = attrsOf (either str path); + + autoAcceptFolders = mkOption { + type = types.bool; + default = false; description = mdDoc '' - The parameters for versioning. Structure depends on - [versioning.type](#opt-services.syncthing.folders._name_.versioning.type). - See <https://docs.syncthing.net/users/versioning.html>. + Automatically create or share folders that this device advertises at the default path. + See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>. ''; }; + }; - }); + })); }; - rescanInterval = mkOption { - type = types.int; - default = 3600; - description = lib.mdDoc '' - How often the folder should be rescanned for changes. - ''; - }; + # folder settings + folders = mkOption { + default = {}; + description = mdDoc '' + Folders which should be shared by Syncthing. - type = mkOption { - type = types.enum [ "sendreceive" "sendonly" "receiveonly" "receiveencrypted" ]; - default = "sendreceive"; - description = lib.mdDoc '' - Whether to only send changes for this folder, only receive them - or both. `receiveencrypted` can be used for untrusted devices. See - <https://docs.syncthing.net/users/untrusted.html> for reference. + Note that you can still add folders manually, but those changes + will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders) + is enabled. ''; - }; - - watch = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - Whether the folder should be watched for changes by inotify. + example = literalExpression '' + { + "/home/user/sync" = { + id = "syncme"; + devices = [ "bigbox" ]; + }; + } ''; - }; + type = types.attrsOf (types.submodule ({ name, ... }: { + freeformType = settingsFormat.type; + options = { - watchDelay = mkOption { - type = types.int; - default = 10; - description = lib.mdDoc '' - The delay after an inotify event is triggered. - ''; - }; + enable = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to share this folder. + This option is useful when you want to define all folders + in one place, but not every machine should share all folders. + ''; + }; - ignorePerms = mkOption { - type = types.bool; - default = true; - description = lib.mdDoc '' - Whether to ignore permission changes. - ''; - }; + path = mkOption { + # TODO for release 23.05: allow relative paths again and set + # working directory to cfg.dataDir + type = types.str // { + check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/"); + description = types.str.description + " starting with / or ~/"; + }; + default = name; + description = lib.mdDoc '' + The path to the folder which should be shared. + Only absolute paths (starting with `/`) and paths relative to + the [user](#opt-services.syncthing.user)'s home directory + (starting with `~/`) are allowed. + ''; + }; - ignoreDelete = mkOption { - type = types.bool; - default = false; - description = mdDoc '' - Whether to skip deleting files that are deleted by peers. - See <https://docs.syncthing.net/advanced/folder-ignoredelete.html>. - ''; + id = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + The ID of the folder. Must be the same on all devices. + ''; + }; + + label = mkOption { + type = types.str; + default = name; + description = lib.mdDoc '' + The label of the folder. + ''; + }; + + devices = mkOption { + type = types.listOf types.str; + default = []; + description = mdDoc '' + The devices this folder should be shared with. Each device must + be defined in the [devices](#opt-services.syncthing.settings.devices) option. + ''; + }; + + versioning = mkOption { + default = null; + description = mdDoc '' + How to keep changed/deleted files with Syncthing. + There are 4 different types of versioning with different parameters. + See <https://docs.syncthing.net/users/versioning.html>. + ''; + example = literalExpression '' + [ + { + versioning = { + type = "simple"; + params.keep = "10"; + }; + } + { + versioning = { + type = "trashcan"; + params.cleanoutDays = "1000"; + }; + } + { + versioning = { + type = "staggered"; + fsPath = "/syncthing/backup"; + params = { + cleanInterval = "3600"; + maxAge = "31536000"; + }; + }; + } + { + versioning = { + type = "external"; + params.versionsPath = pkgs.writers.writeBash "backup" ''' + folderpath="$1" + filepath="$2" + rm -rf "$folderpath/$filepath" + '''; + }; + } + ] + ''; + type = with types; nullOr (submodule { + freeformType = settingsFormat.type; + options = { + type = mkOption { + type = enum [ "external" "simple" "staggered" "trashcan" ]; + description = mdDoc '' + The type of versioning. + See <https://docs.syncthing.net/users/versioning.html>. + ''; + }; + }; + }); + }; + + copyOwnershipFromParent = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + On Unix systems, tries to copy file/folder ownership from the parent directory (the directory it’s located in). + Requires running Syncthing as a privileged user, or granting it additional capabilities (e.g. CAP_CHOWN on Linux). + ''; + }; + }; + })); }; - }; - })); - }; - extraOptions = mkOption { - type = types.addCheck (pkgs.formats.json {}).type isAttrs; + }; + }; default = {}; description = mdDoc '' Extra configuration options for Syncthing. @@ -530,6 +524,10 @@ in { This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher". It can be enabled on a per-folder basis through the web interface. '') + (mkRenamedOptionModule [ "services" "syncthing" "extraOptions" ] [ "services" "syncthing" "settings" ]) + (mkRenamedOptionModule [ "services" "syncthing" "folders" ] [ "services" "syncthing" "settings" "folders" ]) + (mkRenamedOptionModule [ "services" "syncthing" "devices" ] [ "services" "syncthing" "settings" "devices" ]) + (mkRenamedOptionModule [ "services" "syncthing" "options" ] [ "services" "syncthing" "settings" "options" ]) ] ++ map (o: mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ] ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"]; @@ -615,9 +613,7 @@ in { ]; }; }; - syncthing-init = mkIf ( - cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {} - ) { + syncthing-init = mkIf (cfg.settings != {}) { description = "Syncthing configuration updater"; requisite = [ "syncthing.service" ]; after = [ "syncthing.service" ]; diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix index 8b025228cc1ff..21473388d76e1 100644 --- a/nixos/modules/services/networking/wireguard.nix +++ b/nixos/modules/services/networking/wireguard.nix @@ -170,10 +170,22 @@ let # peer options - peerOpts = { + peerOpts = self: { options = { + name = mkOption { + default = + replaceStrings + [ "/" "-" " " "+" "=" ] + [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ] + self.config.publicKey; + defaultText = literalExpression "publicKey"; + example = "bernd"; + type = types.str; + description = lib.mdDoc "Name used to derive peer unit name."; + }; + publicKey = mkOption { example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; type = types.singleLineStr; @@ -313,15 +325,11 @@ let ''; }; - peerUnitServiceName = interfaceName: publicKey: dynamicRefreshEnabled: + peerUnitServiceName = interfaceName: peerName: dynamicRefreshEnabled: let - keyToUnitName = replaceStrings - [ "/" "-" " " "+" "=" ] - [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ]; - unitName = keyToUnitName publicKey; refreshSuffix = optionalString dynamicRefreshEnabled "-refresh"; in - "wireguard-${interfaceName}-peer-${unitName}${refreshSuffix}"; + "wireguard-${interfaceName}-peer-${peerName}${refreshSuffix}"; generatePeerUnit = { interfaceName, interfaceCfg, peer }: let @@ -337,10 +345,11 @@ let # We generate a different name (a `-refresh` suffix) when `dynamicEndpointRefreshSeconds` # to avoid that the same service switches `Type` (`oneshot` vs `simple`), # with the intent to make scripting more obvious. - serviceName = peerUnitServiceName interfaceName peer.publicKey dynamicRefreshEnabled; + serviceName = peerUnitServiceName interfaceName peer.name dynamicRefreshEnabled; in nameValuePair serviceName { - description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}"; + description = "WireGuard Peer - ${interfaceName} - ${peer.name}" + + optionalString (peer.name != peer.publicKey) " (${peer.publicKey})"; requires = [ "wireguard-${interfaceName}.service" ]; wants = [ "network-online.target" ]; after = [ "wireguard-${interfaceName}.service" "network-online.target" ]; @@ -418,7 +427,7 @@ let # the target is required to start new peer units when they are added generateInterfaceTarget = name: values: let - mkPeerUnit = peer: (peerUnitServiceName name peer.publicKey (peer.dynamicEndpointRefreshSeconds != 0)) + ".service"; + mkPeerUnit = peer: (peerUnitServiceName name peer.name (peer.dynamicEndpointRefreshSeconds != 0)) + ".service"; in nameValuePair "wireguard-${name}" rec { diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix index 8c932b0e6f78f..4cda06ed42481 100644 --- a/nixos/modules/services/system/cloud-init.nix +++ b/nixos/modules/services/system/cloud-init.nix @@ -90,7 +90,7 @@ in }; - config = { + config = mkIf cfg.enable { services.cloud-init.settings = { system_info = mkDefault { distro = "nixos"; @@ -142,7 +142,6 @@ in "power-state-change" ]; }; - } // (mkIf cfg.enable { environment.etc."cloud/cloud.cfg" = if cfg.config == "" then @@ -225,5 +224,7 @@ in description = "Cloud-config availability"; requires = [ "cloud-init-local.service" "cloud-init.service" ]; }; - }); + }; + + meta.maintainers = [ maintainers.zimbatm ]; } diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix index de3d077daec96..003f7b2613b76 100644 --- a/nixos/modules/services/torrent/deluge.nix +++ b/nixos/modules/services/torrent/deluge.nix @@ -93,7 +93,7 @@ in { `true`. It does NOT apply to the daemon port nor the web UI port. To access those - ports secuerly check the documentation + ports securely check the documentation <https://dev.deluge-torrent.org/wiki/UserGuide/ThinClient#CreateSSHTunnel> or use a VPN or configure certificates for deluge. ''; diff --git a/nixos/modules/services/web-apps/mainsail.nix b/nixos/modules/services/web-apps/mainsail.nix new file mode 100644 index 0000000000000..f335d9b015d49 --- /dev/null +++ b/nixos/modules/services/web-apps/mainsail.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.mainsail; + moonraker = config.services.moonraker; +in +{ + options.services.mainsail = { + enable = mkEnableOption (lib.mdDoc "a modern and responsive user interface for Klipper"); + + package = mkOption { + type = types.package; + description = lib.mdDoc "Mainsail package to be used in the module"; + default = pkgs.mainsail; + defaultText = literalExpression "pkgs.mainsail"; + }; + + hostName = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc "Hostname to serve mainsail on"; + }; + + nginx = mkOption { + type = types.submodule + (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }); + default = { }; + example = literalExpression '' + { + serverAliases = [ "mainsail.''${config.networking.domain}" ]; + } + ''; + description = lib.mdDoc "Extra configuration for the nginx virtual host of mainsail."; + }; + }; + + config = mkIf cfg.enable { + services.nginx = { + enable = true; + upstreams.mainsail-apiserver.servers."${moonraker.address}:${toString moonraker.port}" = { }; + virtualHosts."${cfg.hostName}" = mkMerge [ + cfg.nginx + { + root = mkForce "${cfg.package}/share/mainsail"; + locations = { + "/" = { + index = "index.html"; + tryFiles = "$uri $uri/ /index.html"; + }; + "/index.html".extraConfig = '' + add_header Cache-Control "no-store, no-cache, must-revalidate"; + ''; + "/websocket" = { + proxyWebsockets = true; + proxyPass = "http://mainsail-apiserver/websocket"; + }; + "~ ^/(printer|api|access|machine|server)/" = { + proxyWebsockets = true; + proxyPass = "http://mainsail-apiserver$request_uri"; + }; + }; + } + ]; + }; + }; +} diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix index cf5329be489c1..247eb707b15c6 100644 --- a/nixos/modules/services/web-apps/mastodon.nix +++ b/nixos/modules/services/web-apps/mastodon.nix @@ -588,11 +588,12 @@ in { ''; } { - assertion = 1 == builtins.length - (lib.mapAttrsToList - (_: v: builtins.elem "scheduler" v.jobClasses || v.jobClasses == [ ]) - cfg.sidekiqProcesses); - message = "There must be one and only one Sidekiq queue in services.mastodon.sidekiqProcesses with jobClass \"scheduler\"."; + assertion = 1 == + (lib.count (x: x) + (lib.mapAttrsToList + (_: v: builtins.elem "scheduler" v.jobClasses || v.jobClasses == [ ]) + cfg.sidekiqProcesses)); + message = "There must be exactly one Sidekiq queue in services.mastodon.sidekiqProcesses with jobClass \"scheduler\"."; } ]; diff --git a/nixos/modules/services/web-apps/matomo.md b/nixos/modules/services/web-apps/matomo.md index f5536a35f7a89..e750c0c147752 100644 --- a/nixos/modules/services/web-apps/matomo.md +++ b/nixos/modules/services/web-apps/matomo.md @@ -3,7 +3,7 @@ Matomo is a real-time web analytics application. This module configures php-fpm as backend for Matomo, optionally configuring an nginx vhost as well. -An automatic setup is not suported by Matomo, so you need to configure Matomo +An automatic setup is not supported by Matomo, so you need to configure Matomo itself in the browser-based Matomo setup. ## Database Setup {#module-services-matomo-database-setup} diff --git a/nixos/modules/services/web-apps/nextcloud-notify_push.nix b/nixos/modules/services/web-apps/nextcloud-notify_push.nix index 52a772f12148f..d6aeee081fc96 100644 --- a/nixos/modules/services/web-apps/nextcloud-notify_push.nix +++ b/nixos/modules/services/web-apps/nextcloud-notify_push.nix @@ -2,6 +2,7 @@ let cfg = config.services.nextcloud.notify_push; + cfgN = config.services.nextcloud; in { options.services.nextcloud.notify_push = { @@ -25,6 +26,16 @@ in default = "error"; description = lib.mdDoc "Log level"; }; + + bendDomainToLocalhost = lib.mkOption { + type = lib.types.bool; + default = false; + description = lib.mdDoc '' + Wether to add an entry to `/etc/hosts` for the configured nextcloud domain to point to `localhost` and add `localhost `to nextcloud's `trusted_proxies` config option. + + This is useful when nextcloud's domain is not a static IP address and when the reverse proxy cannot be bypassed because the backend connection is done via unix socket. + ''; + }; } // ( lib.genAttrs [ "dbtype" @@ -44,11 +55,14 @@ in config = lib.mkIf cfg.enable { systemd.services.nextcloud-notify_push = let - nextcloudUrl = "http${lib.optionalString config.services.nextcloud.https "s"}://${config.services.nextcloud.hostName}"; + nextcloudUrl = "http${lib.optionalString cfgN.https "s"}://${cfgN.hostName}"; in { description = "Push daemon for Nextcloud clients"; documentation = [ "https://github.com/nextcloud/notify_push" ]; - after = [ "phpfpm-nextcloud.service" ]; + after = [ + "phpfpm-nextcloud.service" + "redis-nextcloud.service" + ]; wantedBy = [ "multi-user.target" ]; environment = { NEXTCLOUD_URL = nextcloudUrl; @@ -57,7 +71,7 @@ in LOG = cfg.logLevel; }; postStart = '' - ${config.services.nextcloud.occ}/bin/nextcloud-occ notify_push:setup ${nextcloudUrl}/push + ${cfgN.occ}/bin/nextcloud-occ notify_push:setup ${nextcloudUrl}/push ''; script = let dbType = if cfg.dbtype == "pgsql" then "postgresql" else cfg.dbtype; @@ -76,7 +90,7 @@ in export DATABASE_PASSWORD="$(<"${cfg.dbpassFile}")" '' + '' export DATABASE_URL="${dbUrl}" - ${cfg.package}/bin/notify_push '${config.services.nextcloud.datadir}/config/config.php' + ${cfg.package}/bin/notify_push '${cfgN.datadir}/config/config.php' ''; serviceConfig = { User = "nextcloud"; @@ -87,10 +101,23 @@ in }; }; - services.nginx.virtualHosts.${config.services.nextcloud.hostName}.locations."^~ /push/" = { - proxyPass = "http://unix:${cfg.socketPath}"; - proxyWebsockets = true; - recommendedProxySettings = true; + networking.hosts = lib.mkIf cfg.bendDomainToLocalhost { + "127.0.0.1" = [ cfgN.hostName ]; + "::1" = [ cfgN.hostName ]; }; + + services = lib.mkMerge [ + { + nginx.virtualHosts.${cfgN.hostName}.locations."^~ /push/" = { + proxyPass = "http://unix:${cfg.socketPath}"; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + } + + (lib.mkIf cfg.bendDomainToLocalhost { + nextcloud.extraOptions.trusted_proxies = [ "127.0.0.1" "::1" ]; + }) + ]; }; } diff --git a/nixos/modules/services/web-apps/nextcloud.md b/nixos/modules/services/web-apps/nextcloud.md index 6ecfc6ca7e473..5be81a18dfecd 100644 --- a/nixos/modules/services/web-apps/nextcloud.md +++ b/nixos/modules/services/web-apps/nextcloud.md @@ -17,11 +17,12 @@ and optionally supports For the database, you can set [`services.nextcloud.config.dbtype`](#opt-services.nextcloud.config.dbtype) to -either `sqlite` (the default), `mysql`, or `pgsql`. For the last two, by -default, a local database will be created and nextcloud will connect to it via -socket; this can be disabled by setting +either `sqlite` (the default), `mysql`, or `pgsql`. The simplest is `sqlite`, +which will be automatically created and managed by the application. For the +last two, you can easily create a local database by setting [`services.nextcloud.database.createLocally`](#opt-services.nextcloud.database.createLocally) -to `false`. +to `true`, Nextcloud will automatically be configured to connect to it through +socket. A very basic configuration may look like this: ``` @@ -30,6 +31,7 @@ A very basic configuration may look like this: services.nextcloud = { enable = true; hostName = "nextcloud.tld"; + database.createLocally = true; config = { dbtype = "pgsql"; adminpassFile = "/path/to/admin-pass-file"; diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 8edf270c88991..01dca43776892 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -317,7 +317,7 @@ in { createLocally = mkOption { type = types.bool; - default = true; + default = false; description = lib.mdDoc '' Create the database and database user locally. ''; @@ -551,6 +551,19 @@ in { default = true; }; + configureRedis = lib.mkOption { + type = lib.types.bool; + default = config.services.nextcloud.notify_push.enable; + defaultText = literalExpression "config.services.nextcloud.notify_push.enable"; + description = lib.mdDoc '' + Wether to configure nextcloud to use the recommended redis settings for small instances. + + ::: {.note} + The `notify_push` app requires redis to be configured. If this option is turned off, this must be configured manually. + ::: + ''; + }; + caching = { apcu = mkOption { type = types.bool; @@ -741,9 +754,8 @@ in { { assertions = [ { assertion = cfg.database.createLocally -> cfg.config.dbpassFile == null; message = '' - Using `services.nextcloud.database.createLocally` (that now defaults - to true) with database password authentication is no longer - supported. + Using `services.nextcloud.database.createLocally` with database + password authentication is no longer supported. If you use an external database (or want to use password auth for any other reason), set `services.nextcloud.database.createLocally` to @@ -1044,6 +1056,25 @@ in { }]; }; + services.redis.servers.nextcloud = lib.mkIf cfg.configureRedis { + enable = true; + user = "nextcloud"; + }; + + services.nextcloud = lib.mkIf cfg.configureRedis { + caching.redis = true; + extraOptions = { + memcache = { + distributed = ''\OC\Memcache\Redis''; + locking = ''\OC\Memcache\Redis''; + }; + redis = { + host = config.services.redis.servers.nextcloud.unixSocket; + port = 0; + }; + }; + }; + services.nginx.enable = mkDefault true; services.nginx.virtualHosts.${cfg.hostName} = { diff --git a/nixos/modules/services/web-apps/openvscode-server.nix b/nixos/modules/services/web-apps/openvscode-server.nix new file mode 100644 index 0000000000000..d0db614d8d72b --- /dev/null +++ b/nixos/modules/services/web-apps/openvscode-server.nix @@ -0,0 +1,211 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.openvscode-server; + defaultUser = "openvscode-server"; + defaultGroup = defaultUser; +in { + options = { + services.openvscode-server = { + enable = lib.mkEnableOption (lib.mdDoc "openvscode-server"); + + package = lib.mkPackageOptionMD pkgs "openvscode-server" { }; + + extraPackages = lib.mkOption { + default = [ ]; + description = lib.mdDoc '' + Additional packages to add to the openvscode-server {env}`PATH`. + ''; + example = lib.literalExpression "[ pkgs.go ]"; + type = lib.types.listOf lib.types.package; + }; + + extraEnvironment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = lib.mdDoc '' + Additional environment variables to pass to openvscode-server. + ''; + default = { }; + example = { PKG_CONFIG_PATH = "/run/current-system/sw/lib/pkgconfig"; }; + }; + + extraArguments = lib.mkOption { + default = [ ]; + description = lib.mdDoc '' + Additional arguments to pass to openvscode-server. + ''; + example = lib.literalExpression ''[ "--log=info" ]''; + type = lib.types.listOf lib.types.str; + }; + + host = lib.mkOption { + default = "localhost"; + description = lib.mdDoc '' + The host name or IP address the server should listen to. + ''; + type = lib.types.str; + }; + + port = lib.mkOption { + default = 3000; + description = lib.mdDoc '' + The port the server should listen to. If 0 is passed a random free port is picked. If a range in the format num-num is passed, a free port from the range (end inclusive) is selected. + ''; + type = lib.types.port; + }; + + user = lib.mkOption { + default = defaultUser; + example = "yourUser"; + description = lib.mdDoc '' + The user to run openvscode-server as. + By default, a user named `${defaultUser}` will be created. + ''; + type = lib.types.str; + }; + + group = lib.mkOption { + default = defaultGroup; + example = "yourGroup"; + description = lib.mdDoc '' + The group to run openvscode-server under. + By default, a group named `${defaultGroup}` will be created. + ''; + type = lib.types.str; + }; + + extraGroups = lib.mkOption { + default = [ ]; + description = lib.mdDoc '' + An array of additional groups for the `${defaultUser}` user. + ''; + example = [ "docker" ]; + type = lib.types.listOf lib.types.str; + }; + + withoutConnectionToken = lib.mkOption { + default = false; + description = lib.mdDoc '' + Run without a connection token. Only use this if the connection is secured by other means. + ''; + example = true; + type = lib.types.bool; + }; + + socketPath = lib.mkOption { + default = null; + example = "/run/openvscode/socket"; + description = lib.mdDoc '' + The path to a socket file for the server to listen to. + ''; + type = lib.types.nullOr lib.types.str; + }; + + userDataDir = lib.mkOption { + default = null; + description = lib.mdDoc '' + Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code. + ''; + type = lib.types.nullOr lib.types.str; + }; + + serverDataDir = lib.mkOption { + default = null; + description = lib.mdDoc '' + Specifies the directory that server data is kept in. + ''; + type = lib.types.nullOr lib.types.str; + }; + + extensionsDir = lib.mkOption { + default = null; + description = lib.mdDoc '' + Set the root path for extensions. + ''; + type = lib.types.nullOr lib.types.str; + }; + + telemetryLevel = lib.mkOption { + default = "off"; + example = "crash"; + description = lib.mdDoc '' + Sets the initial telemetry level. Valid levels are: 'off', 'crash', 'error' and 'all'. + ''; + type = lib.types.str; + }; + + connectionToken = lib.mkOption { + default = null; + example = "secret-token"; + description = lib.mdDoc '' + A secret that must be included with all requests. + ''; + type = lib.types.nullOr lib.types.str; + }; + + connectionTokenFile = lib.mkOption { + default = null; + description = lib.mdDoc '' + Path to a file that contains the connection token. + ''; + type = lib.types.nullOr lib.types.str; + }; + + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.openvscode-server = { + description = "OpenVSCode server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + path = cfg.extraPackages; + environment = cfg.extraEnvironment; + serviceConfig = { + ExecStart = '' + ${lib.getExe cfg.package} \ + --accept-server-license-terms \ + --host=${cfg.host} \ + --port=${toString cfg.port} \ + '' + lib.optionalString (cfg.telemetryLevel == true) '' + --telemetry-level=${cfg.telemetryLevel} \ + '' + lib.optionalString (cfg.withoutConnectionToken == true) '' + --without-connection-token \ + '' + lib.optionalString (cfg.socketPath != null) '' + --socket-path=${cfg.socketPath} \ + '' + lib.optionalString (cfg.userDataDir != null) '' + --user-data-dir=${cfg.userDataDir} \ + '' + lib.optionalString (cfg.serverDataDir != null) '' + --server-data-dir=${cfg.serverDataDir} \ + '' + lib.optionalString (cfg.extensionsDir != null) '' + --extensions-dir=${cfg.extensionsDir} \ + '' + lib.optionalString (cfg.connectionToken != null) '' + --connection-token=${cfg.connectionToken} \ + '' + lib.optionalString (cfg.connectionTokenFile != null) '' + --connection-token-file=${cfg.connectionTokenFile} \ + '' + lib.escapeShellArgs cfg.extraArguments; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + RuntimeDirectory = cfg.user; + User = cfg.user; + Group = cfg.group; + Restart = "on-failure"; + }; + }; + + users.users."${cfg.user}" = lib.mkMerge [ + (lib.mkIf (cfg.user == defaultUser) { + isNormalUser = true; + description = "openvscode-server user"; + inherit (cfg) group; + }) + { + packages = cfg.extraPackages; + inherit (cfg) extraGroups; + } + ]; + + users.groups."${defaultGroup}" = lib.mkIf (cfg.group == defaultGroup) { }; + }; + + meta.maintainers = [ lib.maintainers.drupol ]; +} diff --git a/nixos/modules/services/web-apps/peertube.nix b/nixos/modules/services/web-apps/peertube.nix index 65b3b70c48add..4ef2d7dce532c 100644 --- a/nixos/modules/services/web-apps/peertube.nix +++ b/nixos/modules/services/web-apps/peertube.nix @@ -429,7 +429,7 @@ in { environment = env; - path = with pkgs; [ bashInteractive ffmpeg nodejs_16 openssl yarn python3 ]; + path = with pkgs; [ bashInteractive ffmpeg nodejs_18 openssl yarn python3 ]; script = '' #!/bin/sh @@ -490,7 +490,7 @@ in { services.nginx = lib.mkIf cfg.configureNginx { enable = true; virtualHosts."${cfg.localDomain}" = { - root = "/var/lib/peertube"; + root = "/var/lib/peertube/www"; # Application locations."/" = { @@ -593,7 +593,7 @@ in { # Bypass PeerTube for performance reasons. locations."~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png|default-playlist\.jpg|default-avatar-account\.png|default-avatar-account-48x48\.png|default-avatar-video-channel\.png|default-avatar-video-channel-48x48\.png))$" = { - tryFiles = "/www/client-overrides/$1 /www/client/$1 $1"; + tryFiles = "/client-overrides/$1 /client/$1 $1"; priority = 1310; }; @@ -859,7 +859,7 @@ in { home = cfg.package; }; }) - (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package peertubeEnv peertubeCli pkgs.ffmpeg pkgs.nodejs_16 pkgs.yarn ]) + (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package peertubeEnv peertubeCli pkgs.ffmpeg pkgs.nodejs_18 pkgs.yarn ]) (lib.mkIf cfg.redis.enableUnixSocket {${config.services.peertube.user}.extraGroups = [ "redis-peertube" ];}) ]; diff --git a/nixos/modules/services/web-apps/pict-rs.nix b/nixos/modules/services/web-apps/pict-rs.nix index 0f13b2ae6db13..3270715a051ba 100644 --- a/nixos/modules/services/web-apps/pict-rs.nix +++ b/nixos/modules/services/web-apps/pict-rs.nix @@ -34,8 +34,8 @@ in config = lib.mkIf cfg.enable { systemd.services.pict-rs = { environment = { - PICTRS_PATH = cfg.dataDir; - PICTRS_ADDR = "${cfg.address}:${toString cfg.port}"; + PICTRS__PATH = cfg.dataDir; + PICTRS__ADDR = "${cfg.address}:${toString cfg.port}"; }; wantedBy = [ "multi-user.target" ]; serviceConfig = { diff --git a/nixos/modules/services/web-apps/pixelfed.nix b/nixos/modules/services/web-apps/pixelfed.nix new file mode 100644 index 0000000000000..817d0f9b60f50 --- /dev/null +++ b/nixos/modules/services/web-apps/pixelfed.nix @@ -0,0 +1,478 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.pixelfed; + user = cfg.user; + group = cfg.group; + pixelfed = cfg.package.override { inherit (cfg) dataDir runtimeDir; }; + # https://github.com/pixelfed/pixelfed/blob/dev/app/Console/Commands/Installer.php#L185-L190 + extraPrograms = with pkgs; [ jpegoptim optipng pngquant gifsicle ffmpeg ]; + # Ensure PHP extensions: https://github.com/pixelfed/pixelfed/blob/dev/app/Console/Commands/Installer.php#L135-L147 + phpPackage = cfg.phpPackage.buildEnv { + extensions = { enabled, all }: + enabled + ++ (with all; [ bcmath ctype curl mbstring gd intl zip redis imagick ]); + }; + configFile = + pkgs.writeText "pixelfed-env" (lib.generators.toKeyValue { } cfg.settings); + # Management script + pixelfed-manage = pkgs.writeShellScriptBin "pixelfed-manage" '' + cd ${pixelfed} + sudo=exec + if [[ "$USER" != ${user} ]]; then + sudo='exec /run/wrappers/bin/sudo -u ${user}' + fi + $sudo ${cfg.phpPackage}/bin/php artisan "$@" + ''; + dbSocket = { + "pgsql" = "/run/postgresql"; + "mysql" = "/run/mysqld/mysqld.sock"; + }.${cfg.database.type}; + dbService = { + "pgsql" = "postgresql.service"; + "mysql" = "mysql.service"; + }.${cfg.database.type}; + redisService = "redis-pixelfed.service"; +in { + options.services = { + pixelfed = { + enable = mkEnableOption (lib.mdDoc "a Pixelfed instance"); + package = mkPackageOptionMD pkgs "pixelfed" { }; + phpPackage = mkPackageOptionMD pkgs "php81" { }; + + user = mkOption { + type = types.str; + default = "pixelfed"; + description = lib.mdDoc '' + User account under which pixelfed runs. + + ::: {.note} + If left as the default value this user will automatically be created + on system activation, otherwise you are responsible for + ensuring the user exists before the pixelfed application starts. + ::: + ''; + }; + + group = mkOption { + type = types.str; + default = "pixelfed"; + description = lib.mdDoc '' + Group account under which pixelfed runs. + + ::: {.note} + If left as the default value this group will automatically be created + on system activation, otherwise you are responsible for + ensuring the group exists before the pixelfed application starts. + ::: + ''; + }; + + domain = mkOption { + type = types.str; + description = lib.mdDoc '' + FQDN for the Pixelfed instance. + ''; + }; + + secretFile = mkOption { + type = types.path; + description = lib.mdDoc '' + A secret file to be sourced for the .env settings. + Place `APP_KEY` and other settings that should not end up in the Nix store here. + ''; + }; + + settings = mkOption { + type = with types; (attrsOf (oneOf [ bool int str ])); + description = lib.mdDoc '' + .env settings for Pixelfed. + Secrets should use `secretFile` option instead. + ''; + }; + + nginx = mkOption { + type = types.nullOr (types.submodule + (import ../web-servers/nginx/vhost-options.nix { + inherit config lib; + })); + default = null; + example = lib.literalExpression '' + { + serverAliases = [ + "pics.''${config.networking.domain}" + ]; + enableACME = true; + forceHttps = true; + } + ''; + description = lib.mdDoc '' + With this option, you can customize an nginx virtual host which already has sensible defaults for Dolibarr. + Set to {} if you do not need any customization to the virtual host. + If enabled, then by default, the {option}`serverName` is + `''${domain}`, + If this is set to null (the default), no nginx virtualHost will be configured. + ''; + }; + + redis.createLocally = mkEnableOption + (lib.mdDoc "a local Redis database using UNIX socket authentication") + // { + default = true; + }; + + database = { + createLocally = mkEnableOption + (lib.mdDoc "a local database using UNIX socket authentication") // { + default = true; + }; + automaticMigrations = mkEnableOption + (lib.mdDoc "automatic migrations for database schema and data") // { + default = true; + }; + + type = mkOption { + type = types.enum [ "mysql" "pgsql" ]; + example = "pgsql"; + default = "mysql"; + description = lib.mdDoc '' + Database engine to use. + Note that PGSQL is not well supported: https://github.com/pixelfed/pixelfed/issues/2727 + ''; + }; + + name = mkOption { + type = types.str; + default = "pixelfed"; + description = lib.mdDoc "Database name."; + }; + }; + + maxUploadSize = mkOption { + type = types.str; + default = "8M"; + description = lib.mdDoc '' + Max upload size with units. + ''; + }; + + poolConfig = mkOption { + type = with types; attrsOf (oneOf [ int str bool ]); + default = { }; + + description = lib.mdDoc '' + Options for Pixelfed's PHP-FPM pool. + ''; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/pixelfed"; + description = lib.mdDoc '' + State directory of the `pixelfed` user which holds + the application's state and data. + ''; + }; + + runtimeDir = mkOption { + type = types.str; + default = "/run/pixelfed"; + description = lib.mdDoc '' + Ruutime directory of the `pixelfed` user which holds + the application's caches and temporary files. + ''; + }; + + schedulerInterval = mkOption { + type = types.str; + default = "1d"; + description = lib.mdDoc "How often the Pixelfed cron task should run"; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.pixelfed = mkIf (cfg.user == "pixelfed") { + isSystemUser = true; + group = cfg.group; + extraGroups = lib.optional cfg.redis.createLocally "redis-pixelfed"; + }; + users.groups.pixelfed = mkIf (cfg.group == "pixelfed") { }; + + services.redis.servers.pixelfed.enable = lib.mkIf cfg.redis.createLocally true; + services.pixelfed.settings = mkMerge [ + ({ + APP_ENV = mkDefault "production"; + APP_DEBUG = mkDefault false; + # https://github.com/pixelfed/pixelfed/blob/dev/app/Console/Commands/Installer.php#L312-L316 + APP_URL = mkDefault "https://${cfg.domain}"; + ADMIN_DOMAIN = mkDefault cfg.domain; + APP_DOMAIN = mkDefault cfg.domain; + SESSION_DOMAIN = mkDefault cfg.domain; + SESSION_SECURE_COOKIE = mkDefault true; + OPEN_REGISTRATION = mkDefault false; + # ActivityPub: https://github.com/pixelfed/pixelfed/blob/dev/app/Console/Commands/Installer.php#L360-L364 + ACTIVITY_PUB = mkDefault true; + AP_REMOTE_FOLLOW = mkDefault true; + AP_INBOX = mkDefault true; + AP_OUTBOX = mkDefault true; + AP_SHAREDINBOX = mkDefault true; + # Image optimization: https://github.com/pixelfed/pixelfed/blob/dev/app/Console/Commands/Installer.php#L367-L404 + PF_OPTIMIZE_IMAGES = mkDefault true; + IMAGE_DRIVER = mkDefault "imagick"; + # Mobile APIs + OAUTH_ENABLED = mkDefault true; + # https://github.com/pixelfed/pixelfed/blob/dev/app/Console/Commands/Installer.php#L351 + EXP_EMC = mkDefault true; + # Defer to systemd + LOG_CHANNEL = mkDefault "stderr"; + # TODO: find out the correct syntax? + # TRUST_PROXIES = mkDefault "127.0.0.1/8, ::1/128"; + }) + (mkIf (cfg.redis.createLocally) { + BROADCAST_DRIVER = mkDefault "redis"; + CACHE_DRIVER = mkDefault "redis"; + QUEUE_DRIVER = mkDefault "redis"; + SESSION_DRIVER = mkDefault "redis"; + WEBSOCKET_REPLICATION_MODE = mkDefault "redis"; + # Suppport phpredis and predis configuration-style. + REDIS_SCHEME = "unix"; + REDIS_HOST = config.services.redis.servers.pixelfed.unixSocket; + REDIS_PATH = config.services.redis.servers.pixelfed.unixSocket; + }) + (mkIf (cfg.database.createLocally) { + DB_CONNECTION = cfg.database.type; + DB_SOCKET = dbSocket; + DB_DATABASE = cfg.database.name; + DB_USERNAME = user; + # No TCP/IP connection. + DB_PORT = 0; + }) + ]; + + environment.systemPackages = [ pixelfed-manage ]; + + services.mysql = + mkIf (cfg.database.createLocally && cfg.database.type == "mysql") { + enable = mkDefault true; + package = mkDefault pkgs.mariadb; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [{ + name = user; + ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; }; + }]; + }; + + services.postgresql = + mkIf (cfg.database.createLocally && cfg.database.type == "pgsql") { + enable = mkDefault true; + ensureDatabases = [ cfg.database.name ]; + ensureUsers = [{ + name = user; + ensurePermissions = { }; + }]; + }; + + # Make each individual option overridable with lib.mkDefault. + services.pixelfed.poolConfig = lib.mapAttrs' (n: v: lib.nameValuePair n (lib.mkDefault v)) { + "pm" = "dynamic"; + "php_admin_value[error_log]" = "stderr"; + "php_admin_flag[log_errors]" = true; + "catch_workers_output" = true; + "pm.max_children" = "32"; + "pm.start_servers" = "2"; + "pm.min_spare_servers" = "2"; + "pm.max_spare_servers" = "4"; + "pm.max_requests" = "500"; + }; + + services.phpfpm.pools.pixelfed = { + inherit user group; + inherit phpPackage; + + phpOptions = '' + post_max_size = ${toString cfg.maxUploadSize} + upload_max_filesize = ${toString cfg.maxUploadSize} + max_execution_time = 600; + ''; + + settings = { + "listen.owner" = user; + "listen.group" = group; + "listen.mode" = "0660"; + "catch_workers_output" = "yes"; + } // cfg.poolConfig; + }; + + systemd.services.phpfpm-pixelfed.after = [ "pixelfed-data-setup.service" ]; + systemd.services.phpfpm-pixelfed.requires = + [ "pixelfed-horizon.service" "pixelfed-data-setup.service" ] + ++ lib.optional cfg.database.createLocally dbService + ++ lib.optional cfg.redis.createLocally redisService; + # Ensure image optimizations programs are available. + systemd.services.phpfpm-pixelfed.path = extraPrograms; + + systemd.services.pixelfed-horizon = { + description = "Pixelfed task queueing via Laravel Horizon framework"; + after = [ "network.target" "pixelfed-data-setup.service" ]; + requires = [ "pixelfed-data-setup.service" ] + ++ (lib.optional cfg.database.createLocally dbService) + ++ (lib.optional cfg.redis.createLocally redisService); + wantedBy = [ "multi-user.target" ]; + # Ensure image optimizations programs are available. + path = extraPrograms; + + serviceConfig = { + Type = "simple"; + ExecStart = "${pixelfed-manage}/bin/pixelfed-manage horizon"; + StateDirectory = + lib.mkIf (cfg.dataDir == "/var/lib/pixelfed") "pixelfed"; + User = user; + Group = group; + Restart = "on-failure"; + }; + }; + + systemd.timers.pixelfed-cron = { + description = "Pixelfed periodic tasks timer"; + after = [ "pixelfed-data-setup.service" ]; + requires = [ "phpfpm-pixelfed.service" ]; + wantedBy = [ "timers.target" ]; + + timerConfig = { + OnBootSec = cfg.schedulerInterval; + OnUnitActiveSec = cfg.schedulerInterval; + }; + }; + + systemd.services.pixelfed-cron = { + description = "Pixelfed periodic tasks"; + # Ensure image optimizations programs are available. + path = extraPrograms; + + serviceConfig = { + ExecStart = "${pixelfed-manage}/bin/pixelfed-manage schedule:run"; + User = user; + Group = group; + StateDirectory = cfg.dataDir; + }; + }; + + systemd.services.pixelfed-data-setup = { + description = + "Pixelfed setup: migrations, environment file update, cache reload, data changes"; + wantedBy = [ "multi-user.target" ]; + after = lib.optional cfg.database.createLocally dbService; + requires = lib.optional cfg.database.createLocally dbService; + path = with pkgs; [ bash pixelfed-manage rsync ] ++ extraPrograms; + + serviceConfig = { + Type = "oneshot"; + User = user; + Group = group; + StateDirectory = + lib.mkIf (cfg.dataDir == "/var/lib/pixelfed") "pixelfed"; + LoadCredential = "env-secrets:${cfg.secretFile}"; + UMask = "077"; + }; + + script = '' + # Concatenate non-secret .env and secret .env + rm -f ${cfg.dataDir}/.env + cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env + echo -e '\n' >> ${cfg.dataDir}/.env + cat "$CREDENTIALS_DIRECTORY/env-secrets" >> ${cfg.dataDir}/.env + + # Link the static storage (package provided) to the runtime storage + # Necessary for cities.json and static images. + mkdir -p ${cfg.dataDir}/storage + rsync -av --no-perms ${pixelfed}/storage-static/ ${cfg.dataDir}/storage + chmod -R +w ${cfg.dataDir}/storage + + # Link the app.php in the runtime folder. + # We cannot link the cache folder only because bootstrap folder needs to be writeable. + ln -sf ${pixelfed}/bootstrap-static/app.php ${cfg.runtimeDir}/app.php + + # https://laravel.com/docs/10.x/filesystem#the-public-disk + # Creating the public/storage → storage/app/public link + # is unnecessary as it's part of the installPhase of pixelfed. + + # Install Horizon + # FIXME: require write access to public/ — should be done as part of install — pixelfed-manage horizon:publish + + # Before running any PHP program, cleanup the bootstrap. + # It's necessary if you upgrade the application otherwise you might + # try to import non-existent modules. + rm -rf ${cfg.runtimeDir}/bootstrap/* + + # Perform the first migration. + [[ ! -f ${cfg.dataDir}/.initial-migration ]] && pixelfed-manage migrate --force && touch ${cfg.dataDir}/.initial-migration + + ${lib.optionalString cfg.database.automaticMigrations '' + # Force migrate the database. + pixelfed-manage migrate --force + ''} + + # Import location data + pixelfed-manage import:cities + + ${lib.optionalString cfg.settings.ACTIVITY_PUB '' + # ActivityPub federation bookkeeping + [[ ! -f ${cfg.dataDir}/.instance-actor-created ]] && pixelfed-manage instance:actor && touch ${cfg.dataDir}/.instance-actor-created + ''} + + ${lib.optionalString cfg.settings.OAUTH_ENABLED '' + # Generate Passport encryption keys + [[ ! -f ${cfg.dataDir}/.passport-keys-generated ]] && pixelfed-manage passport:keys && touch ${cfg.dataDir}/.passport-keys-generated + ''} + + pixelfed-manage route:cache + pixelfed-manage view:cache + pixelfed-manage config:cache + ''; + }; + + systemd.tmpfiles.rules = [ + # Cache must live across multiple systemd units runtimes. + "d ${cfg.runtimeDir}/ 0700 ${user} ${group} - -" + "d ${cfg.runtimeDir}/cache 0700 ${user} ${group} - -" + ]; + + # Enable NGINX to access our phpfpm-socket. + users.users."${config.services.nginx.group}".extraGroups = [ cfg.group ]; + services.nginx = mkIf (cfg.nginx != null) { + enable = true; + virtualHosts."${cfg.domain}" = mkMerge [ + cfg.nginx + { + root = lib.mkForce "${pixelfed}/public/"; + locations."/".tryFiles = "$uri $uri/ /index.php?query_string"; + locations."/favicon.ico".extraConfig = '' + access_log off; log_not_found off; + ''; + locations."/robots.txt".extraConfig = '' + access_log off; log_not_found off; + ''; + locations."~ \\.php$".extraConfig = '' + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:${config.services.phpfpm.pools.pixelfed.socket}; + fastcgi_index index.php; + ''; + locations."~ /\\.(?!well-known).*".extraConfig = '' + deny all; + ''; + extraConfig = '' + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Content-Type-Options "nosniff"; + index index.html index.htm index.php; + error_page 404 /index.php; + client_max_body_size ${toString cfg.maxUploadSize}; + ''; + } + ]; + }; + }; +} diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix index 6f494fae4cc18..3102e6a469536 100644 --- a/nixos/modules/services/web-apps/tt-rss.nix +++ b/nixos/modules/services/web-apps/tt-rss.nix @@ -534,7 +534,7 @@ let services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") { ${poolName} = { inherit (cfg) user; - phpPackage = pkgs.php80; + phpPackage = pkgs.php81; settings = mapAttrs (name: mkDefault) { "listen.owner" = "nginx"; "listen.group" = "nginx"; diff --git a/nixos/modules/services/web-apps/vikunja.nix b/nixos/modules/services/web-apps/vikunja.nix index c3552200d4e53..8bc8e8c29259e 100644 --- a/nixos/modules/services/web-apps/vikunja.nix +++ b/nixos/modules/services/web-apps/vikunja.nix @@ -56,6 +56,11 @@ in { type = types.str; description = lib.mdDoc "The Hostname under which the frontend is running."; }; + port = mkOption { + type = types.port; + default = 3456; + description = lib.mdDoc "The TCP port exposed by the API."; + }; settings = mkOption { type = format.type; @@ -101,6 +106,7 @@ in { inherit (cfg.database) type host user database path; }; service = { + interface = ":${toString cfg.port}"; frontendurl = "${cfg.frontendScheme}://${cfg.frontendHostname}/"; }; files = { @@ -132,7 +138,7 @@ in { tryFiles = "try_files $uri $uri/ /"; }; "~* ^/(api|dav|\\.well-known)/" = { - proxyPass = "http://localhost:3456"; + proxyPass = "http://localhost:${toString cfg.port}"; extraConfig = '' client_max_body_size 20M; ''; diff --git a/nixos/modules/services/web-apps/wiki-js.nix b/nixos/modules/services/web-apps/wiki-js.nix index 22682002532d5..631740f51ce35 100644 --- a/nixos/modules/services/web-apps/wiki-js.nix +++ b/nixos/modules/services/web-apps/wiki-js.nix @@ -133,7 +133,7 @@ in { WorkingDirectory = "/var/lib/${cfg.stateDirectoryName}"; DynamicUser = true; PrivateTmp = true; - ExecStart = "${pkgs.nodejs_16}/bin/node ${pkgs.wiki-js}/server"; + ExecStart = "${pkgs.nodejs_18}/bin/node ${pkgs.wiki-js}/server"; }; }; }; diff --git a/nixos/modules/services/web-servers/unit/default.nix b/nixos/modules/services/web-servers/unit/default.nix index 0aaac8a14e49d..1515779c90649 100644 --- a/nixos/modules/services/web-servers/unit/default.nix +++ b/nixos/modules/services/web-servers/unit/default.nix @@ -104,7 +104,7 @@ in { PIDFile = "/run/unit/unit.pid"; ExecStart = '' ${cfg.package}/bin/unitd --control 'unix:/run/unit/control.unit.sock' --pid '/run/unit/unit.pid' \ - --log '${cfg.logDir}/unit.log' --state '${cfg.stateDir}' --tmp '/tmp' \ + --log '${cfg.logDir}/unit.log' --statedir '${cfg.stateDir}' --tmpdir '/tmp' \ --user ${cfg.user} --group ${cfg.group} ''; ExecStop = '' diff --git a/nixos/modules/services/web-servers/varnish/default.nix b/nixos/modules/services/web-servers/varnish/default.nix index e34c22d2868f1..d7f19be0cec47 100644 --- a/nixos/modules/services/web-servers/varnish/default.nix +++ b/nixos/modules/services/web-servers/varnish/default.nix @@ -99,7 +99,7 @@ in environment.systemPackages = [ cfg.package ]; # check .vcl syntax at compile time (e.g. before nixops deployment) - system.extraDependencies = mkIf cfg.enableConfigCheck [ + system.checks = mkIf cfg.enableConfigCheck [ (pkgs.runCommand "check-varnish-syntax" {} '' ${cfg.package}/bin/varnishd -C ${commandLine} 2> $out || (cat $out; exit 1) '') diff --git a/nixos/modules/services/x11/desktop-managers/budgie.nix b/nixos/modules/services/x11/desktop-managers/budgie.nix index 2eff81750d9e0..b7341d4d8b491 100644 --- a/nixos/modules/services/x11/desktop-managers/budgie.nix +++ b/nixos/modules/services/x11/desktop-managers/budgie.nix @@ -45,10 +45,15 @@ in { enable = mkEnableOption (mdDoc "the Budgie desktop"); sessionPath = mkOption { - description = mdDoc "Additional list of packages to be added to the session search path. Useful for GSettings-conditional autostart."; - type = with types; listOf package; - example = literalExpression "[ pkgs.budgie.budgie-desktop-view ]"; + description = lib.mdDoc '' + Additional list of packages to be added to the session search path. + Useful for GSettings-conditional autostart. + + Note that this should be a last resort; patching the package is preferred (see GPaste). + ''; + type = types.listOf types.package; default = []; + example = literalExpression "[ pkgs.gnome.gpaste ]"; }; extraGSettingsOverrides = mkOption { @@ -59,20 +64,21 @@ in { extraGSettingsOverridePackages = mkOption { description = mdDoc "List of packages for which GSettings are overridden."; - type = with types; listOf path; + type = types.listOf types.path; default = []; }; extraPlugins = mkOption { description = mdDoc "Extra plugins for the Budgie desktop"; - type = with types; listOf package; + type = types.listOf types.package; default = []; + example = literalExpression "[ pkgs.budgiePlugins.budgie-analogue-clock-applet ]"; }; }; environment.budgie.excludePackages = mkOption { description = mdDoc "Which packages Budgie should exclude from the default environment."; - type = with types; listOf package; + type = types.listOf types.package; default = []; example = literalExpression "[ pkgs.mate-terminal ]"; }; diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix index f77036360e029..f3391c6e11693 100644 --- a/nixos/modules/services/x11/hardware/libinput.nix +++ b/nixos/modules/services/x11/hardware/libinput.nix @@ -260,7 +260,9 @@ in { options = { services.xserver.libinput = { - enable = mkEnableOption (lib.mdDoc "libinput"); + enable = mkEnableOption (lib.mdDoc "libinput") // { + default = true; + }; mouse = mkConfigForDevice "mouse"; touchpad = mkConfigForDevice "touchpad"; }; diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index c0051a2ce38fa..6d2321be8ef73 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -776,7 +776,7 @@ in xorg.xf86inputevdev.out ]; - system.extraDependencies = singleton (pkgs.runCommand "xkb-validated" { + system.checks = singleton (pkgs.runCommand "xkb-validated" { inherit (cfg) xkbModel layout xkbVariant xkbOptions; nativeBuildInputs = with pkgs.buildPackages; [ xkbvalidate ]; preferLocalBuild = true; diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index f2e7413547828..c28e530cdc777 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -263,8 +263,23 @@ in default = []; description = lib.mdDoc '' A list of packages that should be included in the system - closure but not otherwise made available to users. This is - primarily used by the installation tests. + closure but generally not visible to users. + + This option has also been used for build-time checks, but the + `system.checks` option is more appropriate for that purpose as checks + should not leave a trace in the built system configuration. + ''; + }; + + system.checks = mkOption { + type = types.listOf types.package; + default = []; + description = lib.mdDoc '' + Packages that are added as dependencies of the system's build, usually + for the purpose of validating some part of the configuration. + + Unlike `system.extraDependencies`, these store paths do not + become part of the built system configuration. ''; }; @@ -363,7 +378,17 @@ in fi ''; - system.systemBuilderArgs = lib.optionalAttrs (config.system.forbiddenDependenciesRegex != "") { + system.systemBuilderArgs = { + # Not actually used in the builder. `passedChecks` is just here to create + # the build dependencies. Checks are similar to build dependencies in the + # sense that if they fail, the system build fails. However, checks do not + # produce any output of value, so they are not used by the system builder. + # In fact, using them runs the risk of accidentally adding unneeded paths + # to the system closure, which defeats the purpose of the `system.checks` + # option, as opposed to `system.extraDependencies`. + passedChecks = concatStringsSep " " config.system.checks; + } + // lib.optionalAttrs (config.system.forbiddenDependenciesRegex != "") { inherit (config.system) forbiddenDependenciesRegex; closureInfo = pkgs.closureInfo { rootPaths = [ # override to avoid infinite recursion (and to allow using extraDependencies to add forbidden dependencies) @@ -371,6 +396,7 @@ in ]; }; }; + system.build.toplevel = if config.system.includeBuildDependencies then systemWithBuildDeps else system; }; diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index 5c0a07fb51272..9f80b40d116c6 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, options, lib, pkgs, ... }: with lib; @@ -12,13 +12,8 @@ let # Package set of targeted architecture if cfg.forcei686 then pkgs.pkgsi686Linux else pkgs; - realGrub = if cfg.version == 1 then grubPkgs.grub - else if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; } - else if cfg.trustedBoot.enable - then if cfg.trustedBoot.isHPLaptop - then grubPkgs.trustedGrub-for-HP - else grubPkgs.trustedGrub - else grubPkgs.grub2; + realGrub = if cfg.zfsSupport then grubPkgs.grub2.override { zfsSupport = true; } + else grubPkgs.grub2; grub = # Don't include GRUB if we're only generating a GRUB menu (e.g., @@ -28,8 +23,7 @@ let else realGrub; grubEfi = - # EFI version of Grub v2 - if cfg.efiSupport && (cfg.version == 2) + if cfg.efiSupport then realGrub.override { efiSupport = cfg.efiSupport; } else null; @@ -52,24 +46,24 @@ let fullName = lib.getName realGrub; fullVersion = lib.getVersion realGrub; grubEfi = f grubEfi; - grubTargetEfi = optionalString (cfg.efiSupport && (cfg.version == 2)) (f (grubEfi.grubTarget or "")); + grubTargetEfi = optionalString cfg.efiSupport (f (grubEfi.grubTarget or "")); bootPath = args.path; storePath = config.boot.loader.grub.storePath; bootloaderId = if args.efiBootloaderId == null then "${config.system.nixos.distroName}${efiSysMountPoint'}" else args.efiBootloaderId; timeout = if config.boot.loader.timeout == null then -1 else config.boot.loader.timeout; - users = if cfg.users == {} || cfg.version != 1 then cfg.users else throw "GRUB version 1 does not support user accounts."; theme = f cfg.theme; inherit efiSysMountPoint; inherit (args) devices; inherit (efi) canTouchEfiVariables; inherit (cfg) - version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber + extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber extraGrubInstallArgs extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels - default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios; + default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios gfxpayloadEfi gfxpayloadBios + users; path = with pkgs; makeBinPath ( [ coreutils gnused gnugrep findutils diffutils btrfs-progs util-linux mdadm ] - ++ optional (cfg.efiSupport && (cfg.version == 2)) efibootmgr + ++ optional cfg.efiSupport efibootmgr ++ optionals cfg.useOSProber [ busybox os-prober ]); font = if cfg.font == null then "" else (if lib.last (lib.splitString "." cfg.font) == "pf2" @@ -109,14 +103,8 @@ in }; version = mkOption { - default = 2; - example = 1; + visible = false; type = types.int; - description = lib.mdDoc '' - The version of GRUB to use: `1` for GRUB - Legacy (versions 0.9x), or `2` (the - default) for GRUB 2. - ''; }; device = mkOption { @@ -682,39 +670,6 @@ in ''; }; - trustedBoot = { - - enable = mkOption { - default = false; - type = types.bool; - description = lib.mdDoc '' - Enable trusted boot. GRUB will measure all critical components during - the boot process to offer TCG (TPM) support. - ''; - }; - - systemHasTPM = mkOption { - default = ""; - example = "YES_TPM_is_activated"; - type = types.str; - description = lib.mdDoc '' - Assertion that the target system has an activated TPM. It is a safety - check before allowing the activation of 'trustedBoot.enable'. TrustedBoot - WILL FAIL TO BOOT YOUR SYSTEM if no TPM is available. - ''; - }; - - isHPLaptop = mkOption { - default = false; - type = types.bool; - description = lib.mdDoc '' - Use a special version of TrustedGRUB that is needed by some HP laptops - and works only for the HP laptops. - ''; - }; - - }; - }; }; @@ -724,14 +679,7 @@ in config = mkMerge [ - { boot.loader.grub.splashImage = mkDefault ( - if cfg.version == 1 then pkgs.fetchurl { - url = "http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz"; - sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59"; - } - # GRUB 1.97 doesn't support gzipped XPMs. - else defaultSplash); - } + { boot.loader.grub.splashImage = mkDefault defaultSplash; } (mkIf (cfg.splashImage == defaultSplash) { boot.loader.grub.backgroundColor = mkDefault "#2F302F"; @@ -789,10 +737,6 @@ in assertions = [ { - assertion = !cfg.zfsSupport || cfg.version == 2; - message = "Only GRUB version 2 provides ZFS support"; - } - { assertion = cfg.mirroredBoots != [ ]; message = "You must set the option ‘boot.loader.grub.devices’ or " + "'boot.loader.grub.mirroredBoots' to make the system bootable."; @@ -802,22 +746,6 @@ in message = "You cannot have duplicated devices in mirroredBoots"; } { - assertion = !cfg.trustedBoot.enable || cfg.version == 2; - message = "Trusted GRUB is only available for GRUB 2"; - } - { - assertion = !cfg.efiSupport || !cfg.trustedBoot.enable; - message = "Trusted GRUB does not have EFI support"; - } - { - assertion = !cfg.zfsSupport || !cfg.trustedBoot.enable; - message = "Trusted GRUB does not have ZFS support"; - } - { - assertion = !cfg.trustedBoot.enable || cfg.trustedBoot.systemHasTPM == "YES_TPM_is_activated"; - message = "Trusted GRUB can break the system! Confirm that the system has an activated TPM by setting 'systemHasTPM'."; - } - { assertion = cfg.efiInstallAsRemovable -> cfg.efiSupport; message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn on boot.loader.grub.efiSupport"; } @@ -825,6 +753,10 @@ in assertion = cfg.efiInstallAsRemovable -> !config.boot.loader.efi.canTouchEfiVariables; message = "If you wish to to use boot.loader.grub.efiInstallAsRemovable, then turn off boot.loader.efi.canTouchEfiVariables"; } + { + assertion = !(options.boot.loader.grub.version.isDefined && cfg.version == 1); + message = "Support for version 0.9x of GRUB was removed after being unsupported upstream for around a decade"; + } ] ++ flip concatMap cfg.mirroredBoots (args: [ { assertion = args.devices != [ ]; @@ -844,6 +776,11 @@ in })); }) + (mkIf options.boot.loader.grub.version.isDefined { + warnings = [ '' + The boot.loader.grub.version option does not have any effect anymore, please remove it from your configuration. + '' ]; + }) ]; @@ -855,6 +792,10 @@ in (mkRenamedOptionModule [ "boot" "grubDevice" ] [ "boot" "loader" "grub" "device" ]) (mkRenamedOptionModule [ "boot" "bootMount" ] [ "boot" "loader" "grub" "bootDevice" ]) (mkRenamedOptionModule [ "boot" "grubSplashImage" ] [ "boot" "loader" "grub" "splashImage" ]) + (mkRemovedOptionModule [ "boot" "loader" "grub" "trustedBoot" ] '' + Support for Trusted GRUB has been removed, because the project + has been retired upstream. + '') (mkRemovedOptionModule [ "boot" "loader" "grub" "extraInitrd" ] '' This option has been replaced with the bootloader agnostic boot.initrd.secrets option. To migrate to the initrd secrets system, diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl index 2779f26aa1b62..cfccb93264bfd 100644 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -61,7 +61,6 @@ sub runCommand { } my $grub = get("grub"); -my $grubVersion = int(get("version")); my $grubTarget = get("grubTarget"); my $extraConfig = get("extraConfig"); my $extraPrepareConfig = get("extraPrepareConfig"); @@ -96,9 +95,7 @@ my $theme = get("theme"); my $saveDefault = $defaultEntry eq "saved"; $ENV{'PATH'} = get("path"); -die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2; - -print STDERR "updating GRUB $grubVersion menu...\n"; +print STDERR "updating GRUB 2 menu...\n"; mkpath("$bootPath/grub", 0, 0700); @@ -176,76 +173,74 @@ sub GrubFs { } my $search = ""; - if ($grubVersion > 1) { - # ZFS is completely separate logic as zpools are always identified by a label - # or custom UUID - if ($fs->type eq 'zfs') { - my $sid = index($fs->device, '/'); - - if ($sid < 0) { - $search = '--label ' . $fs->device; - $path = '/@' . $path; - } else { - $search = '--label ' . substr($fs->device, 0, $sid); - $path = '/' . substr($fs->device, $sid) . '/@' . $path; + # ZFS is completely separate logic as zpools are always identified by a label + # or custom UUID + if ($fs->type eq 'zfs') { + my $sid = index($fs->device, '/'); + + if ($sid < 0) { + $search = '--label ' . $fs->device; + $path = '/@' . $path; + } else { + $search = '--label ' . substr($fs->device, 0, $sid); + $path = '/' . substr($fs->device, $sid) . '/@' . $path; + } + } else { + my %types = ('uuid' => '--fs-uuid', 'label' => '--label'); + + if ($fsIdentifier eq 'provided') { + # If the provided dev is identifying the partition using a label or uuid, + # we should get the label / uuid and do a proper search + my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/; + if ($#matches > 1) { + die "Too many matched devices" + } elsif ($#matches == 1) { + $search = "$types{$matches[0]} $matches[1]" } } else { - my %types = ('uuid' => '--fs-uuid', 'label' => '--label'); - - if ($fsIdentifier eq 'provided') { - # If the provided dev is identifying the partition using a label or uuid, - # we should get the label / uuid and do a proper search - my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/; - if ($#matches > 1) { - die "Too many matched devices" - } elsif ($#matches == 1) { - $search = "$types{$matches[0]} $matches[1]" - } - } else { - # Determine the identifying type - $search = $types{$fsIdentifier} . ' '; + # Determine the identifying type + $search = $types{$fsIdentifier} . ' '; - # Based on the type pull in the identifier from the system - my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid", "-o", "export", @{[$fs->device]}); - if ($status != 0) { - die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}"; - } - my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/; - if ($#matches != 0) { - die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n" - } - $search .= $matches[0]; + # Based on the type pull in the identifier from the system + my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid", "-o", "export", @{[$fs->device]}); + if ($status != 0) { + die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}"; + } + my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/; + if ($#matches != 0) { + die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n" } + $search .= $matches[0]; + } - # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes - if ($fs->type eq 'btrfs') { - my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "show", @{[$fs->mount]}); + # BTRFS is a special case in that we need to fix the referrenced path based on subvolumes + if ($fs->type eq 'btrfs') { + my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "show", @{[$fs->mount]}); + if ($status != 0) { + die "Failed to retrieve subvolume info for @{[$fs->mount]}\n"; + } + my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s; + if ($#ids > 0) { + die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n" + } elsif ($#ids == 0) { + my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "list", @{[$fs->mount]}); if ($status != 0) { - die "Failed to retrieve subvolume info for @{[$fs->mount]}\n"; + die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n"; } - my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s; - if ($#ids > 0) { - die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n" - } elsif ($#ids == 0) { - my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs", "subvol", "list", @{[$fs->mount]}); - if ($status != 0) { - die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n"; - } - my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/; - if ($#paths > 0) { - die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n"; - } elsif ($#paths != 0) { - die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n"; - } - $path = "/$paths[0]$path"; + my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/; + if ($#paths > 0) { + die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n"; + } elsif ($#paths != 0) { + die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n"; } + $path = "/$paths[0]$path"; } } - if (not $search eq "") { - $search = "search --set=drive$driveid " . $search; - $path = "(\$drive$driveid)$path"; - $driveid += 1; - } + } + if (not $search eq "") { + $search = "search --set=drive$driveid " . $search; + $path = "(\$drive$driveid)$path"; + $driveid += 1; } return Grub->new(path => $path, search => $search); } @@ -258,166 +253,151 @@ if ($copyKernels == 0) { # Generate the header. my $conf .= "# Automatically generated. DO NOT EDIT THIS FILE!\n"; -if ($grubVersion == 1) { - # $defaultEntry might be "saved", indicating that we want to use the last selected configuration as default. - # Incidentally this is already the correct value for the grub 1 config to achieve this behaviour. - $conf .= " - default $defaultEntry - timeout $timeout - "; - if ($splashImage) { - copy $splashImage, "$bootPath/background.xpm.gz" or die "cannot copy $splashImage to $bootPath: $!\n"; - $conf .= "splashimage " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background.xpm.gz\n"; +my @users = (); +foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) { + my $name = $user->findvalue('@name') or die; + my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value'); + my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value'); + my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value'); + my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value'); + + if ($hashedPasswordFile) { + open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!"; + $hashedPassword = <$f>; + chomp $hashedPassword; + } + if ($passwordFile) { + open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!"; + $password = <$f>; + chomp $password; } -} - -else { - my @users = (); - foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) { - my $name = $user->findvalue('@name') or die; - my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value'); - my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value'); - my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value'); - my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value'); - - if ($hashedPasswordFile) { - open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!"; - $hashedPassword = <$f>; - chomp $hashedPassword; - } - if ($passwordFile) { - open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!"; - $password = <$f>; - chomp $password; - } - if ($hashedPassword) { - if (index($hashedPassword, "grub.pbkdf2.") == 0) { - $conf .= "\npassword_pbkdf2 $name $hashedPassword"; - } - else { - die "Password hash for GRUB user '$name' is not valid!"; - } - } - elsif ($password) { - $conf .= "\npassword $name $password"; + if ($hashedPassword) { + if (index($hashedPassword, "grub.pbkdf2.") == 0) { + $conf .= "\npassword_pbkdf2 $name $hashedPassword"; } else { - die "GRUB user '$name' has no password!"; + die "Password hash for GRUB user '$name' is not valid!"; } - push(@users, $name); } - if (@users) { - $conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n"; - } - - if ($copyKernels == 0) { - $conf .= " - " . $grubStore->search; + elsif ($password) { + $conf .= "\npassword $name $password"; } - # FIXME: should use grub-mkconfig. - my $defaultEntryText = $defaultEntry; - if ($saveDefault) { - $defaultEntryText = "\"\${saved_entry}\""; + else { + die "GRUB user '$name' has no password!"; } - $conf .= " - " . $grubBoot->search . " - if [ -s \$prefix/grubenv ]; then - load_env - fi + push(@users, $name); +} +if (@users) { + $conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n"; +} - # ‘grub-reboot’ sets a one-time saved entry, which we process here and - # then delete. - if [ \"\${next_entry}\" ]; then - set default=\"\${next_entry}\" - set next_entry= - save_env next_entry - set timeout=1 - set boot_once=true - else - set default=$defaultEntryText - set timeout=$timeout +if ($copyKernels == 0) { + $conf .= " + " . $grubStore->search; +} +# FIXME: should use grub-mkconfig. +my $defaultEntryText = $defaultEntry; +if ($saveDefault) { + $defaultEntryText = "\"\${saved_entry}\""; +} +$conf .= " + " . $grubBoot->search . " + if [ -s \$prefix/grubenv ]; then + load_env + fi + + # ‘grub-reboot’ sets a one-time saved entry, which we process here and + # then delete. + if [ \"\${next_entry}\" ]; then + set default=\"\${next_entry}\" + set next_entry= + save_env next_entry + set timeout=1 + set boot_once=true + else + set default=$defaultEntryText + set timeout=$timeout + fi + + function savedefault { + if [ -z \"\${boot_once}\"]; then + saved_entry=\"\${chosen}\" + save_env saved_entry fi + } - function savedefault { - if [ -z \"\${boot_once}\"]; then - saved_entry=\"\${chosen}\" - save_env saved_entry - fi - } - - # Setup the graphics stack for bios and efi systems - if [ \"\${grub_platform}\" = \"efi\" ]; then - insmod efi_gop - insmod efi_uga - else - insmod vbe + # Setup the graphics stack for bios and efi systems + if [ \"\${grub_platform}\" = \"efi\" ]; then + insmod efi_gop + insmod efi_uga + else + insmod vbe + fi +"; + +if ($font) { + copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n"; + $conf .= " + insmod font + if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then + insmod gfxterm + if [ \"\${grub_platform}\" = \"efi\" ]; then + set gfxmode=$gfxmodeEfi + set gfxpayload=$gfxpayloadEfi + else + set gfxmode=$gfxmodeBios + set gfxpayload=$gfxpayloadBios + fi + terminal_output gfxterm fi "; - - if ($font) { - copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n"; - $conf .= " - insmod font - if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then - insmod gfxterm - if [ \"\${grub_platform}\" = \"efi\" ]; then - set gfxmode=$gfxmodeEfi - set gfxpayload=$gfxpayloadEfi - else - set gfxmode=$gfxmodeBios - set gfxpayload=$gfxpayloadBios - fi - terminal_output gfxterm - fi - "; +} +if ($splashImage) { + # Keeps the image's extension. + my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$"); + # The module for jpg is jpeg. + if ($suffix eq ".jpg") { + $suffix = ".jpeg"; } - if ($splashImage) { - # Keeps the image's extension. - my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$"); - # The module for jpg is jpeg. - if ($suffix eq ".jpg") { - $suffix = ".jpeg"; - } - if ($backgroundColor) { - $conf .= " - background_color '$backgroundColor' - "; - } - copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n"; + if ($backgroundColor) { $conf .= " - insmod " . substr($suffix, 1) . " - if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; then - set color_normal=white/black - set color_highlight=black/white - else - set menu_color_normal=cyan/blue - set menu_color_highlight=white/blue - fi + background_color '$backgroundColor' "; } + copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n"; + $conf .= " + insmod " . substr($suffix, 1) . " + if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; then + set color_normal=white/black + set color_highlight=black/white + else + set menu_color_normal=cyan/blue + set menu_color_highlight=white/blue + fi + "; +} - rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme"; +rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme"; - if ($theme) { - # Copy theme - rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n"; - $conf .= " - # Sets theme. - set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt - export theme - # Load theme fonts, if any - "; +if ($theme) { + # Copy theme + rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n"; + $conf .= " + # Sets theme. + set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt + export theme + # Load theme fonts, if any + "; - find( { wanted => sub { - if ($_ =~ /\.pf2$/i) { - $font = File::Spec->abs2rel($File::Find::name, $theme); - $conf .= " - loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font - "; - } - }, no_chdir => 1 }, $theme ); - } + find( { wanted => sub { + if ($_ =~ /\.pf2$/i) { + $font = File::Spec->abs2rel($File::Find::name, $theme); + $conf .= " + loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font + "; + } + }, no_chdir => 1 }, $theme ); } $conf .= "$extraConfig\n"; @@ -494,31 +474,19 @@ sub addEntry { readFile("$path/kernel-params"); my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : ""; - if ($grubVersion == 1) { - $conf .= "title $name\n"; - $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; - $conf .= " kernel $xen $xenParams\n" if $xen; - $conf .= " " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n"; - $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n"; - if ($saveDefault) { - $conf .= " savedefault\n"; - } - $conf .= "\n"; - } else { - $conf .= "menuentry \"$name\" " . $options . " {\n"; - if ($saveDefault) { - $conf .= " savedefault\n"; - } - $conf .= $grubBoot->search . "\n"; - if ($copyKernels == 0) { - $conf .= $grubStore->search . "\n"; - } - $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; - $conf .= " multiboot $xen $xenParams\n" if $xen; - $conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n"; - $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n"; - $conf .= "}\n\n"; + $conf .= "menuentry \"$name\" " . $options . " {\n"; + if ($saveDefault) { + $conf .= " savedefault\n"; } + $conf .= $grubBoot->search . "\n"; + if ($copyKernels == 0) { + $conf .= $grubStore->search . "\n"; + } + $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; + $conf .= " multiboot $xen $xenParams\n" if $xen; + $conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n"; + $conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n"; + $conf .= "}\n\n"; } @@ -562,7 +530,7 @@ sub addProfile { my ($profile, $description) = @_; # Add entries for all generations of this profile. - $conf .= "submenu \"$description\" --class submenu {\n" if $grubVersion == 2; + $conf .= "submenu \"$description\" --class submenu {\n"; sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; } @@ -585,17 +553,15 @@ sub addProfile { addEntry("@distroName@ - Configuration " . nrFromGen($link) . " ($date - $version)", $link, $subEntryOptions, 0); } - $conf .= "}\n" if $grubVersion == 2; + $conf .= "}\n"; } addProfile "/nix/var/nix/profiles/system", "@distroName@ - All configurations"; -if ($grubVersion == 2) { - for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") { - my $name = basename($profile); - next unless $name =~ /^\w+$/; - addProfile $profile, "@distroName@ - Profile '$name'"; - } +for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") { + my $name = basename($profile); + next unless $name =~ /^\w+$/; + addProfile $profile, "@distroName@ - Profile '$name'"; } # extraPrepareConfig could refer to @bootPath@, which we have to substitute @@ -607,16 +573,14 @@ if ($extraPrepareConfig ne "") { } # write the GRUB config. -my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg"; +my $confFile = "$bootPath/grub/grub.cfg"; my $tmpFile = $confFile . ".tmp"; writeFile($tmpFile, $conf); # check whether to install GRUB EFI or not sub getEfiTarget { - if ($grubVersion == 1) { - return "no" - } elsif (($grub ne "") && ($grubEfi ne "")) { + if (($grub ne "") && ($grubEfi ne "")) { # EFI can only be installed when target is set; # A target is also required then for non-EFI grub if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die } @@ -741,7 +705,7 @@ symlink "$bootPath", "$tmpDir/boot" or die "Failed to symlink $tmpDir/boot: $!"; if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) { foreach my $dev (@deviceTargets) { next if $dev eq "nodev"; - print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n"; + print STDERR "installing the GRUB 2 boot loader on $dev...\n"; my @command = ("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev), @extraGrubInstallArgs); if ($forceInstall eq "true") { push @command, "--force"; @@ -756,7 +720,7 @@ if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) { # install EFI GRUB if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) { - print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n"; + print STDERR "installing the GRUB 2 boot loader into $efiSysMountPoint...\n"; my @command = ("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", @extraGrubInstallArgs); if ($forceInstall eq "true") { push @command, "--force"; diff --git a/nixos/modules/system/boot/loader/grub/ipxe.nix b/nixos/modules/system/boot/loader/grub/ipxe.nix index adddcbee0164d..d926b7ceaa6e6 100644 --- a/nixos/modules/system/boot/loader/grub/ipxe.nix +++ b/nixos/modules/system/boot/loader/grub/ipxe.nix @@ -46,11 +46,7 @@ in config = mkIf (builtins.length scripts != 0) { - boot.loader.grub.extraEntries = - if config.boot.loader.grub.version == 2 then - toString (map grubEntry scripts) - else - throw "iPXE is not supported with GRUB 1."; + boot.loader.grub.extraEntries = toString (map grubEntry scripts); boot.loader.grub.extraFiles = { "ipxe.lkrn" = "${pkgs.ipxe}/ipxe.lkrn"; } diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix index ccb6e8cc3caf5..ee969e9bff5bf 100644 --- a/nixos/modules/system/boot/loader/grub/memtest.nix +++ b/nixos/modules/system/boot/loader/grub/memtest.nix @@ -84,15 +84,11 @@ in }) (mkIf (cfg.enable && !efiSupport) { - boot.loader.grub.extraEntries = - if config.boot.loader.grub.version == 2 then - '' - menuentry "Memtest86+" { - linux16 @bootRoot@/memtest.bin ${toString cfg.params} - } - '' - else - throw "Memtest86+ is not supported with GRUB 1."; + boot.loader.grub.extraEntries = '' + menuentry "Memtest86+" { + linux16 @bootRoot@/memtest.bin ${toString cfg.params} + } + ''; boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin"; }) diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 52413a13f07b7..07f51f43184d9 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -2848,7 +2848,7 @@ let '' + optionalString (def.tokenBucketFilterConfig != { }) '' [TokenBucketFilter] - ${attrsToSection def.tockenBucketFilterConfig} + ${attrsToSection def.tokenBucketFilterConfig} '' + optionalString (def.pieConfig != { }) '' [PIE] diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index 835788dbbc976..387c27d86ebbe 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -293,6 +293,9 @@ checkFS() { # Skip fsck for inherently readonly filesystems. if [ "$fsType" = squashfs ]; then return 0; fi + # Skip fsck.erofs because it is still experimental. + if [ "$fsType" = erofs ]; then return 0; fi + # If we couldn't figure out the FS type, then skip fsck. if [ "$fsType" = auto ]; then echo 'cannot check filesystem with type "auto"!' diff --git a/nixos/modules/system/boot/systemd/repart.nix b/nixos/modules/system/boot/systemd/repart.nix index 8f3a700237700..251c7e2361231 100644 --- a/nixos/modules/system/boot/systemd/repart.nix +++ b/nixos/modules/system/boot/systemd/repart.nix @@ -72,11 +72,6 @@ in }; config = lib.mkIf (cfg.enable || initrdCfg.enable) { - # Always link the definitions into /etc so that they are also included in - # the /nix/store of the sysroot during early userspace (i.e. while in the - # initrd). - environment.etc."repart.d".source = definitionsDirectory; - boot.initrd.systemd = lib.mkIf initrdCfg.enable { additionalUpstreamUnits = [ "systemd-repart.service" @@ -86,33 +81,40 @@ in "${config.boot.initrd.systemd.package}/bin/systemd-repart" ]; + contents."/etc/repart.d".source = definitionsDirectory; + # Override defaults in upstream unit. services.systemd-repart = { - # Unset the conditions as they cannot be met before activation because - # the definition files are not stored in the expected locations. - unitConfig.ConditionDirectoryNotEmpty = [ - " " # required to unset the previous value. - ]; + # systemd-repart tries to create directories in /var/tmp by default to + # store large temporary files that benefit from persistence on disk. In + # the initrd, however, /var/tmp does not provide more persistence than + # /tmp, so we re-use it here. + environment."TMPDIR" = "/tmp"; serviceConfig = { - # systemd-repart runs before the activation script. Thus we cannot - # rely on them being linked in /etc already. Instead we have to - # explicitly pass their location in the sysroot to the binary. ExecStart = [ " " # required to unset the previous value. + # When running in the initrd, systemd-repart by default searches + # for definition files in /sysroot or /sysusr. We tell it to look + # in the initrd itself. ''${config.boot.initrd.systemd.package}/bin/systemd-repart \ - --definitions=/sysroot${definitionsDirectory} \ + --definitions=/etc/repart.d \ --dry-run=no '' ]; }; - # Because the initrd does not have the `initrd-usr-fs.target` the - # upestream unit runs too early in the boot process, before the sysroot - # is available. However, systemd-repart needs access to the sysroot to - # find the definition files. + # systemd-repart needs to run after /sysroot (or /sysuser, but we don't + # have it) has been mounted because otherwise it cannot determine the + # device (i.e disk) to operate on. If you want to run systemd-repart + # without /sysroot, you have to explicitly tell it which device to + # operate on. after = [ "sysroot.mount" ]; }; }; + environment.etc = lib.mkIf cfg.enable { + "repart.d".source = definitionsDirectory; + }; + systemd = lib.mkIf cfg.enable { additionalUpstreamSystemUnits = [ "systemd-repart.service" @@ -120,4 +122,5 @@ in }; }; + meta.maintainers = with lib.maintainers; [ nikstur ]; } diff --git a/nixos/modules/tasks/filesystems/envfs.nix b/nixos/modules/tasks/filesystems/envfs.nix index 76344f5f87eaf..365cb46ff2fe3 100644 --- a/nixos/modules/tasks/filesystems/envfs.nix +++ b/nixos/modules/tasks/filesystems/envfs.nix @@ -12,12 +12,13 @@ let ln -s ${config.environment.usrbinenv} $out/env ln -s ${config.environment.binsh} $out/sh '' + cfg.extraFallbackPathCommands)}" + "nofail" ]; }; "/bin" = { device = "/usr/bin"; fsType = "none"; - options = [ "bind" ]; + options = [ "bind" "nofail" ]; }; }; in { diff --git a/nixos/modules/tasks/filesystems/erofs.nix b/nixos/modules/tasks/filesystems/erofs.nix new file mode 100644 index 0000000000000..a3d6576693505 --- /dev/null +++ b/nixos/modules/tasks/filesystems/erofs.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: + +let + + inInitrd = lib.any (fs: fs == "erofs") config.boot.initrd.supportedFilesystems; + inSystem = lib.any (fs: fs == "erofs") config.boot.supportedFilesystems; + +in + +{ + config = lib.mkIf (inInitrd || inSystem) { + + system.fsPackages = [ pkgs.erofs-utils ]; + + boot.initrd.availableKernelModules = lib.mkIf inInitrd [ "erofs" ]; + + # fsck.erofs is currently experimental and should not be run as a + # privileged user. Thus, it is not included in the initrd. + + }; +} diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix index 028099c64643c..9c4bbecf48090 100644 --- a/nixos/modules/testing/test-instrumentation.nix +++ b/nixos/modules/testing/test-instrumentation.nix @@ -36,8 +36,16 @@ in while ! exec 2> /dev/${qemu-common.qemuSerialDevice}; do sleep 0.1; done echo "connecting to host..." >&2 stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion - echo - PS1= exec /bin/sh + # The following line is essential since it signals to + # the test driver that the shell is ready. + # See: the connect method in the Machine class. + echo "Spawning backdoor root shell..." + # Passing the terminal device makes bash run non-interactively. + # Otherwise we get errors on the terminal because bash tries to + # setup things like job control. + # Note: calling bash explicitely here instead of sh makes sure that + # we can also run non-NixOS guests during tests. + PS1= exec /usr/bin/env bash --norc /dev/hvc0 ''; serviceConfig.KillSignal = "SIGHUP"; }; diff --git a/nixos/modules/virtualisation/azure-common.nix b/nixos/modules/virtualisation/azure-common.nix index f29d368137ae0..cd1ffdb6cbcc3 100644 --- a/nixos/modules/virtualisation/azure-common.nix +++ b/nixos/modules/virtualisation/azure-common.nix @@ -12,7 +12,6 @@ with lib; # Generate a GRUB menu. boot.loader.grub.device = "/dev/sda"; - boot.loader.grub.version = 2; boot.loader.timeout = 0; boot.growPartition = true; diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix index 96b749102241d..55b285b69147a 100644 --- a/nixos/modules/virtualisation/lxc-container.nix +++ b/nixos/modules/virtualisation/lxc-container.nix @@ -161,6 +161,11 @@ in extraCommands = "mkdir -p proc sys dev"; }; + system.build.installBootLoader = pkgs.writeScript "install-lxd-sbin-init.sh" '' + #!${pkgs.runtimeShell} + ln -fs "$1/init" /sbin/init + ''; + # Add the overrides from lxd distrobuilder # https://github.com/lxc/distrobuilder/blob/05978d0d5a72718154f1525c7d043e090ba7c3e0/distrobuilder/main.go#L630 systemd.packages = [ diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix index d54e2ed3f3ae1..c3949564d4bde 100644 --- a/nixos/modules/virtualisation/nixos-containers.nix +++ b/nixos/modules/virtualisation/nixos-containers.nix @@ -515,6 +515,10 @@ in in [ extraConfig ] ++ (map (x: x.value) defs); prefix = [ "containers" name ]; inherit (config) specialArgs; + + # The system is inherited from the host above. + # Set it to null, to remove the "legacy" entrypoint's non-hermetic default. + system = null; }).config; }; }; diff --git a/nixos/modules/virtualisation/proxmox-image.nix b/nixos/modules/virtualisation/proxmox-image.nix index c66a4f178ec73..82b33a341799e 100644 --- a/nixos/modules/virtualisation/proxmox-image.nix +++ b/nixos/modules/virtualisation/proxmox-image.nix @@ -193,6 +193,7 @@ with lib; sha256 = "sha256-9rN1x5UfcoQCeYsLqrsthkeMpT1Eztvvq74cRr9G+Dk="; }; patches = [ + # Proxmox' VMA tool is published as a particular patch upon QEMU (pkgs.fetchpatch { url = let @@ -201,6 +202,21 @@ with lib; in "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;hb=${rev};f=${path}"; hash = "sha256-2Dz+ceTwrcyYYxi76RtyY3v15/2pwGcDhFuoZWlgbjc="; }) + + # Proxmox' VMA tool uses O_DIRECT which fails on tmpfs + # Filed to upstream issue tracker: https://bugzilla.proxmox.com/show_bug.cgi?id=4710 + (pkgs.writeText "inline.patch" '' + --- a/vma-writer.c 2023-05-01 15:11:13.361341177 +0200 + +++ b/vma-writer.c 2023-05-01 15:10:51.785293129 +0200 + @@ -306,7 +306,7 @@ + /* try to use O_NONBLOCK */ + fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK); + } else { + - oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_EXCL; + + oflags = O_NONBLOCK|O_WRONLY|O_EXCL; + vmaw->fd = qemu_create(filename, oflags, 0644, errp); + } + '') ]; buildInputs = super.buildInputs ++ [ pkgs.libuuid ]; diff --git a/nixos/modules/virtualisation/rosetta.nix b/nixos/modules/virtualisation/rosetta.nix index 109b114d649c5..ee811b571b8f8 100644 --- a/nixos/modules/virtualisation/rosetta.nix +++ b/nixos/modules/virtualisation/rosetta.nix @@ -50,11 +50,19 @@ in } ]; - fileSystems."${cfg.mountPoint}" = { + fileSystems."${cfg.mountPoint}" = { device = cfg.mountTag; fsType = "virtiofs"; }; + + nix.settings = { + extra-platforms = [ "x86_64-linux" ]; + extra-sandbox-paths = [ + "/run/binfmt" + cfg.mountPoint + ]; + }; boot.binfmt.registrations.rosetta = { interpreter = "${cfg.mountPoint}/rosetta"; diff --git a/nixos/modules/virtualisation/xen-domU.nix b/nixos/modules/virtualisation/xen-domU.nix index c00b984c2ce04..ce5a482b1145b 100644 --- a/nixos/modules/virtualisation/xen-domU.nix +++ b/nixos/modules/virtualisation/xen-domU.nix @@ -3,7 +3,6 @@ { ... }: { - boot.loader.grub.version = 2; boot.loader.grub.device = "nodev"; boot.initrd.kernelModules = |