Possible to programatically make variant of existing NixOS module?


#1

I want to make a variant of the wpa_supplicant module, which is exactly the same and inherits all configuration, except that the ExecStart of the systemd service is prefixed with ip netns exec foo (which launches the process inside a network namespace). I want the original wpa_supplicant service to continue existing, so that I can switch between them with systemctl rather than with nixos-rebuild.

Is this possible? Would it have to be in an overlay? Or can I do something along the lines of

    wpa-wg0 = import /etc/nixos/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix { } // { my-changes; };

I haven’t had any luck with this type of approach, and I suspect it might be impossible to structure like this.

This is in connection with https://github.com/NixOS/nixpkgs/issues/52411


#2

This is probably doable, but it will hardly be elegant. Not only you will need to change ExecStart, you will also need to replace the name of the systemd service.

So I think that an easier and more reliable option would be to just copy the module file to the directory you control, change it the way you like, and import this new module.

(Feel free to correct me if I’m wrong here.)


#3

You could add a directory for the service inside /etc/systemd/system directory named foo.service.d (if the service you want to redefine is foo.service) and add a .conf file in there with the added parameters as explained in man systemd.unit (lookup Along with a unit file foo.service, a "drop-in" directory foo.service.d/ may exist.) I think you can use the etc subtree in NixOS conf to achieve that.


#4

The name of the systemd service doesn’t have to be changed. The best way I know of the top of my head is using including <nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix> in disabledModules while importing the revised new module.


#5

I’m pretty sure this function in <nixpkgs/lib/modules.nix> does exactly what you want:

538   # Convenient property used to transfer all definitions and their
539   # properties from one option to another. This property is useful for
540   # renaming options, and also for including properties from another module
541   # system, including sub-modules.
542   #
543   #   { config, options, ... }:
544   #
545   #   {
546   #     # 'bar' might not always be defined in the current module-set.
547   #     config.foo.enable = mkAliasDefinitions (options.bar.enable or {});
548   #
549   #     # 'barbaz' has to be defined in the current module-set.
550   #     config.foobar.paths = mkAliasDefinitions options.barbaz.paths;
551   #   }
552   #
553   # Note, this is different than taking the value of the option and using it
554   # as a definition, as the new definition will not keep the mkOverride /
555   # mkDefault properties of the previous option.
556   #
557   mkAliasDefinitions = mkAliasAndWrapDefinitions id;
558   mkAliasAndWrapDefinitions = wrap: option:
559     mkIf (isOption option && option.isDefined) (wrap (mkMerge option.definitions));

#6

I’d like to present another kind of solution here. Enter nesting.clone!

networking.wireless.enable = true;

nesting.clone = [
  ({ pkgs, lib, config, ...}: {
    systemd.services.wpa_supplicant.serviceConfig = lib.mkForce {
      ExecStart = "${pkgs.iproute}/bin/ip netns exec foo";
    };
  })
];

Rebuild. Now, when you want to activate the new fine tuned variant of your system with patched wpa_supplicant, you do:

$ sudo /nix/var/nix/profiles/system/fine-tune/child-1/bin/switch-to-configuration test

This will run NixOS activation, but no rebuilds. Also, this is only little patched VS original system, so should be fast and minimal service restarts.

When you want to switch back,

$ sudo /nix/var/nix/profiles/system/bin/switch-to-configuration test