diff --git a/nixos/doc/manual/configuration/ad-hoc-network-config.section.md b/nixos/doc/manual/configuration/ad-hoc-network-config.section.md index 611f3e3956ee..7ef24825526d 100644 --- a/nixos/doc/manual/configuration/ad-hoc-network-config.section.md +++ b/nixos/doc/manual/configuration/ad-hoc-network-config.section.md @@ -1,24 +1,14 @@ # Ad-Hoc Configuration {#ad-hoc-network-config} -You can use [](#opt-networking.localCommands) to specify shell commands to be -run after the network interfaces have been created, but not necessarily fully -configured. -This is useful for doing network configuration not covered by the existing -NixOS modules. For example, you can create a network namespace and a pair -of virtual ethernet devices like this: +You can use [](#opt-networking.localCommands) to +specify shell commands to be run at the end of `network-setup.service`. This +is useful for doing network configuration not covered by the existing NixOS +modules. For instance, to statically configure an IPv6 address: ```nix { networking.localCommands = '' - ip netns add mynet - ip link add name veth-in type veth peer name veth-out - ip link set dev veth-out netns mynet + ip -6 addr add 2001:610:685:1::1/64 dev eth0 ''; } ``` - -::: {.note} -The commands should ideally be idempotent, so it's recommended to perform -cleanups of the state you create (e.g. virtual interfaces), or at least make -sure possible failures are handled. -::: diff --git a/nixos/doc/manual/configuration/ipv4-config.section.md b/nixos/doc/manual/configuration/ipv4-config.section.md index ceb9a350b680..ed0db9da8c3e 100644 --- a/nixos/doc/manual/configuration/ipv4-config.section.md +++ b/nixos/doc/manual/configuration/ipv4-config.section.md @@ -26,16 +26,9 @@ servers: ``` ::: {.note} -Addresses and routes for statically configured interfaces and the default -gateway are set up by systemd services named -`network-addresses-.service`. The name servers configuration, -instead, is performed by `network-local-commands.service` using resolvconf. -::: - -::: {.note} -If needed, for example if addresses/routes were added/removed, -you can reset the network configuration by running -`systemctl restart networking-scripted.target` +Statically configured interfaces are set up by the systemd service +`interface-name-cfg.service`. The default gateway and name server +configuration is performed by `network-setup.service`. ::: The host name is set using [](#opt-networking.hostName): diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index 28465b2f0363..b855729692f6 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -261,13 +261,6 @@ See . Note for NetworkManager users: before these changes NetworkManager used to spawn its own wpa_supplicant daemon, but now it relies on `networking.wireless`. So, if you had `networking.wireless.enable = false` in your configuration, you should remove that line. -- Some implementation details of the NixOS network-interfaces module have been changed: - - - In the "scripted" backend, `network-setup.service` has been removed and the network configuration services are now part of `network.target`, which is now directly pulled into `multi-user.target`. - - Interface addresses, routes and default gateways are now configured asynchronously as soon as the underlying network devices become available (fixes issue [#154737](https://github.com/NixOS/nixpkgs/issues/154737)). - - In both "networkd" and "scripted" backends, the configuration of name servers is now part of `network-local-commands.service` (fixes issue [#445496](https://github.com/NixOS/nixpkgs/issues/445496)). - - The issue that resulted in a completely unconfigured network if both `resolvconf` was disabled and no default gateway configured, has also been fixed. - - `kratos` has been updated from 1.3.1 to [25.4.0](https://github.com/ory/kratos/releases/tag/v25.4.0). Upstream switched to a new versioning scheme (year.major.minor). Notable breaking changes: - The `migrate sql` CLI command is now `migrate sql up` diff --git a/nixos/modules/programs/i3lock.nix b/nixos/modules/programs/i3lock.nix index 1b4091abaac3..79b62635a454 100644 --- a/nixos/modules/programs/i3lock.nix +++ b/nixos/modules/programs/i3lock.nix @@ -32,7 +32,7 @@ in description = '' Whether to enable U2F support in the i3lock program. U2F enables authentication using a hardware device, such as a security key. - When U2F support is enabled, the i3lock program will set the setuid bit on the i3lock binary and enable the pam u2fAuth service, + When U2F support is enabled, the i3lock program will set the setuid bit on the i3lock binary and enable the pam u2f service, ''; }; }; @@ -51,7 +51,7 @@ in source = "${cfg.package.out}/bin/i3lock"; }; - security.pam.services.i3lock.u2fAuth = cfg.u2fSupport; + security.pam.services.i3lock.u2f.enable = cfg.u2fSupport; }; diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index d568b32a45a3..539029e16978 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -135,6 +135,7 @@ let imports = [ (lib.mkRenamedOptionModule [ "enableKwallet" ] [ "kwallet" "enable" ]) + (lib.mkRenamedOptionModule [ "u2fAuth" ] [ "u2f" "enable" ]) ]; options = { @@ -202,17 +203,39 @@ let ''; }; - u2fAuth = lib.mkOption { - default = config.security.pam.u2f.enable; - defaultText = lib.literalExpression "config.security.pam.u2f.enable"; - type = lib.types.bool; - description = '' - If set, users listed in - {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or - {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is - not set) are able to log in with the associated U2F key. Path can be - changed using {option}`security.pam.u2f.authFile` option. - ''; + u2f = { + enable = lib.mkOption { + default = config.security.pam.u2f.enable; + defaultText = lib.literalExpression "config.security.pam.u2f.enable"; + type = lib.types.bool; + description = '' + If set, users listed in + {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or + {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is + not set) are able to log in with the associated U2F key. Path can be + changed using {option}`security.pam.u2f.authFile` option. + ''; + }; + + control = lib.mkOption { + default = config.security.pam.u2f.control; + defaultText = lib.literalExpression "config.security.pam.u2f.control"; + type = lib.types.enum [ + "required" + "requisite" + "sufficient" + "optional" + ]; + description = '' + This option sets pam "control". + If you want to have multi factor authentication, use "required". + If you want to use U2F device instead of regular password, use "sufficient". + + Read + {manpage}`pam.conf(5)` + for better understanding of this option. + ''; + }; }; usshAuth = lib.mkOption { @@ -1045,8 +1068,8 @@ let in { name = "u2f"; - enable = cfg.u2fAuth; - control = u2f.control; + enable = cfg.u2f.enable; + control = cfg.u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; inherit (u2f) settings; } diff --git a/nixos/modules/services/system/nix-daemon.nix b/nixos/modules/services/system/nix-daemon.nix index bceaf82798fc..97a5752bd35c 100644 --- a/nixos/modules/services/system/nix-daemon.nix +++ b/nixos/modules/services/system/nix-daemon.nix @@ -297,94 +297,105 @@ in ###### implementation - config = lib.mkIf (cfg.enable && nixPackage.pname != "lix") { - environment.systemPackages = [ - nixPackage - pkgs.nix-info - ] - ++ lib.optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions; - - systemd.packages = [ nixPackage ]; - - # The upstream Nix tmpfiles.d file assumes the daemon runs as root - systemd.tmpfiles.packages = lib.mkIf (cfg.daemonUser == "root") [ nixPackage ]; - - systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; - - systemd.services.nix-daemon = { - path = [ + config = lib.mkMerge [ + (lib.mkIf cfg.enable { + systemd.tmpfiles.rules = [ + "d /nix/var 0755 root root - -" + "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system" + # Boot-time cleanup + "R! /nix/var/nix/gcroots/tmp - - - - -" + "R! /nix/var/nix/temproots - - - - -" + ]; + }) + (lib.mkIf (cfg.enable && nixPackage.pname != "lix") { + environment.systemPackages = [ nixPackage - config.programs.ssh.package + pkgs.nix-info ] - # For running "newuidmap" - ++ lib.optional (cfg.daemonUser != "root") "/run/wrappers"; + ++ lib.optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions; - environment = - cfg.envVars - // { - CURL_CA_BUNDLE = config.security.pki.caBundle; - } - // config.networking.proxy.envVars; + systemd.packages = [ nixPackage ]; + + # The upstream Nix tmpfiles.d file assumes the daemon runs as root + systemd.tmpfiles.packages = lib.mkIf (cfg.daemonUser == "root") [ nixPackage ]; + + systemd.sockets.nix-daemon.wantedBy = [ "sockets.target" ]; + + systemd.services.nix-daemon = { + path = [ + nixPackage + config.programs.ssh.package + ] + # For running "newuidmap" + ++ lib.optional (cfg.daemonUser != "root") "/run/wrappers"; + + environment = + cfg.envVars + // { + CURL_CA_BUNDLE = config.security.pki.caBundle; + } + // config.networking.proxy.envVars; + + serviceConfig = { + CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy; + IOSchedulingClass = cfg.daemonIOSchedClass; + IOSchedulingPriority = cfg.daemonIOSchedPriority; + }; + + restartTriggers = [ config.environment.etc."nix/nix.conf".source ]; + + # `stopIfChanged = false` changes to switch behavior + # from stop -> update units -> start + # to update units -> restart + # + # The `stopIfChanged` setting therefore controls a trade-off between a + # more predictable lifecycle, which runs the correct "version" of + # the `ExecStop` line, and on the other hand the availability of + # sockets during the switch, as the effectiveness of the stop operation + # depends on the socket being stopped as well. + # + # As `nix-daemon.service` does not make use of `ExecStop`, we prefer + # to keep the socket up and available. This is important for machines + # that run Nix-based services, such as automated build, test, and deploy + # services, that expect the daemon socket to be available at all times. + # + # Notably, the Nix client does not retry on failure to connect to the + # daemon socket, and the in-process RemoteStore instance will disable + # itself. This makes retries infeasible even for services that are + # aware of the issue. Failure to connect can affect not only new client + # processes, but also new RemoteStore instances in existing processes, + # as well as existing RemoteStore instances that have not saturated + # their connection pool. + # + # Also note that `stopIfChanged = true` does not kill existing + # connection handling daemons, as one might wish to happen before a + # breaking Nix upgrade (which is rare). The daemon forks that handle + # the individual connections split off into their own sessions, causing + # them not to be stopped by systemd. + # If a Nix upgrade does require all existing daemon processes to stop, + # nix-daemon must do so on its own accord, and only when the new version + # starts and detects that Nix's persistent state needs an upgrade. + stopIfChanged = false; - serviceConfig = { - CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy; - IOSchedulingClass = cfg.daemonIOSchedClass; - IOSchedulingPriority = cfg.daemonIOSchedPriority; }; - restartTriggers = [ config.environment.etc."nix/nix.conf".source ]; + # Set up the environment variables for running Nix. + environment.sessionVariables = cfg.envVars; - # `stopIfChanged = false` changes to switch behavior - # from stop -> update units -> start - # to update units -> restart - # - # The `stopIfChanged` setting therefore controls a trade-off between a - # more predictable lifecycle, which runs the correct "version" of - # the `ExecStop` line, and on the other hand the availability of - # sockets during the switch, as the effectiveness of the stop operation - # depends on the socket being stopped as well. - # - # As `nix-daemon.service` does not make use of `ExecStop`, we prefer - # to keep the socket up and available. This is important for machines - # that run Nix-based services, such as automated build, test, and deploy - # services, that expect the daemon socket to be available at all times. - # - # Notably, the Nix client does not retry on failure to connect to the - # daemon socket, and the in-process RemoteStore instance will disable - # itself. This makes retries infeasible even for services that are - # aware of the issue. Failure to connect can affect not only new client - # processes, but also new RemoteStore instances in existing processes, - # as well as existing RemoteStore instances that have not saturated - # their connection pool. - # - # Also note that `stopIfChanged = true` does not kill existing - # connection handling daemons, as one might wish to happen before a - # breaking Nix upgrade (which is rare). The daemon forks that handle - # the individual connections split off into their own sessions, causing - # them not to be stopped by systemd. - # If a Nix upgrade does require all existing daemon processes to stop, - # nix-daemon must do so on its own accord, and only when the new version - # starts and detects that Nix's persistent state needs an upgrade. - stopIfChanged = false; + nix.nrBuildUsers = lib.mkDefault ( + if cfg.settings.auto-allocate-uids or false then + 0 + else + lib.max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs) + ); - }; + users.users = nixbldUsers; - # Set up the environment variables for running Nix. - environment.sessionVariables = cfg.envVars; + services.displayManager.hiddenUsers = lib.attrNames nixbldUsers; - nix.nrBuildUsers = lib.mkDefault ( - if cfg.settings.auto-allocate-uids or false then - 0 - else - lib.max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs) - ); - - users.users = nixbldUsers; - - services.displayManager.hiddenUsers = lib.attrNames nixbldUsers; - - # Legacy configuration conversion. - nix.settings.sandbox-fallback = false; - }; + # Legacy configuration conversion. + nix.settings.sandbox-fallback = false; + }) + ]; } diff --git a/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixos/modules/system/boot/systemd/tmpfiles.nix index 12eca48f945d..c606ff1ac50b 100644 --- a/nixos/modules/system/boot/systemd/tmpfiles.nix +++ b/nixos/modules/system/boot/systemd/tmpfiles.nix @@ -350,19 +350,11 @@ in "d /var/db 0755 root root - -" "L /var/lock - - - - ../run/lock" ] - ++ lib.optionals config.nix.enable [ - "d /nix/var 0755 root root - -" - "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system" - ] # Boot-time cleanup ++ [ "R! /etc/group.lock - - - - -" "R! /etc/passwd.lock - - - - -" "R! /etc/shadow.lock - - - - -" - ] - ++ lib.optionals config.nix.enable [ - "R! /nix/var/nix/gcroots/tmp - - - - -" - "R! /nix/var/nix/temproots - - - - -" ]; boot.initrd.systemd = { diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 4bf274d4157f..4b457e078e77 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -51,71 +51,6 @@ let (lib.concatStringsSep " ") ]; - # Converts an IPv4 address literal to a list of bits - parseAddr.ipv4 = - addr: - let - pad = b: lib.replicate (8 - builtins.length b) 0 ++ b; - toBin = n: pad (lib.toBaseDigits 2 (lib.toInt n)); - in - lib.concatMap toBin (builtins.splitVersion addr); - - # Converts an IPv6 address literal to a list of bits - parseAddr.ipv6 = - addr: - let - pad = b: lib.replicate (16 - builtins.length b) 0 ++ b; - fromHex = n: (builtins.fromTOML "n = 0x${n}").n; - toBin = n: pad (lib.toBaseDigits 2 (fromHex n)); - normal = (lib.network.ipv6.fromString addr).address; - in - lib.concatMap toBin (lib.splitString ":" normal); - - # Checks if `addr` is part of the `net` subnet - inSubnet = - v: net: addr: - let - prefix = lib.take net.prefixLength (parseAddr.${v} net.address); - match = lib.zipListsWith (a: b: a == b) prefix (parseAddr.${v} addr); - in - lib.all lib.id match; - - # Checks if the netmask of all addresses on interface `iface` includes - # the IP address of `gateway` - # - # Note: this is used to check whether networking.defaultGateway relies on - # the given interface, either explicitly, via the `interface` (optional), - # or explicitly, by using an address in a subnet of this interface. - # - # Configuration of the default gateway is then performed as part of that - # interface setup in `configureAddrs`, below. - isGateway = - v: gateway: iface: - lib.any lib.id ( - [ (iface.name == gateway.interface) ] - ++ map (net: inSubnet v net gateway.address) iface.${v}.addresses - ); - - # Checks if `gateway` uses an address from `iface` as default source - # - # Note: this is needed to delay the configuration of the gateway and default - # source until the right interfaces and address have been set up, otherwise - # the commands will fail. - hasSource = - v: gateway: iface: - builtins.elem gateway.source (map (i: i.address) iface.${v}.addresses); - - # Interfaces corresponding to the default gateways - gateway4Iface = builtins.filter (isGateway "ipv4" cfg.defaultGateway) interfaces; - gateway6Iface = builtins.filter (isGateway "ipv6" cfg.defaultGateway6) interfaces; - - # Interfaces corresponding to the default source addresses - # - # Note: the use of `head` here is safe because these expressions - # are evaluated only when `needsSourceIface`, see `configureAddrs` below. - source4Iface = builtins.head (builtins.filter (hasSource "ipv4" cfg.defaultGateway) interfaces); - source6Iface = builtins.head (builtins.filter (hasSource "ipv6" cfg.defaultGateway6) interfaces); - # warn that these attributes are deprecated (2017-2-2) # Should be removed in the release after next bondDeprecation = rec { @@ -183,71 +118,121 @@ let else optional (!config.boot.isContainer) (subsystemDevice dev); - # For each interface , creates a network-addresses-.service - # job that performs static address configuration. - # - # It has a Wants dependency on -netdev.service, which creates - # create the interface, or on a device unit (for hardware interfaces). - # It also has a BindsTo dependency on the device unit: so, it only gets - # started after the interface has appeared and it's stopped when the - # interface disappears. - # - # Unless in a container, the job is not made part of network.target, so - # if an interface is not found (e.g. a USB interface not plugged in) it - # will not hang the boot sequence. - # - # If the interface is the default gateway, the job will also set the - # default gateway and delay network-online.target. + hasDefaultGatewaySet = + (cfg.defaultGateway != null && cfg.defaultGateway.address != "") + || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != ""); + + needNetworkSetup = + cfg.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null; + + networkLocalCommands = lib.mkIf needNetworkSetup { + after = [ "network-setup.service" ]; + bindsTo = [ "network-setup.service" ]; + }; + + networkSetup = lib.mkIf needNetworkSetup { + description = "Networking Setup"; + + after = [ "network-pre.target" ]; + before = [ + "network.target" + "shutdown.target" + ]; + wants = [ "network.target" ]; + # exclude bridges from the partOf relationship to fix container networking bug #47210 + partOf = map (i: "network-addresses-${i.name}.service") ( + filter (i: !(hasAttr i.name cfg.bridges)) interfaces + ); + conflicts = [ "shutdown.target" ]; + wantedBy = [ "multi-user.target" ] ++ optional hasDefaultGatewaySet "network-online.target"; + + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + + path = [ pkgs.iproute2 ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + unitConfig.DefaultDependencies = false; + + script = '' + ${optionalString config.networking.resolvconf.enable '' + # Set the static DNS configuration, if given. + ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <, create a job ‘network-addresses-.service" + # that performs static address configuration. It has a "wants" + # dependency on ‘.service’, which is supposed to create + # the interface and need not exist (i.e. for hardware + # interfaces). It has a binds-to dependency on the actual + # network device, so it only gets started after the interface + # has appeared, and it's stopped when the interface + # disappears. configureAddrs = i: let ips = interfaceIps i; - isDefaultGateway4 = cfg.defaultGateway != null && builtins.elem i gateway4Iface; - isDefaultGateway6 = cfg.defaultGateway6 != null && builtins.elem i gateway6Iface; - needsSourceIface4 = - isDefaultGateway4 && cfg.defaultGateway.source != null && i.name != source4Iface.name; - needsSourceIface6 = - isDefaultGateway6 && cfg.defaultGateway6.source != null && i.name != source6Iface.name; - - configureGateway = - version: gateway: - optionalString (gateway.address != "") '' - echo -n "setting ${i.name} as default IPv${version} gateway... " - ${optionalString (gateway.interface != null) '' - ip -${version} route replace ${gateway.address} proto static ${ - formatIpArgs { - metric = gateway.metric; - dev = gateway.interface; - } - } - ''} - ip -${version} route replace default proto static ${ - formatIpArgs { - metric = gateway.metric; - via = gateway.address; - window = cfg.defaultGatewayWindowSize; - dev = gateway.interface; - src = gateway.source; - } - } - echo "done" - ''; in nameValuePair "network-addresses-${i.name}" { description = "Address configuration of ${i.name}"; - - wantedBy = - deviceDependency i.name - ++ optional config.boot.isContainer "network.target" - ++ optional (isDefaultGateway4 || isDefaultGateway6) "network-online.target"; + wantedBy = [ + "network-setup.service" + "network.target" + ]; + # order before network-setup because the routes that are configured + # there may need ip addresses configured + before = [ "network-setup.service" ]; bindsTo = deviceDependency i.name; - partOf = [ "networking-scripted.target" ]; - after = [ - "network-pre.target" - ] - ++ optional needsSourceIface4 "network-addresses-${source4Iface.name}.service" - ++ optional needsSourceIface6 "network-addresses-${source6Iface.name}.service" - ++ deviceDependency i.name; + after = [ "network-pre.target" ] ++ (deviceDependency i.name); serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; # Restart rather than stop+start this unit to prevent the @@ -299,10 +284,6 @@ let fi '' )} - - # Set the default gateway - ${optionalString isDefaultGateway4 (configureGateway "4" cfg.defaultGateway)} - ${optionalString isDefaultGateway6 (configureGateway "6" cfg.defaultGateway6)} ''; preStop = '' state="/run/nixos/network/routes/${i.name}" @@ -330,13 +311,13 @@ let nameValuePair "${i.name}-netdev" { description = "Virtual Network Interface ${i.name}"; bindsTo = optional (!config.boot.isContainer) "dev-net-tun.device"; - partOf = [ "networking-scripted.target" ]; after = optional (!config.boot.isContainer) "dev-net-tun.device" ++ [ "network-pre.target" ]; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice i.name) ]; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; path = [ pkgs.iproute2 ]; serviceConfig = { Type = "oneshot"; @@ -362,21 +343,18 @@ let description = "Bridge Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps ++ optional v.rstp "mstpd.service"; - partOf = [ - "network.target" - "networking-scripted.target" - ] - ++ optional v.rstp "mstpd.service"; + partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service"; after = [ "network-pre.target" ] ++ deps ++ optional v.rstp "mstpd.service" ++ map (i: "network-addresses-${i}.service") v.interfaces; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -470,14 +448,15 @@ let description = "Open vSwitch Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ] ++ internalConfigs; - before = [ "network.target" ] ++ internalConfigs; - partOf = [ - "network.target" - "networking-scripted.target" - ]; # shutdown the bridge when network is shutdown + # before = [ "network-setup.service" ]; + # should work without internalConfigs dependencies because address/link configuration depends + # on the device, which is created by ovs-vswitchd with type=internal, but it does not... + before = [ "network-setup.service" ] ++ internalConfigs; + partOf = [ "network-setup.service" ]; # shutdown the bridge when network is shutdown bindsTo = [ "ovs-vswitchd.service" ]; # requires ovs-vswitchd to be alive at all times after = [ "network-pre.target" @@ -542,12 +521,12 @@ let description = "Bond Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps ++ map (i: "network-addresses-${i}.service") v.interfaces; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ @@ -591,12 +570,12 @@ let description = "MACVLAN Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -623,12 +602,12 @@ let description = "IPVLAN Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -668,12 +647,12 @@ let description = "FOU endpoint ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -698,11 +677,12 @@ let description = "IPv6 in IPv4 Tunnel Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -740,12 +720,12 @@ let description = "IP in IP Tunnel Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -788,12 +768,12 @@ let description = "GRE Tunnel Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "networking-scripted.target" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -823,15 +803,13 @@ let description = "VLAN Interface ${n}"; wantedBy = [ "network.target" + "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ - "network.target" - "networking-scripted.target" - ]; + partOf = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps; - before = [ "network.target" ]; + before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; path = [ pkgs.iproute2 ]; @@ -867,27 +845,10 @@ let // mapAttrs' createGreDevice cfg.greTunnels // mapAttrs' createVlanDevice cfg.vlans // { - network-local-commands = { - after = [ "network-pre.target" ]; - wantedBy = [ "network.target" ]; - }; + network-setup = networkSetup; + network-local-commands = networkLocalCommands; }; - # Note: the scripted networking backend consistent of many - # independent services that are linked to the network.target. - # Since there is no daemon (e.g systemd-networkd) that is - # started as part of the system and pulls in network.target. - # Thus, to start these services we link network.target directly - # to multi-user.target, this has the same result. - systemd.targets.network.wantedBy = [ "multi-user.target" ]; - - # This target serves no purpose during the boot, but can be - # used to quickly reset the network configuration by running - # systemctl restart networking-scripted.target - systemd.targets.networking-scripted = { - description = "NixOS scripted networking setup"; - }; - services.udev.extraRules = '' KERNEL=="tun", TAG+="systemd" ''; diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index d0fe0ed8443f..83d60ba80e40 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -747,9 +747,10 @@ in default = ""; example = "text=anything; echo You can put $text here."; description = '' - Shell commands to be executed after all the network - interfaces have been created, but not necessarily - fully configured. + Shell commands to be executed at the end of the + `network-setup` systemd service. Note that if + you are using DHCP to obtain the network configuration, + interfaces may not be fully configured yet. ''; }; @@ -1850,20 +1851,6 @@ in ''; }; }; - - networking.localCommands = lib.mkIf config.networking.resolvconf.enable '' - # Set the static DNS configuration, if given. - ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <