diff options
Diffstat (limited to 'nixos')
50 files changed, 1326 insertions, 639 deletions
diff --git a/nixos/doc/manual/configuration/x-windows.xml b/nixos/doc/manual/configuration/x-windows.xml index dd879702d7dc0..a499f0080d705 100644 --- a/nixos/doc/manual/configuration/x-windows.xml +++ b/nixos/doc/manual/configuration/x-windows.xml @@ -150,7 +150,6 @@ <xref linkend="opt-services.xserver.videoDrivers"/> = [ "nvidiaLegacy390" ]; <xref linkend="opt-services.xserver.videoDrivers"/> = [ "nvidiaLegacy340" ]; <xref linkend="opt-services.xserver.videoDrivers"/> = [ "nvidiaLegacy304" ]; -<xref linkend="opt-services.xserver.videoDrivers"/> = [ "nvidiaLegacy173" ]; </programlisting> You may need to reboot after enabling this driver to prevent a clash with other kernel modules. @@ -159,21 +158,16 @@ <simplesect xml:id="sec-x11--graphics-cards-amd"> <title>Proprietary AMD drivers</title> <para> - AMD provides a proprietary driver for its graphics cards that has better 3D - performance than the X.org drivers. It is not enabled by default because - it’s not free software. You can enable it as follows: + AMD provides a proprietary driver for its graphics cards that is not + enabled by default because it’s not Free Software, is often broken + in nixpkgs and as of this writing doesn't offer more features or + performance. If you still want to use it anyway, you need to explicitly set: <programlisting> -<xref linkend="opt-services.xserver.videoDrivers"/> = [ "ati_unfree" ]; +<xref linkend="opt-services.xserver.videoDrivers"/> = [ "amdgpu-pro" ]; </programlisting> You will need to reboot after enabling this driver to prevent a clash with other kernel modules. </para> - <note> - <para> - For recent AMD GPUs you most likely want to keep either the defaults - or <literal>"amdgpu"</literal> (both free). - </para> - </note> </simplesect> <simplesect xml:id="sec-x11-touchpads"> <title>Touchpads</title> diff --git a/nixos/doc/manual/development/writing-nixos-tests.xml b/nixos/doc/manual/development/writing-nixos-tests.xml index cab4c067e0d35..5f70f74d5d90d 100644 --- a/nixos/doc/manual/development/writing-nixos-tests.xml +++ b/nixos/doc/manual/development/writing-nixos-tests.xml @@ -449,5 +449,16 @@ import ./make-test-python.nix { ''; } </programlisting> + This will produce a Nix warning at evaluation time. To fully disable the + linter, wrap the test script in comment directives to disable the Black linter + directly (again, don't commit this within the Nixpkgs repository): +<programlisting> + testScript = + '' + # fmt: off + <replaceable>Python code…</replaceable> + # fmt: on + ''; +</programlisting> </para> </section> diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml index 566cd5d7240ed..e052632ecafd9 100644 --- a/nixos/doc/manual/release-notes/rl-2105.xml +++ b/nixos/doc/manual/release-notes/rl-2105.xml @@ -39,6 +39,24 @@ (<link xlink:href="https://github.com/NixOS/nixpkgs/issues/75478">#7547</link>). </para> </listitem> + <listitem> + <para> + <link xlink:href="https://www.privoxy.org/">Privoxy</link> has been updated + to version 3.0.32 (See <link xlink:href="https://lists.privoxy.org/pipermail/privoxy-announce/2021-February/000007.html">announcement</link>). + Compared to the previous release, Privoxy has gained support for HTTPS + inspection (still experimental), Brotli decompression, several new filters + and lots of bug fixes, including security ones. In addition, the package + is now built with compression and external filters support, which were + previously disabled. + </para> + <para> + Regarding the NixOS module, new options for HTTPS inspection have been added + and <option>services.privoxy.extraConfig</option> has been replaced by the new + <xref linkend="opt-services.privoxy.settings"/> + (See <link xlink:href="https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md">RFC 0042</link> + for the motivation). + </para> + </listitem> </itemizedlist> </section> @@ -539,6 +557,26 @@ self: super: <package>imagemagick6Big</package> if you need the older version. </para> </listitem> + <listitem> + <para> + <xref linkend="opt-services.xserver.videoDrivers" /> no longer uses the deprecated <literal>cirrus</literal> and <literal>vesa</literal> device dependent X drivers by default. It also enables both <literal>amdgpu</literal> and <literal>nouveau</literal> drivers by default now. + </para> + </listitem> + <listitem> + <para> + The <package>apacheKafka</package> packages are now built with + version-matched JREs. Versions 2.6 and above, the ones that recommend it, + use jdk11, while versions below remain on jdk8. The NixOS service has + been adjusted to start the service using the same version as the package, + adjustable with the new + <link linkend="opt-services.apache-kafka.jre">services.apache-kafka.jre</link> + option. Furthermore, the default list of + <link linkend="opt-services.apache-kafka.jvmOptions">services.apache-kafka.jvmOptions</link> + have been removed. You should set your own according to the + <link xlink:href="https://kafka.apache.org/documentation/#java">upstream documentation</link> + for your Kafka version. + </para> + </listitem> </itemizedlist> </section> @@ -754,6 +792,49 @@ self: super: once during the time when the timer was inactive. </para> </listitem> + <listitem> + <para> + The <literal>rustPlatform.buildRustPackage</literal> function is split into several hooks: + <package>cargoSetupHook</package> to set up vendoring for Cargo-based projects, + <package>cargoBuildHook</package> to build a project using Cargo, + <package>cargoInstallHook</package> to install a project using Cargo, and + <package>cargoCheckHook</package> to run tests in Cargo-based projects. With this change, + mixed-language projects can use the relevant hooks within builders other than + <literal>buildRustPackage</literal>. However, these changes also required several API changes to + <literal>buildRustPackage</literal> itself: + + <itemizedlist> + <listitem> + <para> + The <literal>target</literal> argument was removed. Instead, <literal>buildRustPackage</literal> + will always use the same target as the C/C++ compiler that is used. + </para> + </listitem> + <listitem> + <para> + The <literal>cargoParallelTestThreads</literal> argument was removed. Parallel tests are + now disabled through <literal>dontUseCargoParallelTests</literal>. + </para> + </listitem> + </itemizedlist> + </para> + </listitem> + <listitem> + <para> + The <literal>rustPlatform.maturinBuildHook</literal> hook was added. This hook can be used + with <literal>buildPythonPackage</literal> to build Python packages that are written in Rust + and use Maturin as their build tool. + </para> + </listitem> + <listitem> + <para> + Kubernetes has <link xlink:href="https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/">deprecated docker</link> as container runtime. + As a consequence, the Kubernetes module now has support for configuration of custom remote container runtimes and enables containerd by default. + Note that containerd is more strict regarding container image OCI-compliance. + As an example, images with CMD or ENTRYPOINT defined as strings (not lists) will fail on containerd, while working fine on docker. + Please test your setup and container images with containerd prior to upgrading. + </para> + </listitem> </itemizedlist> </section> </section> diff --git a/nixos/lib/qemu-flags.nix b/nixos/lib/qemu-flags.nix index 0f06624589354..f786745ba3247 100644 --- a/nixos/lib/qemu-flags.nix +++ b/nixos/lib/qemu-flags.nix @@ -18,13 +18,15 @@ rec { ]; qemuSerialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0" - else if pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64 then "ttyAMA0" + else if (with pkgs.stdenv.hostPlatform; isAarch32 || isAarch64 || isPower) then "ttyAMA0" else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'"; qemuBinary = qemuPkg: { x86_64-linux = "${qemuPkg}/bin/qemu-kvm -cpu max"; armv7l-linux = "${qemuPkg}/bin/qemu-system-arm -enable-kvm -machine virt -cpu host"; aarch64-linux = "${qemuPkg}/bin/qemu-system-aarch64 -enable-kvm -machine virt,gic-version=host -cpu host"; + powerpc64le-linux = "${qemuPkg}/bin/qemu-system-ppc64 -machine powernv"; + powerpc64-linux = "${qemuPkg}/bin/qemu-system-ppc64 -machine powernv"; x86_64-darwin = "${qemuPkg}/bin/qemu-kvm -cpu max"; }.${pkgs.stdenv.hostPlatform.system} or "${qemuPkg}/bin/qemu-kvm"; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index f91c21ad5cbb5..f226194efd569 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -242,6 +242,7 @@ ./services/backup/automysqlbackup.nix ./services/backup/bacula.nix ./services/backup/borgbackup.nix + ./services/backup/borgmatic.nix ./services/backup/duplicati.nix ./services/backup/duplicity.nix ./services/backup/mysql-backup.nix @@ -1053,6 +1054,7 @@ ./testing/service-runner.nix ./virtualisation/anbox.nix ./virtualisation/container-config.nix + ./virtualisation/containerd.nix ./virtualisation/containers.nix ./virtualisation/nixos-containers.nix ./virtualisation/oci-containers.nix diff --git a/nixos/modules/programs/fish_completion-generator.patch b/nixos/modules/programs/fish_completion-generator.patch index 997f38c5066dc..fa207e484c99e 100644 --- a/nixos/modules/programs/fish_completion-generator.patch +++ b/nixos/modules/programs/fish_completion-generator.patch @@ -1,13 +1,14 @@ --- a/create_manpage_completions.py +++ b/create_manpage_completions.py -@@ -844,10 +844,6 @@ def parse_manpage_at_path(manpage_path, output_directory): +@@ -879,10 +879,6 @@ def parse_manpage_at_path(manpage_path, output_directory): + ) + return False - built_command_output.insert(0, "# " + CMDNAME) +- # Output the magic word Autogenerated so we can tell if we can overwrite this +- built_command_output.insert( +- 0, "# " + CMDNAME + "\n# Autogenerated from man page " + manpage_path +- ) + # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABLE PARSER! Was really using Type2 but reporting TypeDeroffManParser -- # Output the magic word Autogenerated so we can tell if we can overwrite this -- built_command_output.insert( -- 1, "# Autogenerated from man page " + manpage_path -- ) - # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABILE PARSER! Was really using Type2 but reporting TypeDeroffManParser - - for line in built_command_output: + for line in built_command_output: + diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix index 6b805c374a017..ff4deba2bf0ae 100644 --- a/nixos/modules/programs/steam.nix +++ b/nixos/modules/programs/steam.nix @@ -12,7 +12,25 @@ let else [ package32 ] ++ extraPackages32; }; in { - options.programs.steam.enable = mkEnableOption "steam"; + options.programs.steam = { + enable = mkEnableOption "steam"; + + remotePlay.openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for Steam Remote Play. + ''; + }; + + dedicatedServer.openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for Source Dedicated Server. + ''; + }; + }; config = mkIf cfg.enable { hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932 @@ -27,6 +45,18 @@ in { hardware.steam-hardware.enable = true; environment.systemPackages = [ steam steam.run ]; + + networking.firewall = lib.mkMerge [ + (mkIf cfg.remotePlay.openFirewall { + allowedTCPPorts = [ 27036 ]; + allowedUDPPortRanges = [ { from = 27031; to = 27036; } ]; + }) + + (mkIf cfg.dedicatedServer.openFirewall { + allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port + allowedUDPPorts = [ 27015 ]; # Gameplay traffic + }) + ]; }; meta.maintainers = with maintainers; [ mkg20001 ]; diff --git a/nixos/modules/services/audio/spotifyd.nix b/nixos/modules/services/audio/spotifyd.nix index a589153248fe9..9279a03aed4e5 100644 --- a/nixos/modules/services/audio/spotifyd.nix +++ b/nixos/modules/services/audio/spotifyd.nix @@ -27,6 +27,7 @@ in wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" "sound.target" ]; description = "spotifyd, a Spotify playing daemon"; + environment.SHELL = "/bin/sh"; serviceConfig = { ExecStart = "${pkgs.spotifyd}/bin/spotifyd --no-daemon --cache-path /var/cache/spotifyd --config-path ${spotifydConf}"; Restart = "always"; diff --git a/nixos/modules/services/backup/borgmatic.nix b/nixos/modules/services/backup/borgmatic.nix new file mode 100644 index 0000000000000..5e5c0bbecccaa --- /dev/null +++ b/nixos/modules/services/backup/borgmatic.nix @@ -0,0 +1,57 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.borgmatic; + cfgfile = pkgs.writeText "config.yaml" (builtins.toJSON cfg.settings); +in { + options.services.borgmatic = { + enable = mkEnableOption "borgmatic"; + + settings = mkOption { + description = '' + See https://torsion.org/borgmatic/docs/reference/configuration/ + ''; + type = types.submodule { + freeformType = with lib.types; attrsOf anything; + options.location = { + source_directories = mkOption { + type = types.listOf types.str; + description = '' + List of source directories to backup (required). Globs and + tildes are expanded. + ''; + example = [ "/home" "/etc" "/var/log/syslog*" ]; + }; + repositories = mkOption { + type = types.listOf types.str; + description = '' + Paths to local or remote repositories (required). Tildes are + expanded. Multiple repositories are backed up to in + sequence. Borg placeholders can be used. See the output of + "borg help placeholders" for details. See ssh_command for + SSH options like identity file or port. If systemd service + is used, then add local repository paths in the systemd + service file to the ReadWritePaths list. + ''; + example = [ + "user@backupserver:sourcehostname.borg" + "user@backupserver:{fqdn}" + ]; + }; + }; + }; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.borgmatic ]; + + environment.etc."borgmatic/config.yaml".source = cfgfile; + + systemd.packages = [ pkgs.borgmatic ]; + + }; +} diff --git a/nixos/modules/services/cluster/kubernetes/addons/dns.nix b/nixos/modules/services/cluster/kubernetes/addons/dns.nix index f12e866930dab..24d86628b211d 100644 --- a/nixos/modules/services/cluster/kubernetes/addons/dns.nix +++ b/nixos/modules/services/cluster/kubernetes/addons/dns.nix @@ -3,7 +3,7 @@ with lib; let - version = "1.6.4"; + version = "1.7.1"; cfg = config.services.kubernetes.addons.dns; ports = { dns = 10053; @@ -55,9 +55,9 @@ in { type = types.attrs; default = { imageName = "coredns/coredns"; - imageDigest = "sha256:493ee88e1a92abebac67cbd4b5658b4730e0f33512461442d8d9214ea6734a9b"; + imageDigest = "sha256:4a6e0769130686518325b21b0c1d0688b54e7c79244d48e1b15634e98e40c6ef"; finalImageTag = version; - sha256 = "0fm9zdjavpf5hni8g7fkdd3csjbhd7n7py7llxjc66sbii087028"; + sha256 = "02r440xcdsgi137k5lmmvp0z5w5fmk8g9mysq5pnysq1wl8sj6mw"; }; }; }; @@ -156,7 +156,6 @@ in { health :${toString ports.health} kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa { pods insecure - upstream fallthrough in-addr.arpa ip6.arpa } prometheus :${toString ports.metrics} diff --git a/nixos/modules/services/cluster/kubernetes/apiserver.nix b/nixos/modules/services/cluster/kubernetes/apiserver.nix index 95bdb4c0d14e4..a5b1321547668 100644 --- a/nixos/modules/services/cluster/kubernetes/apiserver.nix +++ b/nixos/modules/services/cluster/kubernetes/apiserver.nix @@ -238,14 +238,40 @@ in type = int; }; + apiAudiences = mkOption { + description = '' + Kubernetes apiserver ServiceAccount issuer. + ''; + default = "api,https://kubernetes.default.svc"; + type = str; + }; + + serviceAccountIssuer = mkOption { + description = '' + Kubernetes apiserver ServiceAccount issuer. + ''; + default = "https://kubernetes.default.svc"; + type = str; + }; + + serviceAccountSigningKeyFile = mkOption { + description = '' + Path to the file that contains the current private key of the service + account token issuer. The issuer will sign issued ID tokens with this + private key. + ''; + type = path; + }; + serviceAccountKeyFile = mkOption { description = '' - Kubernetes apiserver PEM-encoded x509 RSA private or public key file, - used to verify ServiceAccount tokens. By default tls private key file - is used. + File containing PEM-encoded x509 RSA or ECDSA private or public keys, + used to verify ServiceAccount tokens. The specified file can contain + multiple keys, and the flag can be specified multiple times with + different files. If unspecified, --tls-private-key-file is used. + Must be specified when --service-account-signing-key is provided ''; - default = null; - type = nullOr path; + type = path; }; serviceClusterIpRange = mkOption { @@ -357,8 +383,10 @@ in ${optionalString (cfg.runtimeConfig != "") "--runtime-config=${cfg.runtimeConfig}"} \ --secure-port=${toString cfg.securePort} \ - ${optionalString (cfg.serviceAccountKeyFile!=null) - "--service-account-key-file=${cfg.serviceAccountKeyFile}"} \ + --api-audiences=${toString cfg.apiAudiences} \ + --service-account-issuer=${toString cfg.serviceAccountIssuer} \ + --service-account-signing-key-file=${cfg.serviceAccountSigningKeyFile} \ + --service-account-key-file=${cfg.serviceAccountKeyFile} \ --service-cluster-ip-range=${cfg.serviceClusterIpRange} \ --storage-backend=${cfg.storageBackend} \ ${optionalString (cfg.tlsCertFile != null) diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix index 3a11a6513a491..19edc338bba1b 100644 --- a/nixos/modules/services/cluster/kubernetes/default.nix +++ b/nixos/modules/services/cluster/kubernetes/default.nix @@ -5,6 +5,29 @@ with lib; let cfg = config.services.kubernetes; + defaultContainerdConfigFile = pkgs.writeText "containerd.toml" '' + version = 2 + root = "/var/lib/containerd/daemon" + state = "/var/run/containerd/daemon" + oom_score = 0 + + [grpc] + address = "/var/run/containerd/containerd.sock" + + [plugins."io.containerd.grpc.v1.cri"] + sandbox_image = "pause:latest" + + [plugins."io.containerd.grpc.v1.cri".cni] + bin_dir = "/opt/cni/bin" + max_conf_num = 0 + + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] + runtime_type = "io.containerd.runc.v2" + + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes."io.containerd.runc.v2".options] + SystemdCgroup = true + ''; + mkKubeConfig = name: conf: pkgs.writeText "${name}-kubeconfig" (builtins.toJSON { apiVersion = "v1"; kind = "Config"; @@ -222,14 +245,9 @@ in { }) (mkIf cfg.kubelet.enable { - virtualisation.docker = { + virtualisation.containerd = { enable = mkDefault true; - - # kubernetes needs access to logs - logDriver = mkDefault "json-file"; - - # iptables must be disabled for kubernetes - extraOptions = "--iptables=false --ip-masq=false"; + configFile = mkDefault defaultContainerdConfigFile; }; }) @@ -269,7 +287,6 @@ in { users.users.kubernetes = { uid = config.ids.uids.kubernetes; description = "Kubernetes user"; - extraGroups = [ "docker" ]; group = "kubernetes"; home = cfg.dataDir; createHome = true; diff --git a/nixos/modules/services/cluster/kubernetes/flannel.nix b/nixos/modules/services/cluster/kubernetes/flannel.nix index 548ffed1ddb58..3f55719027f05 100644 --- a/nixos/modules/services/cluster/kubernetes/flannel.nix +++ b/nixos/modules/services/cluster/kubernetes/flannel.nix @@ -8,16 +8,6 @@ let # we want flannel to use kubernetes itself as configuration backend, not direct etcd storageBackend = "kubernetes"; - - # needed for flannel to pass options to docker - mkDockerOpts = pkgs.runCommand "mk-docker-opts" { - buildInputs = [ pkgs.makeWrapper ]; - } '' - mkdir -p $out - - # bashInteractive needed for `compgen` - makeWrapper ${pkgs.bashInteractive}/bin/bash $out/mk-docker-opts --add-flags "${pkgs.kubernetes}/bin/mk-docker-opts.sh" - ''; in { ###### interface @@ -43,43 +33,17 @@ in cniVersion = "0.3.1"; delegate = { isDefaultGateway = true; - bridge = "docker0"; + bridge = "mynet"; }; }]; }; - systemd.services.mk-docker-opts = { - description = "Pre-Docker Actions"; - path = with pkgs; [ gawk gnugrep ]; - script = '' - ${mkDockerOpts}/mk-docker-opts -d /run/flannel/docker - systemctl restart docker - ''; - serviceConfig.Type = "oneshot"; - }; - - systemd.paths.flannel-subnet-env = { - wantedBy = [ "flannel.service" ]; - pathConfig = { - PathModified = "/run/flannel/subnet.env"; - Unit = "mk-docker-opts.service"; - }; - }; - - systemd.services.docker = { - environment.DOCKER_OPTS = "-b none"; - serviceConfig.EnvironmentFile = "-/run/flannel/docker"; - }; - - # read environment variables generated by mk-docker-opts - virtualisation.docker.extraOptions = "$DOCKER_OPTS"; - networking = { firewall.allowedUDPPorts = [ 8285 # flannel udp 8472 # flannel vxlan ]; - dhcpcd.denyInterfaces = [ "docker*" "flannel*" ]; + dhcpcd.denyInterfaces = [ "mynet*" "flannel*" ]; }; services.kubernetes.pki.certs = { diff --git a/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixos/modules/services/cluster/kubernetes/kubelet.nix index 479027f1b2708..ef6da26a024c6 100644 --- a/nixos/modules/services/cluster/kubernetes/kubelet.nix +++ b/nixos/modules/services/cluster/kubernetes/kubelet.nix @@ -23,7 +23,7 @@ let name = "pause"; tag = "latest"; contents = top.package.pause; - config.Cmd = "/bin/pause"; + config.Cmd = ["/bin/pause"]; }; kubeconfig = top.lib.mkKubeConfig "kubelet" cfg.kubeconfig; @@ -125,6 +125,18 @@ in }; }; + containerRuntime = mkOption { + description = "Which container runtime type to use"; + type = enum ["docker" "remote"]; + default = "remote"; + }; + + containerRuntimeEndpoint = mkOption { + description = "Endpoint at which to find the container runtime api interface/socket"; + type = str; + default = "unix:///var/run/containerd/containerd.sock"; + }; + enable = mkEnableOption "Kubernetes kubelet."; extraOpts = mkOption { @@ -235,16 +247,24 @@ in ###### implementation config = mkMerge [ (mkIf cfg.enable { + + environment.etc."cni/net.d".source = cniConfig; + services.kubernetes.kubelet.seedDockerImages = [infraContainer]; + boot.kernel.sysctl = { + "net.bridge.bridge-nf-call-iptables" = 1; + "net.ipv4.ip_forward" = 1; + "net.bridge.bridge-nf-call-ip6tables" = 1; + }; + systemd.services.kubelet = { description = "Kubernetes Kubelet Service"; wantedBy = [ "kubernetes.target" ]; - after = [ "network.target" "docker.service" "kube-apiserver.service" ]; + after = [ "containerd.service" "network.target" "kube-apiserver.service" ]; path = with pkgs; [ gitMinimal openssh - docker util-linux iproute ethtool @@ -254,8 +274,12 @@ in ] ++ lib.optional config.boot.zfs.enabled config.boot.zfs.package ++ top.path; preStart = '' ${concatMapStrings (img: '' - echo "Seeding docker image: ${img}" - docker load <${img} + echo "Seeding container image: ${img}" + ${if (lib.hasSuffix "gz" img) then + ''${pkgs.gzip}/bin/zcat "${img}" | ${pkgs.containerd}/bin/ctr -n k8s.io image import -'' + else + ''${pkgs.coreutils}/bin/cat "${img}" | ${pkgs.containerd}/bin/ctr -n k8s.io image import -'' + } '') cfg.seedDockerImages} rm /opt/cni/bin/* || true @@ -306,6 +330,9 @@ in ${optionalString (cfg.tlsKeyFile != null) "--tls-private-key-file=${cfg.tlsKeyFile}"} \ ${optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \ + --container-runtime=${cfg.containerRuntime} \ + --container-runtime-endpoint=${cfg.containerRuntimeEndpoint} \ + --cgroup-driver=systemd \ ${cfg.extraOpts} ''; WorkingDirectory = top.dataDir; @@ -315,7 +342,7 @@ in # Allways include cni plugins services.kubernetes.kubelet.cni.packages = [pkgs.cni-plugins]; - boot.kernelModules = ["br_netfilter"]; + boot.kernelModules = ["br_netfilter" "overlay"]; services.kubernetes.kubelet.hostname = with config.networking; mkDefault (hostName + optionalString (domain != null) ".${domain}"); diff --git a/nixos/modules/services/cluster/kubernetes/pki.nix b/nixos/modules/services/cluster/kubernetes/pki.nix index 933ae481e9684..8de6a3ba0d80f 100644 --- a/nixos/modules/services/cluster/kubernetes/pki.nix +++ b/nixos/modules/services/cluster/kubernetes/pki.nix @@ -361,6 +361,7 @@ in tlsCertFile = mkDefault cert; tlsKeyFile = mkDefault key; serviceAccountKeyFile = mkDefault cfg.certs.serviceAccount.cert; + serviceAccountSigningKeyFile = mkDefault cfg.certs.serviceAccount.key; kubeletClientCaFile = mkDefault caCert; kubeletClientCertFile = mkDefault cfg.certs.apiserverKubeletClient.cert; kubeletClientKeyFile = mkDefault cfg.certs.apiserverKubeletClient.key; diff --git a/nixos/modules/services/desktops/pipewire/alsa-monitor.conf.json b/nixos/modules/services/desktops/pipewire/alsa-monitor.conf.json new file mode 100644 index 0000000000000..53fc9cc96343b --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/alsa-monitor.conf.json @@ -0,0 +1,34 @@ +{ + "properties": {}, + "rules": [ + { + "matches": [ + { + "device.name": "~alsa_card.*" + } + ], + "actions": { + "update-props": { + "api.alsa.use-acp": true, + "api.acp.auto-profile": false, + "api.acp.auto-port": false + } + } + }, + { + "matches": [ + { + "node.name": "~alsa_input.*" + }, + { + "node.name": "~alsa_output.*" + } + ], + "actions": { + "update-props": { + "node.pause-on-idle": false + } + } + } + ] +} diff --git a/nixos/modules/services/desktops/pipewire/bluez-monitor.conf.json b/nixos/modules/services/desktops/pipewire/bluez-monitor.conf.json new file mode 100644 index 0000000000000..4d50cb9f1adb8 --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/bluez-monitor.conf.json @@ -0,0 +1,30 @@ +{ + "properties": {}, + "rules": [ + { + "matches": [ + { + "device.name": "~bluez_card.*" + } + ], + "actions": { + "update-props": {} + } + }, + { + "matches": [ + { + "node.name": "~bluez_input.*" + }, + { + "node.name": "~bluez_output.*" + } + ], + "actions": { + "update-props": { + "node.pause-on-idle": false + } + } + } + ] +} diff --git a/nixos/modules/services/desktops/pipewire/client-rt.conf.json b/nixos/modules/services/desktops/pipewire/client-rt.conf.json new file mode 100644 index 0000000000000..d294927b4f642 --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/client-rt.conf.json @@ -0,0 +1,26 @@ +{ + "context.properties": { + "log.level": 0 + }, + "context.spa-libs": { + "audio.convert.*": "audioconvert/libspa-audioconvert", + "support.*": "support/libspa-support" + }, + "context.modules": { + "libpipewire-module-rtkit": { + "args": {}, + "flags": [ + "ifexists", + "nofail" + ] + }, + "libpipewire-module-protocol-native": null, + "libpipewire-module-client-node": null, + "libpipewire-module-client-device": null, + "libpipewire-module-adapter": null, + "libpipewire-module-metadata": null, + "libpipewire-module-session-manager": null + }, + "filter.properties": {}, + "stream.properties": {} +} diff --git a/nixos/modules/services/desktops/pipewire/client.conf.json b/nixos/modules/services/desktops/pipewire/client.conf.json new file mode 100644 index 0000000000000..224938abbbcf7 --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/client.conf.json @@ -0,0 +1,19 @@ +{ + "context.properties": { + "log.level": 0 + }, + "context.spa-libs": { + "audio.convert.*": "audioconvert/libspa-audioconvert", + "support.*": "support/libspa-support" + }, + "context.modules": { + "libpipewire-module-protocol-native": null, + "libpipewire-module-client-node": null, + "libpipewire-module-client-device": null, + "libpipewire-module-adapter": null, + "libpipewire-module-metadata": null, + "libpipewire-module-session-manager": null + }, + "filter.properties": {}, + "stream.properties": {} +} diff --git a/nixos/modules/services/desktops/pipewire/jack.conf.json b/nixos/modules/services/desktops/pipewire/jack.conf.json new file mode 100644 index 0000000000000..2de04036b312d --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/jack.conf.json @@ -0,0 +1,21 @@ +{ + "context.properties": { + "log.level": 0 + }, + "context.spa-libs": { + "support.*": "support/libspa-support" + }, + "context.modules": { + "libpipewire-module-rtkit": { + "args": {}, + "flags": [ + "ifexists", + "nofail" + ] + }, + "libpipewire-module-protocol-native": null, + "libpipewire-module-client-node": null, + "libpipewire-module-metadata": null + }, + "jack.properties": {} +} diff --git a/nixos/modules/services/desktops/pipewire/media-session.conf.json b/nixos/modules/services/desktops/pipewire/media-session.conf.json new file mode 100644 index 0000000000000..4b2505ff8164c --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/media-session.conf.json @@ -0,0 +1,53 @@ +{ + "context.properties": {}, + "context.spa-libs": { + "api.bluez5.*": "bluez5/libspa-bluez5", + "api.alsa.*": "alsa/libspa-alsa", + "api.v4l2.*": "v4l2/libspa-v4l2", + "api.libcamera.*": "libcamera/libspa-libcamera" + }, + "context.modules": { + "libpipewire-module-rtkit": { + "args": {}, + "flags": [ + "ifexists", + "nofail" + ] + }, + "libpipewire-module-protocol-native": null, + "libpipewire-module-client-node": null, + "libpipewire-module-client-device": null, + "libpipewire-module-adapter": null, + "libpipewire-module-metadata": null, + "libpipewire-module-session-manager": null + }, + "session.modules": { + "default": [ + "flatpak", + "portal", + "v4l2", + "suspend-node", + "policy-node" + ], + "with-audio": [ + "metadata", + "default-nodes", + "default-profile", + "default-routes", + "alsa-seq", + "alsa-monitor" + ], + "with-alsa": [ + "with-audio" + ], + "with-jack": [ + "with-audio" + ], + "with-pulseaudio": [ + "with-audio", + "bluez5", + "restore-stream", + "streams-follow-default" + ] + } +} diff --git a/nixos/modules/services/desktops/pipewire/pipewire-media-session.nix b/nixos/modules/services/desktops/pipewire/pipewire-media-session.nix index 81f4762e1e601..b41ea349fb8db 100644 --- a/nixos/modules/services/desktops/pipewire/pipewire-media-session.nix +++ b/nixos/modules/services/desktops/pipewire/pipewire-media-session.nix @@ -9,18 +9,36 @@ let && pkgs.stdenv.isx86_64 && pkgs.pkgsi686Linux.pipewire != null; + prioritizeNativeProtocol = { + "context.modules" = { + "libpipewire-module-protocol-native" = { + _priority = -100; + _content = null; + }; + }; + }; + + # Use upstream config files passed through spa-json-dump as the base + # Patched here as necessary for them to work with this module + defaults = { + alsa-monitor = (builtins.fromJSON (builtins.readFile ./alsa-monitor.conf.json)); + bluez-monitor = (builtins.fromJSON (builtins.readFile ./bluez-monitor.conf.json)); + media-session = recursiveUpdate (builtins.fromJSON (builtins.readFile ./media-session.conf.json)) prioritizeNativeProtocol; + v4l2-monitor = (builtins.fromJSON (builtins.readFile ./v4l2-monitor.conf.json)); + }; # Helpers for generating the pipewire JSON config file mkSPAValueString = v: if builtins.isList v then "[${lib.concatMapStringsSep " " mkSPAValueString v}]" else if lib.types.attrs.check v then "{${lib.concatStringsSep " " (mkSPAKeyValue v)}}" + else if builtins.isString v then "\"${lib.generators.mkValueStringDefault { } v}\"" else lib.generators.mkValueStringDefault { } v; mkSPAKeyValue = attrs: map (def: def.content) ( lib.sortProperties ( lib.mapAttrsToList - (k: v: lib.mkOrder (v._priority or 1000) "${lib.escape [ "=" ] k} = ${mkSPAValueString (v._content or v)}") + (k: v: lib.mkOrder (v._priority or 1000) "${lib.escape [ "=" ":" ] k} = ${mkSPAValueString (v._content or v)}") attrs ) ); @@ -51,272 +69,41 @@ in { ''; }; - config = mkOption { - type = types.attrs; - description = '' - Configuration for the media session core. - ''; - default = { - # media-session config file - properties = { - # Properties to configure the session and some - # modules - #mem.mlock-all = false; - #context.profile.modules = "default,rtkit"; - }; - - spa-libs = { - # Mapping from factory name to library. - "api.bluez5.*" = "bluez5/libspa-bluez5"; - "api.alsa.*" = "alsa/libspa-alsa"; - "api.v4l2.*" = "v4l2/libspa-v4l2"; - "api.libcamera.*" = "libcamera/libspa-libcamera"; - }; - - modules = { - # These are the modules that are enabled when a file with - # the key name is found in the media-session.d config directory. - # the default bundle is always enabled. - - default = [ - "flatpak" # manages flatpak access - "portal" # manage portal permissions - "v4l2" # video for linux udev detection - #"libcamera" # libcamera udev detection - "suspend-node" # suspend inactive nodes - "policy-node" # configure and link nodes - #"metadata" # export metadata API - #"default-nodes" # restore default nodes - #"default-profile" # restore default profiles - #"default-routes" # restore default route - #"streams-follow-default" # move streams when default changes - #"alsa-seq" # alsa seq midi support - #"alsa-monitor" # alsa udev detection - #"bluez5" # bluetooth support - #"restore-stream" # restore stream settings - ]; - "with-audio" = [ - "metadata" - "default-nodes" - "default-profile" - "default-routes" - "alsa-seq" - "alsa-monitor" - ]; - "with-alsa" = [ - "with-audio" - ]; - "with-jack" = [ - "with-audio" - ]; - "with-pulseaudio" = [ - "with-audio" - "bluez5" - "restore-stream" - "streams-follow-default" - ]; - }; + config = { + media-session = mkOption { + type = types.attrs; + description = '' + Configuration for the media session core. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/media-session.d/media-session.conf + ''; + default = {}; }; - }; - - alsaMonitorConfig = mkOption { - type = types.attrs; - description = '' - Configuration for the alsa monitor. - ''; - default = { - # alsa-monitor config file - properties = { - #alsa.jack-device = true - }; - rules = [ - # an array of matches/actions to evaluate - { - # rules for matching a device or node. It is an array of - # properties that all need to match the regexp. If any of the - # matches work, the actions are executed for the object. - matches = [ - { - # this matches all cards - device.name = "~alsa_card.*"; - } - ]; - actions = { - # actions can update properties on the matched object. - update-props = { - api.alsa.use-acp = true; - #api.alsa.use-ucm = true; - #api.alsa.soft-mixer = false; - #api.alsa.ignore-dB = false; - #device.profile-set = "profileset-name"; - #device.profile = "default profile name"; - api.acp.auto-profile = false; - api.acp.auto-port = false; - #device.nick = "My Device"; - }; - }; - } - { - matches = [ - { - # matches all sinks - node.name = "~alsa_input.*"; - } - { - # matches all sources - node.name = "~alsa_output.*"; - } - ]; - actions = { - update-props = { - #node.nick = "My Node"; - #node.nick = null; - #priority.driver = 100; - #priority.session = 100; - #node.pause-on-idle = false; - #resample.quality = 4; - #channelmix.normalize = false; - #channelmix.mix-lfe = false; - #audio.channels = 2; - #audio.format = "S16LE"; - #audio.rate = 44100; - #audio.position = "FL,FR"; - #api.alsa.period-size = 1024; - #api.alsa.headroom = 0; - #api.alsa.disable-mmap = false; - #api.alsa.disable-batch = false; - }; - }; - } - ]; + alsa-monitor = mkOption { + type = types.attrs; + description = '' + Configuration for the alsa monitor. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/media-session.d/alsa-monitor.conf + ''; + default = {}; }; - }; - - bluezMonitorConfig = mkOption { - type = types.attrs; - description = '' - Configuration for the bluez5 monitor. - ''; - default = { - # bluez-monitor config file - properties = { - # msbc is not expected to work on all headset + adapter combinations. - #bluez5.msbc-support = true; - #bluez5.sbc-xq-support = true; - # Enabled headset roles (default: [ hsp_hs hfp_ag ]), this - # property only applies to native backend. Currently some headsets - # (Sony WH-1000XM3) are not working with both hsp_ag and hfp_ag - # enabled, disable either hsp_ag or hfp_ag to work around it. - # - # Supported headset roles: hsp_hs (HSP Headset), - # hsp_ag (HSP Audio Gateway), - # hfp_ag (HFP Audio Gateway) - #bluez5.headset-roles = [ "hsp_hs" "hsp_ag" "hfp_ag" ]; - - # Enabled A2DP codecs (default: all) - #bluez5.codecs = [ "sbc" "aac" "ldac" "aptx" "aptx_hd" ]; - }; - - rules = [ - # an array of matches/actions to evaluate - { - # rules for matching a device or node. It is an array of - # properties that all need to match the regexp. If any of the - # matches work, the actions are executed for the object. - matches = [ - { - # this matches all cards - device.name = "~bluez_card.*"; - } - ]; - actions = { - # actions can update properties on the matched object. - update-props = { - #device.nick = "My Device"; - }; - }; - } - { - matches = [ - { - # matches all sinks - node.name = "~bluez_input.*"; - } - { - # matches all sources - node.name = "~bluez_output.*"; - } - ]; - actions = { - update-props = { - #node.nick = "My Node" - #node.nick = null; - #priority.driver = 100; - #priority.session = 100; - #node.pause-on-idle = false; - #resample.quality = 4; - #channelmix.normalize = false; - #channelmix.mix-lfe = false; - }; - }; - } - ]; + bluez-monitor = mkOption { + type = types.attrs; + description = '' + Configuration for the bluez5 monitor. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/media-session.d/bluez-monitor.conf + ''; + default = {}; }; - }; - - v4l2MonitorConfig = mkOption { - type = types.attrs; - description = '' - Configuration for the V4L2 monitor. - ''; - default = { - # v4l2-monitor config file - properties = { - }; - rules = [ - # an array of matches/actions to evaluate - { - # rules for matching a device or node. It is an array of - # properties that all need to match the regexp. If any of the - # matches work, the actions are executed for the object. - matches = [ - { - # this matches all devices - device.name = "~v4l2_device.*"; - } - ]; - actions = { - # actions can update properties on the matched object. - update-props = { - #device.nick = "My Device"; - }; - }; - } - { - matches = [ - { - # matches all sinks - node.name = "~v4l2_input.*"; - } - { - # matches all sources - node.name = "~v4l2_output.*"; - } - ]; - actions = { - update-props = { - #node.nick = "My Node"; - #node.nick = null; - #priority.driver = 100; - #priority.session = 100; - #node.pause-on-idle = true; - }; - }; - } - ]; + v4l2-monitor = mkOption { + type = types.attrs; + description = '' + Configuration for the V4L2 monitor. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/media-session.d/v4l2-monitor.conf + ''; + default = {}; }; }; }; @@ -325,16 +112,17 @@ in { ###### implementation config = mkIf cfg.enable { environment.systemPackages = [ cfg.package ]; - services.pipewire.sessionManagerExecutable = "${cfg.package}/bin/pipewire-media-session"; + systemd.packages = [ cfg.package ]; + systemd.user.services.pipewire-media-session.wantedBy = [ "pipewire.service" ]; - environment.etc."pipewire/media-session.d/media-session.conf" = { text = toSPAJSON cfg.config; }; - environment.etc."pipewire/media-session.d/v4l2-monitor.conf" = { text = toSPAJSON cfg.v4l2MonitorConfig; }; + environment.etc."pipewire/media-session.d/media-session.conf" = { text = toSPAJSON (recursiveUpdate defaults.media-session cfg.config.media-session); }; + environment.etc."pipewire/media-session.d/v4l2-monitor.conf" = { text = toSPAJSON (recursiveUpdate defaults.v4l2-monitor cfg.config.v4l2-monitor); }; environment.etc."pipewire/media-session.d/with-alsa" = mkIf config.services.pipewire.alsa.enable { text = ""; }; - environment.etc."pipewire/media-session.d/alsa-monitor.conf" = mkIf config.services.pipewire.alsa.enable { text = toSPAJSON cfg.alsaMonitorConfig; }; + environment.etc."pipewire/media-session.d/alsa-monitor.conf" = mkIf config.services.pipewire.alsa.enable { text = toSPAJSON (recursiveUpdate defaults.alsa-monitor cfg.config.alsa-monitor); }; environment.etc."pipewire/media-session.d/with-pulseaudio" = mkIf config.services.pipewire.pulse.enable { text = ""; }; - environment.etc."pipewire/media-session.d/bluez-monitor.conf" = mkIf config.services.pipewire.pulse.enable { text = toSPAJSON cfg.bluezMonitorConfig; }; + environment.etc."pipewire/media-session.d/bluez-monitor.conf" = mkIf config.services.pipewire.pulse.enable { text = toSPAJSON (recursiveUpdate defaults.bluez-monitor cfg.config.bluez-monitor); }; environment.etc."pipewire/media-session.d/with-jack" = mkIf config.services.pipewire.jack.enable { text = ""; }; }; diff --git a/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json b/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json new file mode 100644 index 0000000000000..da08bcea2c90b --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json @@ -0,0 +1,28 @@ +{ + "context.properties": {}, + "context.spa-libs": { + "audio.convert.*": "audioconvert/libspa-audioconvert", + "support.*": "support/libspa-support" + }, + "context.modules": { + "libpipewire-module-rtkit": { + "args": {}, + "flags": [ + "ifexists", + "nofail" + ] + }, + "libpipewire-module-protocol-native": null, + "libpipewire-module-client-node": null, + "libpipewire-module-adapter": null, + "libpipewire-module-metadata": null, + "libpipewire-module-protocol-pulse": { + "args": { + "server.address": [ + "unix:native" + ] + } + } + }, + "stream.properties": {} +} diff --git a/nixos/modules/services/desktops/pipewire/pipewire.conf.json b/nixos/modules/services/desktops/pipewire/pipewire.conf.json new file mode 100644 index 0000000000000..59e2afca09396 --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/pipewire.conf.json @@ -0,0 +1,55 @@ +{ + "context.properties": { + "link.max-buffers": 16, + "core.daemon": true, + "core.name": "pipewire-0" + }, + "context.spa-libs": { + "audio.convert.*": "audioconvert/libspa-audioconvert", + "api.alsa.*": "alsa/libspa-alsa", + "api.v4l2.*": "v4l2/libspa-v4l2", + "api.libcamera.*": "libcamera/libspa-libcamera", + "api.bluez5.*": "bluez5/libspa-bluez5", + "api.vulkan.*": "vulkan/libspa-vulkan", + "api.jack.*": "jack/libspa-jack", + "support.*": "support/libspa-support" + }, + "context.modules": { + "libpipewire-module-rtkit": { + "args": {}, + "flags": [ + "ifexists", + "nofail" + ] + }, + "libpipewire-module-protocol-native": null, + "libpipewire-module-profiler": null, + "libpipewire-module-metadata": null, + "libpipewire-module-spa-device-factory": null, + "libpipewire-module-spa-node-factory": null, + "libpipewire-module-client-node": null, + "libpipewire-module-client-device": null, + "libpipewire-module-portal": { + "flags": [ + "ifexists", + "nofail" + ] + }, + "libpipewire-module-access": { + "args": {} + }, + "libpipewire-module-adapter": null, + "libpipewire-module-link-factory": null, + "libpipewire-module-session-manager": null + }, + "context.objects": { + "spa-node-factory": { + "args": { + "factory.name": "support.node.driver", + "node.name": "Dummy-Driver", + "priority.driver": 8000 + } + } + }, + "context.exec": {} +} diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix index 044120de7c719..2577e77c4a1ff 100644 --- a/nixos/modules/services/desktops/pipewire/pipewire.nix +++ b/nixos/modules/services/desktops/pipewire/pipewire.nix @@ -18,11 +18,53 @@ let ln -s "${cfg.package.jack}/lib" "$out/lib/pipewire" ''; + prioritizeNativeProtocol = { + "context.modules" = { + # Most other modules depend on this, so put it first + "libpipewire-module-protocol-native" = { + _priority = -100; + _content = null; + }; + }; + }; + + fixDaemonModulePriorities = { + "context.modules" = { + # Most other modules depend on thism so put it first + "libpipewire-module-protocol-native" = { + _priority = -100; + _content = null; + }; + # Needs to be before libpipewire-module-access + "libpipewire-module-portal" = { + _priority = -50; + _content = { + flags = [ + "ifexists" + "nofail" + ]; + }; + }; + }; + }; + + # Use upstream config files passed through spa-json-dump as the base + # Patched here as necessary for them to work with this module + defaults = { + client = recursiveUpdate (builtins.fromJSON (builtins.readFile ./client.conf.json)) prioritizeNativeProtocol; + client-rt = recursiveUpdate (builtins.fromJSON (builtins.readFile ./client-rt.conf.json)) prioritizeNativeProtocol; + jack = recursiveUpdate (builtins.fromJSON (builtins.readFile ./jack.conf.json)) prioritizeNativeProtocol; + # Remove session manager invocation from the upstream generated file, it points to the wrong path + pipewire = recursiveUpdate (builtins.fromJSON (builtins.readFile ./pipewire.conf.json)) fixDaemonModulePriorities; + pipewire-pulse = recursiveUpdate (builtins.fromJSON (builtins.readFile ./pipewire-pulse.conf.json)) prioritizeNativeProtocol; + }; + # Helpers for generating the pipewire JSON config file mkSPAValueString = v: if builtins.isList v then "[${lib.concatMapStringsSep " " mkSPAValueString v}]" else if lib.types.attrs.check v then "{${lib.concatStringsSep " " (mkSPAKeyValue v)}}" + else if builtins.isString v then "\"${lib.generators.mkValueStringDefault { } v}\"" else lib.generators.mkValueStringDefault { } v; mkSPAKeyValue = attrs: map (def: def.content) ( @@ -64,129 +106,51 @@ in { ''; }; - config = mkOption { - type = types.attrs; - description = '' - Configuration for the pipewire daemon. - ''; - default = { - properties = { - ## set-prop is used to configure properties in the system - # - # "library.name.system" = "support/libspa-support"; - # "context.data-loop.library.name.system" = "support/libspa-support"; - "link.max-buffers" = 16; # version < 3 clients can't handle more than 16 - #"mem.allow-mlock" = false; - #"mem.mlock-all" = true; - ## https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/src/pipewire/pipewire.h#L93 - #"log.level" = 2; # 5 is trace, which is verbose as hell, default is 2 which is warnings, 4 is debug output, 3 is info - - ## Properties for the DSP configuration - # - #"default.clock.rate" = 48000; - #"default.clock.quantum" = 1024; - #"default.clock.min-quantum" = 32; - #"default.clock.max-quantum" = 8192; - #"default.video.width" = 640; - #"default.video.height" = 480; - #"default.video.rate.num" = 25; - #"default.video.rate.denom" = 1; - }; - - spa-libs = { - ## add-spa-lib <factory-name regex> <library-name> - # - # used to find spa factory names. It maps an spa factory name - # regular expression to a library name that should contain - # that factory. - # - "audio.convert*" = "audioconvert/libspa-audioconvert"; - "api.alsa.*" = "alsa/libspa-alsa"; - "api.v4l2.*" = "v4l2/libspa-v4l2"; - "api.libcamera.*" = "libcamera/libspa-libcamera"; - "api.bluez5.*" = "bluez5/libspa-bluez5"; - "api.vulkan.*" = "vulkan/libspa-vulkan"; - "api.jack.*" = "jack/libspa-jack"; - "support.*" = "support/libspa-support"; - # "videotestsrc" = "videotestsrc/libspa-videotestsrc"; - # "audiotestsrc" = "audiotestsrc/libspa-audiotestsrc"; - }; - - modules = { - ## <module-name> = { [args = "<key>=<value> ..."] - # [flags = ifexists] } - # [flags = [ifexists]|[nofail]} - # - # Loads a module with the given parameters. - # If ifexists is given, the module is ignoed when it is not found. - # If nofail is given, module initialization failures are ignored. - # - libpipewire-module-rtkit = { - args = { - #rt.prio = 20; - #rt.time.soft = 200000; - #rt.time.hard = 200000; - #nice.level = -11; - }; - flags = "ifexists|nofail"; - }; - libpipewire-module-protocol-native = { _priority = -100; _content = "null"; }; - libpipewire-module-profiler = "null"; - libpipewire-module-metadata = "null"; - libpipewire-module-spa-device-factory = "null"; - libpipewire-module-spa-node-factory = "null"; - libpipewire-module-client-node = "null"; - libpipewire-module-client-device = "null"; - libpipewire-module-portal = "null"; - libpipewire-module-access = { - args.access = { - allowed = ["${builtins.unsafeDiscardStringContext cfg.sessionManagerExecutable}"]; - rejected = []; - restricted = []; - force = "flatpak"; - }; - }; - libpipewire-module-adapter = "null"; - libpipewire-module-link-factory = "null"; - libpipewire-module-session-manager = "null"; - }; - - objects = { - ## create-object [-nofail] <factory-name> [<key>=<value> ...] - # - # Creates an object from a PipeWire factory with the given parameters. - # If -nofail is given, errors are ignored (and no object is created) - # - }; + config = { + client = mkOption { + type = types.attrs; + default = {}; + description = '' + Configuration for pipewire clients. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/client.conf.in + ''; + }; + client-rt = mkOption { + type = types.attrs; + default = {}; + description = '' + Configuration for realtime pipewire clients. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/client-rt.conf.in + ''; + }; - exec = { - ## exec <program-name> - # - # Execute the given program. This is usually used to start the - # session manager. run the session manager with -h for options - # - "${builtins.unsafeDiscardStringContext cfg.sessionManagerExecutable}" = { args = "\"${lib.concatStringsSep " " cfg.sessionManagerArguments}\""; }; - }; + jack = mkOption { + type = types.attrs; + default = {}; + description = '' + Configuration for the pipewire daemon's jack module. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/jack.conf.in + ''; }; - }; - sessionManagerExecutable = mkOption { - type = types.str; - default = ""; - example = literalExample ''${pkgs.pipewire.mediaSession}/bin/pipewire-media-session''; - description = '' - Path to the session manager executable. - ''; - }; + pipewire = mkOption { + type = types.attrs; + default = {}; + description = '' + Configuration for the pipewire daemon. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/pipewire.conf.in + ''; + }; - sessionManagerArguments = mkOption { - type = types.listOf types.str; - default = []; - example = literalExample ''["-p" "bluez5.msbc-support=true"]''; - description = '' - Arguments passed to the pipewire session manager. - ''; + pipewire-pulse = mkOption { + type = types.attrs; + default = {}; + description = '' + Configuration for the pipewire-pulse daemon. For details see + https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/${cfg.package.version}/src/daemon/pipewire-pulse.conf.in + ''; + }; }; alsa = { @@ -253,13 +217,16 @@ in { source = "${cfg.package}/share/alsa/alsa.conf.d/99-pipewire-default.conf"; }; + environment.etc."pipewire/client.conf" = { text = toSPAJSON (recursiveUpdate defaults.client cfg.config.client); }; + environment.etc."pipewire/client-rt.conf" = { text = toSPAJSON (recursiveUpdate defaults.client-rt cfg.config.client-rt); }; + environment.etc."pipewire/jack.conf" = { text = toSPAJSON (recursiveUpdate defaults.jack cfg.config.jack); }; + environment.etc."pipewire/pipewire.conf" = { text = toSPAJSON (recursiveUpdate defaults.pipewire cfg.config.pipewire); }; + environment.etc."pipewire/pipewire-pulse.conf" = { text = toSPAJSON (recursiveUpdate defaults.pipewire-pulse cfg.config.pipewire-pulse); }; + environment.sessionVariables.LD_LIBRARY_PATH = lib.optional cfg.jack.enable "/run/current-system/sw/lib/pipewire"; # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/464#note_723554 - systemd.user.services.pipewire.environment = { - "PIPEWIRE_LINK_PASSIVE" = "1"; - "PIPEWIRE_CONFIG_FILE" = pkgs.writeText "pipewire.conf" (toSPAJSON cfg.config); - }; + systemd.user.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1"; }; } diff --git a/nixos/modules/services/desktops/pipewire/v4l2-monitor.conf.json b/nixos/modules/services/desktops/pipewire/v4l2-monitor.conf.json new file mode 100644 index 0000000000000..b08cba1b604b5 --- /dev/null +++ b/nixos/modules/services/desktops/pipewire/v4l2-monitor.conf.json @@ -0,0 +1,30 @@ +{ + "properties": {}, + "rules": [ + { + "matches": [ + { + "device.name": "~v4l2_device.*" + } + ], + "actions": { + "update-props": {} + } + }, + { + "matches": [ + { + "node.name": "~v4l2_input.*" + }, + { + "node.name": "~v4l2_output.*" + } + ], + "actions": { + "update-props": { + "node.pause-on-idle": false + } + } + } + ] +} diff --git a/nixos/modules/services/games/minetest-server.nix b/nixos/modules/services/games/minetest-server.nix index f52079fc1ef62..2111c970d4f27 100644 --- a/nixos/modules/services/games/minetest-server.nix +++ b/nixos/modules/services/games/minetest-server.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.minetest-server; - flag = val: name: if val != null then "--${name} ${val} " else ""; + flag = val: name: if val != null then "--${name} ${toString val} " else ""; flags = [ (flag cfg.gameId "gameid") (flag cfg.world "world") diff --git a/nixos/modules/services/misc/apache-kafka.nix b/nixos/modules/services/misc/apache-kafka.nix index f3a650a260f1e..69dfadfe54e0d 100644 --- a/nixos/modules/services/misc/apache-kafka.nix +++ b/nixos/modules/services/misc/apache-kafka.nix @@ -90,19 +90,7 @@ in { jvmOptions = mkOption { description = "Extra command line options for the JVM running Kafka."; - default = [ - "-server" - "-Xmx1G" - "-Xms1G" - "-XX:+UseCompressedOops" - "-XX:+UseParNewGC" - "-XX:+UseConcMarkSweepGC" - "-XX:+CMSClassUnloadingEnabled" - "-XX:+CMSScavengeBeforeRemark" - "-XX:+DisableExplicitGC" - "-Djava.awt.headless=true" - "-Djava.net.preferIPv4Stack=true" - ]; + default = []; type = types.listOf types.str; example = [ "-Djava.net.preferIPv4Stack=true" @@ -118,6 +106,13 @@ in { type = types.package; }; + jre = mkOption { + description = "The JRE with which to run Kafka"; + default = cfg.package.passthru.jre; + defaultText = "pkgs.apacheKafka.passthru.jre"; + type = types.package; + }; + }; config = mkIf cfg.enable { @@ -138,7 +133,7 @@ in { after = [ "network.target" ]; serviceConfig = { ExecStart = '' - ${pkgs.jre}/bin/java \ + ${cfg.jre}/bin/java \ -cp "${cfg.package}/libs/*" \ -Dlog4j.configuration=file:${logConfig} \ ${toString cfg.jvmOptions} \ diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix index 1f2e13f373257..f53c49a1ee6d1 100644 --- a/nixos/modules/services/misc/home-assistant.nix +++ b/nixos/modules/services/misc/home-assistant.nix @@ -183,8 +183,14 @@ in { }; package = mkOption { - default = pkgs.home-assistant; - defaultText = "pkgs.home-assistant"; + default = pkgs.home-assistant.overrideAttrs (oldAttrs: { + doInstallCheck = false; + }); + defaultText = literalExample '' + pkgs.home-assistant.overrideAttrs (oldAttrs: { + doInstallCheck = false; + }) + ''; type = types.package; example = literalExample '' pkgs.home-assistant.override { @@ -192,7 +198,7 @@ in { } ''; description = '' - Home Assistant package to use. + Home Assistant package to use. By default the tests are disabled, as they take a considerable amout of time to complete. Override <literal>extraPackages</literal> or <literal>extraComponents</literal> in order to add additional dependencies. If you specify <option>config</option> and do not set <option>autoExtraComponents</option> to <literal>false</literal>, overriding <literal>extraComponents</literal> will have no effect. diff --git a/nixos/modules/services/monitoring/alerta.nix b/nixos/modules/services/monitoring/alerta.nix index 34f2d41706a56..7c6eff713cb12 100644 --- a/nixos/modules/services/monitoring/alerta.nix +++ b/nixos/modules/services/monitoring/alerta.nix @@ -95,13 +95,13 @@ in ALERTA_SVR_CONF_FILE = alertaConf; }; serviceConfig = { - ExecStart = "${pkgs.python36Packages.alerta-server}/bin/alertad run --port ${toString cfg.port} --host ${cfg.bind}"; + ExecStart = "${pkgs.alerta-server}/bin/alertad run --port ${toString cfg.port} --host ${cfg.bind}"; User = "alerta"; Group = "alerta"; }; }; - environment.systemPackages = [ pkgs.python36Packages.alerta ]; + environment.systemPackages = [ pkgs.alerta ]; users.users.alerta = { uid = config.ids.uids.alerta; diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index 2c7653189454e..4c24784868539 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -22,6 +22,7 @@ let exporterOpts = genAttrs [ "apcupsd" + "artifactory" "bind" "bird" "blackbox" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix b/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix new file mode 100644 index 0000000000000..2adcecc728bde --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, options }: + +with lib; + +let + cfg = config.services.prometheus.exporters.artifactory; +in +{ + port = 9531; + extraOpts = { + scrapeUri = mkOption { + type = types.str; + default = "http://localhost:8081/artifactory"; + description = '' + URI on which to scrape JFrog Artifactory. + ''; + }; + + artiUsername = mkOption { + type = types.str; + description = '' + Username for authentication against JFrog Artifactory API. + ''; + }; + + artiPassword = mkOption { + type = types.str; + default = ""; + description = '' + Password for authentication against JFrog Artifactory API. + One of the password or access token needs to be set. + ''; + }; + + artiAccessToken = mkOption { + type = types.str; + default = ""; + description = '' + Access token for authentication against JFrog Artifactory API. + One of the password or access token needs to be set. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + ExecStart = '' + ${pkgs.prometheus-artifactory-exporter}/bin/artifactory_exporter \ + --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ + --artifactory.scrape-uri ${cfg.scrapeUri} \ + ${concatStringsSep " \\\n " cfg.extraFlags} + ''; + Environment = [ + "ARTI_USERNAME=${cfg.artiUsername}" + "ARTI_PASSWORD=${cfg.artiPassword}" + "ARTI_ACCESS_TOKEN=${cfg.artiAccessToken}" + ]; + }; + }; +} diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix index 4c040112d28d4..32a7eb3ed69e8 100644 --- a/nixos/modules/services/networking/flannel.nix +++ b/nixos/modules/services/networking/flannel.nix @@ -162,10 +162,7 @@ in { NODE_NAME = cfg.nodeName; }; path = [ pkgs.iptables ]; - preStart = '' - mkdir -p /run/flannel - touch /run/flannel/docker - '' + optionalString (cfg.storageBackend == "etcd") '' + preStart = optionalString (cfg.storageBackend == "etcd") '' echo "setting network configuration" until ${pkgs.etcdctl}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}' do @@ -177,6 +174,7 @@ in { ExecStart = "${cfg.package}/bin/flannel"; Restart = "always"; RestartSec = "10s"; + RuntimeDirectory = "flannel"; }; }; diff --git a/nixos/modules/services/networking/privoxy.nix b/nixos/modules/services/networking/privoxy.nix index 7caae3282032c..7c22b7d09b9bd 100644 --- a/nixos/modules/services/networking/privoxy.nix +++ b/nixos/modules/services/networking/privoxy.nix @@ -4,26 +4,46 @@ with lib; let - inherit (pkgs) privoxy; - cfg = config.services.privoxy; - confFile = pkgs.writeText "privoxy.conf" ('' - user-manual ${privoxy}/share/doc/privoxy/user-manual - confdir ${privoxy}/etc/ - listen-address ${cfg.listenAddress} - enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"} - ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles} - ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles} - '' + optionalString cfg.enableTor '' - forward-socks5t / 127.0.0.1:9063 . - toggle 1 - enable-remote-toggle 0 - enable-edit-actions 0 - enable-remote-http-toggle 0 - '' + '' - ${cfg.extraConfig} - ''); + serialise = name: val: + if isList val then concatMapStrings (serialise name) val + else if isBool val then serialise name (if val then "1" else "0") + else "${name} ${toString val}\n"; + + configType = with types; + let atom = oneOf [ int bool string path ]; + in attrsOf (either atom (listOf atom)) + // { description = '' + privoxy configuration type. The format consists of an attribute + set of settings. Each setting can be either a value (integer, string, + boolean or path) or a list of such values. + ''; + }; + + ageType = types.str // { + check = x: + isString x && + (builtins.match "([0-9]+([smhdw]|min|ms|us)*)+" x != null); + description = "tmpfiles.d(5) age format"; + }; + + configFile = pkgs.writeText "privoxy.conf" + (concatStrings ( + # Relative paths in some options are relative to confdir. Privoxy seems + # to parse the options in order of appearance, so this must come first. + # Nix however doesn't preserve the order in attrsets, so we have to + # hardcode confdir here. + [ "confdir ${pkgs.privoxy}/etc\n" ] + ++ mapAttrsToList serialise cfg.settings + )); + + inspectAction = pkgs.writeText "inspect-all-https.action" + '' + # Enable HTTPS inspection for all requests + {+https-inspection} + / + ''; in @@ -31,70 +51,144 @@ in ###### interface - options = { + options.services.privoxy = { - services.privoxy = { + enable = mkEnableOption "Privoxy, non-caching filtering proxy"; - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether to enable the Privoxy non-caching filtering proxy. - ''; - }; + enableTor = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure Privoxy to use Tor's faster SOCKS port, + suitable for HTTP. + ''; + }; - listenAddress = mkOption { - type = types.str; - default = "127.0.0.1:8118"; - description = '' - Address the proxy server is listening to. - ''; - }; + inspectHttps = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure Privoxy to inspect HTTPS requests, meaning all + encrypted traffic will be filtered as well. This works by decrypting + and re-encrypting the requests using a per-domain generated certificate. - actionsFiles = mkOption { - type = types.listOf types.str; - example = [ "match-all.action" "default.action" "/etc/privoxy/user.action" ]; - default = [ "match-all.action" "default.action" ]; - description = '' - List of paths to Privoxy action files. - These paths may either be absolute or relative to the privoxy configuration directory. - ''; - }; + To issue per-domain certificates, Privoxy must be provided with a CA + certificate, using the <literal>ca-cert-file</literal>, + <literal>ca-key-file</literal> settings. - filterFiles = mkOption { - type = types.listOf types.str; - example = [ "default.filter" "/etc/privoxy/user.filter" ]; - default = [ "default.filter" ]; - description = '' - List of paths to Privoxy filter files. - These paths may either be absolute or relative to the privoxy configuration directory. - ''; - }; + <warning><para> + The CA certificate must also be added to the system trust roots, + otherwise browsers will reject all Privoxy certificates as invalid. + You can do so by using the option + <option>security.pki.certificateFiles</option>. + </para></warning> + ''; + }; - enableEditActions = mkOption { - type = types.bool; - default = false; - description = '' - Whether or not the web-based actions file editor may be used. - ''; - }; + certsLifetime = mkOption { + type = ageType; + default = "10d"; + example = "12h"; + description = '' + If <literal>inspectHttps</literal> is enabled, the time generated HTTPS + certificates will be stored in a temporary directory for reuse. Once + the lifetime has expired the directory will cleared and the certificate + will have to be generated again, on-demand. - enableTor = mkOption { - type = types.bool; - default = false; - description = '' - Whether to configure Privoxy to use Tor's faster SOCKS port, - suitable for HTTP. - ''; - }; + Depending on the traffic, you may want to reduce the lifetime to limit + the disk usage, since Privoxy itself never deletes the certificates. - extraConfig = mkOption { - type = types.lines; - default = "" ; - description = '' - Extra configuration. Contents will be added verbatim to the configuration file. - ''; + <note><para>The format is that of the <literal>tmpfiles.d(5)</literal> + Age parameter.</para></note> + ''; + }; + + userActions = mkOption { + type = types.lines; + default = ""; + description = '' + Actions to be included in a <literal>user.action</literal> file. This + will have a higher priority and can be used to override all other + actions. + ''; + }; + + userFilters = mkOption { + type = types.lines; + default = ""; + description = '' + Filters to be included in a <literal>user.filter</literal> file. This + will have a higher priority and can be used to override all other + filters definitions. + ''; + }; + + settings = mkOption { + type = types.submodule { + freeformType = configType; + + options.listen-address = mkOption { + type = types.str; + default = "127.0.0.1:8118"; + description = "Pair of address:port the proxy server is listening to."; + }; + + options.enable-edit-actions = mkOption { + type = types.bool; + default = false; + description = "Whether the web-based actions file editor may be used."; + }; + + options.actionsfile = mkOption { + type = types.listOf types.str; + # This must come after all other entries, in order to override the + # other actions/filters installed by Privoxy or the user. + apply = x: x ++ optional (cfg.userActions != "") + (toString (pkgs.writeText "user.actions" cfg.userActions)); + default = [ "match-all.action" "default.action" ]; + description = '' + List of paths to Privoxy action files. These paths may either be + absolute or relative to the privoxy configuration directory. + ''; + }; + + options.filterfile = mkOption { + type = types.listOf types.str; + default = [ "default.filter" ]; + apply = x: x ++ optional (cfg.userFilters != "") + (toString (pkgs.writeText "user.filter" cfg.userFilters)); + description = '' + List of paths to Privoxy filter files. These paths may either be + absolute or relative to the privoxy configuration directory. + ''; + }; }; + default = {}; + example = literalExample '' + { # Listen on IPv6 only + listen-address = "[::]:8118"; + + # Forward .onion requests to Tor + forward-socks5 = ".onion localhost:9050 ."; + + # Log redirects and filters + debug = [ 128 64 ]; + # This is equivalent to writing these lines + # in the Privoxy configuration file: + # debug 128 + # debug 64 + } + ''; + description = '' + This option is mapped to the main Privoxy configuration file. + Check out the Privoxy user manual at + <link xlink:href="https://www.privoxy.org/user-manual/config.html"/> + for available settings and documentation. + + <note><para> + Repeated settings can be represented by using a list. + </para></note> + ''; }; }; @@ -104,23 +198,33 @@ in config = mkIf cfg.enable { users.users.privoxy = { + description = "Privoxy daemon user"; isSystemUser = true; - home = "/var/empty"; group = "privoxy"; }; users.groups.privoxy = {}; + systemd.tmpfiles.rules = optional cfg.inspectHttps + "d ${cfg.settings.certificate-directory} 0770 privoxy privoxy ${cfg.certsLifetime}"; + systemd.services.privoxy = { description = "Filtering web proxy"; after = [ "network.target" "nss-lookup.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}"; - - serviceConfig.PrivateDevices = true; - serviceConfig.PrivateTmp = true; - serviceConfig.ProtectHome = true; - serviceConfig.ProtectSystem = "full"; + serviceConfig = { + User = "privoxy"; + Group = "privoxy"; + ExecStart = "${pkgs.privoxy}/bin/privoxy --no-daemon ${configFile}"; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "full"; + }; + unitConfig = mkIf cfg.inspectHttps { + ConditionPathExists = with cfg.settings; + [ ca-cert-file ca-key-file ]; + }; }; services.tor.settings.SOCKSPort = mkIf cfg.enableTor [ @@ -128,8 +232,48 @@ in { addr = "127.0.0.1"; port = 9063; IsolateDestAddr = false; } ]; + services.privoxy.settings = { + user-manual = "${pkgs.privoxy}/share/doc/privoxy/user-manual"; + # This is needed for external filters + temporary-directory = "/tmp"; + filterfile = [ "default.filter" ]; + actionsfile = + [ "match-all.action" + "default.action" + ] ++ optional cfg.inspectHttps (toString inspectAction); + } // (optionalAttrs cfg.enableTor { + forward-socks5 = "127.0.0.1:9063 ."; + toggle = true; + enable-remote-toggle = false; + enable-edit-actions = false; + enable-remote-http-toggle = false; + }) // (optionalAttrs cfg.inspectHttps { + # This allows setting absolute key/crt paths + ca-directory = "/var/empty"; + certificate-directory = "/run/privoxy/certs"; + trusted-cas-file = "/etc/ssl/certs/ca-certificates.crt"; + }); + }; + imports = + let + top = x: [ "services" "privoxy" x ]; + setting = x: [ "services" "privoxy" "settings" x ]; + in + [ (mkRenamedOptionModule (top "enableEditActions") (setting "enable-edit-actions")) + (mkRenamedOptionModule (top "listenAddress") (setting "listen-address")) + (mkRenamedOptionModule (top "actionsFiles") (setting "actionsfile")) + (mkRenamedOptionModule (top "filterFiles") (setting "filterfile")) + (mkRemovedOptionModule (top "extraConfig") + '' + Use services.privoxy.settings instead. + This is part of the general move to use structured settings instead of raw + text for config as introduced by RFC0042: + https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md + '') + ]; + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; } diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 5636415f6a0d0..9a541aba6e43b 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -10,7 +10,7 @@ let extensions = { enabled, all }: (with all; enabled - ++ [ imagick ] # Always enabled + ++ optional (!cfg.disableImagemagick) imagick # Optionally enabled depending on caching settings ++ optional cfg.caching.apcu apcu ++ optional cfg.caching.redis redis @@ -303,6 +303,18 @@ in { }; }; + disableImagemagick = mkOption { + type = types.bool; + default = false; + description = '' + Whether to not load the ImageMagick module into PHP. + This is used by the theming app and for generating previews of certain images (e.g. SVG and HEIF). + You may want to disable it for increased security. In that case, previews will still be available + for some images (e.g. JPEG and PNG). + See https://github.com/nextcloud/server/issues/13099 + ''; + }; + caching = { apcu = mkOption { type = types.bool; diff --git a/nixos/modules/services/web-apps/whitebophir.nix b/nixos/modules/services/web-apps/whitebophir.nix index a19812547c448..b265296d5c1eb 100644 --- a/nixos/modules/services/web-apps/whitebophir.nix +++ b/nixos/modules/services/web-apps/whitebophir.nix @@ -16,6 +16,12 @@ in { description = "Whitebophir package to use."; }; + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Address to listen on (use 0.0.0.0 to allow access from any address)."; + }; + port = mkOption { type = types.port; default = 5001; @@ -30,7 +36,8 @@ in { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; environment = { - PORT = "${toString cfg.port}"; + PORT = toString cfg.port; + HOST = toString cfg.listenAddress; WBO_HISTORY_DIR = "/var/lib/whitebophir"; }; diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix index d39b4d64904fb..fc7f7bea4e444 100644 --- a/nixos/modules/services/x11/desktop-managers/xfce.nix +++ b/nixos/modules/services/x11/desktop-managers/xfce.nix @@ -58,7 +58,7 @@ in noDesktop = mkOption { type = types.bool; default = false; - description = "Don't install XFCE desktop components (xfdesktop, panel and notification daemon)."; + description = "Don't install XFCE desktop components (xfdesktop and panel)."; }; enableXfwm = mkOption { @@ -98,6 +98,7 @@ in parole ristretto xfce4-appfinder + xfce4-notifyd xfce4-screenshooter xfce4-session xfce4-settings @@ -119,7 +120,6 @@ in xfwm4 xfwm4-themes ] ++ optionals (!cfg.noDesktop) [ - xfce4-notifyd xfce4-panel xfdesktop ]; @@ -166,7 +166,8 @@ in # Systemd services systemd.packages = with pkgs.xfce; [ (thunar.override { thunarPlugins = cfg.thunarPlugins; }) - ] ++ optional (!cfg.noDesktop) xfce4-notifyd; + xfce4-notifyd + ]; }; } diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 8858559d8f27d..35bd4dabb6738 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -251,11 +251,10 @@ in videoDrivers = mkOption { type = types.listOf types.str; - # !!! We'd like "nv" here, but it segfaults the X server. - default = [ "radeon" "cirrus" "vesa" "modesetting" ]; + default = [ "amdgpu" "radeon" "nouveau" "modesetting" "fbdev" ]; example = [ - "ati_unfree" "amdgpu" "amdgpu-pro" - "nv" "nvidia" "nvidiaLegacy390" "nvidiaLegacy340" "nvidiaLegacy304" + "nvidia" "nvidiaLegacy390" "nvidiaLegacy340" "nvidiaLegacy304" + "amdgpu-pro" ]; # TODO(@oxij): think how to easily add the rest, like those nvidia things relatedPackages = concatLists diff --git a/nixos/modules/system/boot/kernel_config.nix b/nixos/modules/system/boot/kernel_config.nix index 783685c9dfe41..5d9534024b06b 100644 --- a/nixos/modules/system/boot/kernel_config.nix +++ b/nixos/modules/system/boot/kernel_config.nix @@ -2,24 +2,6 @@ with lib; let - findWinner = candidates: winner: - any (x: x == winner) candidates; - - # winners is an ordered list where first item wins over 2nd etc - mergeAnswer = winners: locs: defs: - let - values = map (x: x.value) defs; - inter = intersectLists values winners; - winner = head winners; - in - if defs == [] then abort "This case should never happen." - else if winner == [] then abort "Give a valid list of winner" - else if inter == [] then mergeOneOption locs defs - else if findWinner values winner then - winner - else - mergeAnswer (tail winners) locs defs; - mergeFalseByDefault = locs: defs: if defs == [] then abort "This case should never happen." else if any (x: x == false) (getValues defs) then false @@ -28,9 +10,7 @@ let kernelItem = types.submodule { options = { tristate = mkOption { - type = types.enum [ "y" "m" "n" null ] // { - merge = mergeAnswer [ "y" "m" "n" ]; - }; + type = types.enum [ "y" "m" "n" null ]; default = null; internal = true; visible = true; diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix index c5470b7af09b0..be83607c0af72 100644 --- a/nixos/modules/virtualisation/amazon-init.nix +++ b/nixos/modules/virtualisation/amazon-init.nix @@ -1,6 +1,10 @@ -{ config, pkgs, ... }: +{ config, lib, pkgs, ... }: + +with lib; let + cfg = config.virtualisation.amazon-init; + script = '' #!${pkgs.runtimeShell} -eu @@ -41,20 +45,33 @@ let nixos-rebuild switch ''; in { - systemd.services.amazon-init = { - inherit script; - description = "Reconfigure the system from EC2 userdata on startup"; - wantedBy = [ "multi-user.target" ]; - after = [ "multi-user.target" ]; - requires = [ "network-online.target" ]; + options.virtualisation.amazon-init = { + enable = mkOption { + default = true; + type = types.bool; + description = '' + Enable or disable the amazon-init service. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.amazon-init = { + inherit script; + description = "Reconfigure the system from EC2 userdata on startup"; + + wantedBy = [ "multi-user.target" ]; + after = [ "multi-user.target" ]; + requires = [ "network-online.target" ]; - restartIfChanged = false; - unitConfig.X-StopOnRemoval = false; + restartIfChanged = false; + unitConfig.X-StopOnRemoval = false; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; }; }; } diff --git a/nixos/modules/virtualisation/containerd.nix b/nixos/modules/virtualisation/containerd.nix new file mode 100644 index 0000000000000..194276d169588 --- /dev/null +++ b/nixos/modules/virtualisation/containerd.nix @@ -0,0 +1,60 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.virtualisation.containerd; + containerdConfigChecked = pkgs.runCommand "containerd-config-checked.toml" { nativeBuildInputs = [pkgs.containerd]; } '' + containerd -c ${cfg.configFile} config dump >/dev/null + ln -s ${cfg.configFile} $out + ''; +in +{ + + options.virtualisation.containerd = with lib.types; { + enable = lib.mkEnableOption "containerd container runtime"; + + configFile = lib.mkOption { + default = null; + description = "path to containerd config file"; + type = nullOr path; + }; + + args = lib.mkOption { + default = {}; + description = "extra args to append to the containerd cmdline"; + type = attrsOf str; + }; + }; + + config = lib.mkIf cfg.enable { + virtualisation.containerd.args.config = lib.mkIf (cfg.configFile != null) (toString containerdConfigChecked); + + environment.systemPackages = [pkgs.containerd]; + + systemd.services.containerd = { + description = "containerd - container runtime"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = with pkgs; [ + containerd + runc + iptables + ]; + serviceConfig = { + ExecStart = ''${pkgs.containerd}/bin/containerd ${lib.concatStringsSep " " (lib.cli.toGNUCommandLine {} cfg.args)}''; + Delegate = "yes"; + KillMode = "process"; + Type = "notify"; + Restart = "always"; + RestartSec = "5"; + StartLimitBurst = "8"; + StartLimitIntervalSec = "120s"; + + # "limits" defined below are adopted from upstream: https://github.com/containerd/containerd/blob/master/containerd.service + LimitNPROC = "infinity"; + LimitCORE = "infinity"; + LimitNOFILE = "infinity"; + TasksMax = "infinity"; + OOMScoreAdjust = "-999"; + }; + }; + }; +} diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix index 1d6a9457dde41..6357baf29e012 100644 --- a/nixos/modules/virtualisation/libvirtd.nix +++ b/nixos/modules/virtualisation/libvirtd.nix @@ -213,7 +213,7 @@ in { systemd.services.libvirtd = { requires = [ "libvirtd-config.service" ]; - after = [ "systemd-udev-settle.service" "libvirtd-config.service" ] + after = [ "libvirtd-config.service" ] ++ optional vswitch.enable "ovs-vswitchd.service"; environment.LIBVIRTD_ARGS = escapeShellArgs ( diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index fe60b0b83f5a6..00e84a9df82c0 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -326,6 +326,7 @@ in predictable-interface-names = handleTest ./predictable-interface-names.nix {}; printing = handleTest ./printing.nix {}; privacyidea = handleTest ./privacyidea.nix {}; + privoxy = handleTest ./privoxy.nix {}; prometheus = handleTest ./prometheus.nix {}; prometheus-exporters = handleTest ./prometheus-exporters.nix {}; prosody = handleTest ./xmpp/prosody.nix {}; diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix index 131f50747fef0..726c7eb6acb68 100644 --- a/nixos/tests/home-assistant.nix +++ b/nixos/tests/home-assistant.nix @@ -24,6 +24,8 @@ in { services.home-assistant = { inherit configDir; enable = true; + # includes the package with all tests enabled + package = pkgs.home-assistant; config = { homeassistant = { name = "Home"; diff --git a/nixos/tests/kafka.nix b/nixos/tests/kafka.nix index d5c54f7d99107..034601c815b92 100644 --- a/nixos/tests/kafka.nix +++ b/nixos/tests/kafka.nix @@ -30,11 +30,6 @@ let ''; package = kafkaPackage; zookeeper = "zookeeper1:2181"; - # These are the default options, but UseCompressedOops doesn't work with 32bit JVM - jvmOptions = [ - "-server" "-Xmx1G" "-Xms1G" "-XX:+UseParNewGC" "-XX:+UseConcMarkSweepGC" "-XX:+CMSClassUnloadingEnabled" - "-XX:+CMSScavengeBeforeRemark" "-XX:+DisableExplicitGC" "-Djava.awt.headless=true" "-Djava.net.preferIPv4Stack=true" - ] ++ optionals (! pkgs.stdenv.isi686 ) [ "-XX:+UseCompressedOops" ]; }; networking.firewall.allowedTCPPorts = [ 9092 ]; @@ -82,4 +77,5 @@ let in with pkgs; { kafka_2_4 = makeKafkaTest "kafka_2_4" apacheKafka_2_4; kafka_2_5 = makeKafkaTest "kafka_2_5" apacheKafka_2_5; + kafka_2_6 = makeKafkaTest "kafka_2_6" apacheKafka_2_6; } diff --git a/nixos/tests/kubernetes/dns.nix b/nixos/tests/kubernetes/dns.nix index 890499a0fb8a9..b6cd811c5aefd 100644 --- a/nixos/tests/kubernetes/dns.nix +++ b/nixos/tests/kubernetes/dns.nix @@ -34,7 +34,7 @@ let name = "redis"; tag = "latest"; contents = [ pkgs.redis pkgs.bind.host ]; - config.Entrypoint = "/bin/redis-server"; + config.Entrypoint = ["/bin/redis-server"]; }; probePod = pkgs.writeText "probe-pod.json" (builtins.toJSON { @@ -55,12 +55,11 @@ let name = "probe"; tag = "latest"; contents = [ pkgs.bind.host pkgs.busybox ]; - config.Entrypoint = "/bin/tail"; + config.Entrypoint = ["/bin/tail"]; }; - extraConfiguration = { config, pkgs, ... }: { + extraConfiguration = { config, pkgs, lib, ... }: { environment.systemPackages = [ pkgs.bind.host ]; - # virtualisation.docker.extraOptions = "--dns=${config.services.kubernetes.addons.dns.clusterIp}"; services.dnsmasq.enable = true; services.dnsmasq.servers = [ "/cluster.local/${config.services.kubernetes.addons.dns.clusterIp}#53" @@ -77,7 +76,7 @@ let # prepare machine1 for test machine1.wait_until_succeeds("kubectl get node machine1.${domain} | grep -w Ready") machine1.wait_until_succeeds( - "docker load < ${redisImage}" + "${pkgs.gzip}/bin/zcat ${redisImage} | ${pkgs.containerd}/bin/ctr -n k8s.io image import -" ) machine1.wait_until_succeeds( "kubectl create -f ${redisPod}" @@ -86,7 +85,7 @@ let "kubectl create -f ${redisService}" ) machine1.wait_until_succeeds( - "docker load < ${probeImage}" + "${pkgs.gzip}/bin/zcat ${probeImage} | ${pkgs.containerd}/bin/ctr -n k8s.io image import -" ) machine1.wait_until_succeeds( "kubectl create -f ${probePod}" @@ -118,7 +117,7 @@ let # prepare machines for test machine1.wait_until_succeeds("kubectl get node machine2.${domain} | grep -w Ready") machine2.wait_until_succeeds( - "docker load < ${redisImage}" + "${pkgs.gzip}/bin/zcat ${redisImage} | ${pkgs.containerd}/bin/ctr -n k8s.io image import -" ) machine1.wait_until_succeeds( "kubectl create -f ${redisPod}" @@ -127,7 +126,7 @@ let "kubectl create -f ${redisService}" ) machine2.wait_until_succeeds( - "docker load < ${probeImage}" + "${pkgs.gzip}/bin/zcat ${probeImage} | ${pkgs.containerd}/bin/ctr -n k8s.io image import -" ) machine1.wait_until_succeeds( "kubectl create -f ${probePod}" diff --git a/nixos/tests/kubernetes/rbac.nix b/nixos/tests/kubernetes/rbac.nix index c922da515d915..3fc8ed0fbe389 100644 --- a/nixos/tests/kubernetes/rbac.nix +++ b/nixos/tests/kubernetes/rbac.nix @@ -85,7 +85,7 @@ let name = "kubectl"; tag = "latest"; contents = [ kubectl pkgs.busybox kubectlPod2 ]; - config.Entrypoint = "/bin/sh"; + config.Entrypoint = ["/bin/sh"]; }; base = { @@ -97,7 +97,7 @@ let machine1.wait_until_succeeds("kubectl get node machine1.my.zyx | grep -w Ready") machine1.wait_until_succeeds( - "docker load < ${kubectlImage}" + "${pkgs.gzip}/bin/zcat ${kubectlImage} | ${pkgs.containerd}/bin/ctr -n k8s.io image import -" ) machine1.wait_until_succeeds( @@ -134,7 +134,7 @@ let machine1.wait_until_succeeds("kubectl get node machine2.my.zyx | grep -w Ready") machine2.wait_until_succeeds( - "docker load < ${kubectlImage}" + "${pkgs.gzip}/bin/zcat ${kubectlImage} | ${pkgs.containerd}/bin/ctr -n k8s.io image import -" ) machine1.wait_until_succeeds( diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix index 0b8e1937128c9..5074b6cdafefe 100644 --- a/nixos/tests/nextcloud/basic.nix +++ b/nixos/tests/nextcloud/basic.nix @@ -7,7 +7,7 @@ in { maintainers = [ globin eqyiel ]; }; - nodes = { + nodes = rec { # The only thing the client needs to do is download a file. client = { ... }: { services.davfs2.enable = true; @@ -47,9 +47,14 @@ in { environment.systemPackages = [ cfg.services.nextcloud.occ ]; }; + + nextcloudWithoutMagick = args@{ config, pkgs, lib, ... }: + lib.mkMerge + [ (nextcloud args) + { services.nextcloud.disableImagemagick = true; } ]; }; - testScript = let + testScript = { nodes, ... }: let withRcloneEnv = pkgs.writeScript "with-rclone-env" '' #!${pkgs.runtimeShell} export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav @@ -68,8 +73,19 @@ in { #!${pkgs.runtimeShell} diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file) ''; + + findInClosure = what: drv: pkgs.runCommand "find-in-closure" { exportReferencesGraph = [ "graph" drv ]; inherit what; } '' + test -e graph + grep "$what" graph >$out || true + ''; + nextcloudUsesImagick = findInClosure "imagick" nodes.nextcloud.config.system.build.vm; + nextcloudWithoutDoesntUseIt = findInClosure "imagick" nodes.nextcloudWithoutMagick.config.system.build.vm; in '' - start_all() + assert open("${nextcloudUsesImagick}").read() != "" + assert open("${nextcloudWithoutDoesntUseIt}").read() == "" + + nextcloud.start() + client.start() nextcloud.wait_for_unit("multi-user.target") # This is just to ensure the nextcloud-occ program is working nextcloud.succeed("nextcloud-occ status") diff --git a/nixos/tests/privoxy.nix b/nixos/tests/privoxy.nix new file mode 100644 index 0000000000000..d16cc498691fd --- /dev/null +++ b/nixos/tests/privoxy.nix @@ -0,0 +1,113 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: + +let + # Note: For some reason Privoxy can't issue valid + # certificates if the CA is generated using gnutls :( + certs = pkgs.runCommand "example-certs" + { buildInputs = [ pkgs.openssl ]; } + '' + mkdir $out + + # generate CA keypair + openssl req -new -nodes -x509 \ + -extensions v3_ca -keyout $out/ca.key \ + -out $out/ca.crt -days 365 \ + -subj "/O=Privoxy CA/CN=Privoxy CA" + + # generate server key/signing request + openssl genrsa -out $out/server.key 3072 + openssl req -new -key $out/server.key \ + -out server.csr -sha256 \ + -subj "/O=An unhappy server./CN=example.com" + + # sign the request/generate the certificate + openssl x509 -req -in server.csr -CA $out/ca.crt \ + -CAkey $out/ca.key -CAcreateserial -out $out/server.crt \ + -days 500 -sha256 + ''; +in + +{ + name = "privoxy"; + meta = with lib.maintainers; { + maintainers = [ rnhmjoj ]; + }; + + machine = { ... }: { + services.nginx.enable = true; + services.nginx.virtualHosts."example.com" = { + addSSL = true; + sslCertificate = "${certs}/server.crt"; + sslCertificateKey = "${certs}/server.key"; + locations."/".root = pkgs.writeTextFile + { name = "bad-day"; + destination = "/how-are-you/index.html"; + text = "I've had a bad day!\n"; + }; + locations."/ads".extraConfig = '' + return 200 "Hot Nixpkgs PRs in your area. Click here!\n"; + ''; + }; + + services.privoxy = { + enable = true; + inspectHttps = true; + settings = { + ca-cert-file = "${certs}/ca.crt"; + ca-key-file = "${certs}/ca.key"; + debug = 65536; + }; + userActions = '' + {+filter{positive}} + example.com + + {+block{Fake ads}} + example.com/ads + ''; + userFilters = '' + FILTER: positive This is a filter example. + s/bad/great/ig + ''; + }; + + security.pki.certificateFiles = [ "${certs}/ca.crt" ]; + + networking.hosts."::1" = [ "example.com" ]; + networking.proxy.httpProxy = "http://localhost:8118"; + networking.proxy.httpsProxy = "http://localhost:8118"; + }; + + testScript = + '' + with subtest("Privoxy is running"): + machine.wait_for_unit("privoxy") + machine.wait_for_open_port("8118") + machine.succeed("curl -f http://config.privoxy.org") + + with subtest("Privoxy can filter http requests"): + machine.wait_for_open_port("80") + assert "great day" in machine.succeed( + "curl -sfL http://example.com/how-are-you? | tee /dev/stderr" + ) + + with subtest("Privoxy can filter https requests"): + machine.wait_for_open_port("443") + assert "great day" in machine.succeed( + "curl -sfL https://example.com/how-are-you? | tee /dev/stderr" + ) + + with subtest("Blocks are working"): + machine.wait_for_open_port("443") + machine.fail("curl -f https://example.com/ads 1>&2") + machine.succeed("curl -f https://example.com/PRIVOXY-FORCE/ads 1>&2") + + with subtest("Temporary certificates are cleaned"): + # Count current certificates + machine.succeed("test $(ls /run/privoxy/certs | wc -l) -gt 0") + # Forward in time 12 days, trigger the timer.. + machine.succeed("date -s \"$(date --date '12 days')\"") + machine.systemctl("start systemd-tmpfiles-clean") + # ...and count again + machine.succeed("test $(ls /run/privoxy/certs | wc -l) -eq 0") + ''; +}) diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index bf0d0fa01ec5c..a97a44e02f1c0 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -75,6 +75,21 @@ let ''; }; + artifactory = { + exporterConfig = { + enable = true; + artiUsername = "artifactory-username"; + artiPassword = "artifactory-password"; + }; + exporterTest = '' + wait_for_unit("prometheus-artifactory-exporter.service") + wait_for_open_port(9531) + succeed( + "curl -sSf http://localhost:9531/metrics | grep -q 'artifactory_up'" + ) + ''; + }; + bind = { exporterConfig = { enable = true; |