Merge staging-nixos into master (#509394)
This commit is contained in:
@@ -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.
|
||||
:::
|
||||
|
||||
@@ -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-<interface>.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):
|
||||
|
||||
@@ -261,13 +261,6 @@ See <https://github.com/NixOS/nixpkgs/issues/481673>.
|
||||
|
||||
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`
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
})
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 <foo>, creates a network-addresses-<foo>.service
|
||||
# job that performs static address configuration.
|
||||
#
|
||||
# It has a Wants dependency on <foo>-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 <<EOF
|
||||
${optionalString (cfg.nameservers != [ ] && cfg.domain != null) ''
|
||||
domain ${cfg.domain}
|
||||
''}
|
||||
${optionalString (cfg.search != [ ]) ("search " + concatStringsSep " " cfg.search)}
|
||||
${flip concatMapStrings cfg.nameservers (ns: ''
|
||||
nameserver ${ns}
|
||||
'')}
|
||||
EOF
|
||||
''}
|
||||
|
||||
# Set the default gateway
|
||||
${flip concatMapStrings
|
||||
[
|
||||
{
|
||||
version = "-4";
|
||||
gateway = cfg.defaultGateway;
|
||||
}
|
||||
{
|
||||
version = "-6";
|
||||
gateway = cfg.defaultGateway6;
|
||||
}
|
||||
]
|
||||
(
|
||||
{ version, gateway }:
|
||||
optionalString (gateway != null && gateway.address != "") ''
|
||||
${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;
|
||||
}
|
||||
}
|
||||
''
|
||||
)
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# For each interface <foo>, create a job ‘network-addresses-<foo>.service"
|
||||
# that performs static address configuration. It has a "wants"
|
||||
# dependency on ‘<foo>.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"
|
||||
'';
|
||||
|
||||
@@ -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 <<EOF
|
||||
${optionalString (cfg.nameservers != [ ] && cfg.domain != null) ''
|
||||
domain ${cfg.domain}
|
||||
''}
|
||||
${optionalString (cfg.search != [ ]) ("search " + concatStringsSep " " cfg.search)}
|
||||
${flip concatMapStrings cfg.nameservers (ns: ''
|
||||
nameserver ${ns}
|
||||
'')}
|
||||
EOF
|
||||
'';
|
||||
|
||||
services.mstpd = mkIf needsMstpd { enable = true; };
|
||||
|
||||
virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; };
|
||||
|
||||
@@ -44,13 +44,17 @@ let
|
||||
defaultGateway6 = {
|
||||
address = "fd00:1234:5678:1::1";
|
||||
interface = "enp1s0";
|
||||
source = "fd00:1234:5678:1::3"; # implicit dependency on enp2s0
|
||||
source = "fd00:1234:5678:1::3";
|
||||
};
|
||||
interfaces.enp1s0.ipv6.addresses = [
|
||||
{
|
||||
address = "fd00:1234:5678:1::2";
|
||||
prefixLength = 64;
|
||||
}
|
||||
{
|
||||
address = "fd00:1234:5678:1::3";
|
||||
prefixLength = 128;
|
||||
}
|
||||
];
|
||||
interfaces.enp1s0.ipv4.addresses = [
|
||||
{
|
||||
@@ -72,12 +76,6 @@ let
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
interfaces.enp2s0.ipv6.addresses = [
|
||||
{
|
||||
address = "fd00:1234:5678:1::3";
|
||||
prefixLength = 128;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
@@ -110,41 +108,6 @@ let
|
||||
client.succeed("ip -6 route show default | grep -q 'src fd00:1234:5678:1::3'")
|
||||
'';
|
||||
};
|
||||
dynamicInterface = {
|
||||
name = "dynamicInterface";
|
||||
nodes.machine = clientConfig {
|
||||
networking.interfaces.usb0 = {
|
||||
ipv6.addresses = lib.singleton {
|
||||
address = "fd::1";
|
||||
prefixLength = 127;
|
||||
};
|
||||
};
|
||||
networking.defaultGateway6 = {
|
||||
address = "fd::";
|
||||
interface = "usb0";
|
||||
source = "fd::1";
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
with subtest("Network comes up without usb0"):
|
||||
machine.wait_for_unit("network.target")
|
||||
|
||||
with subtest("multi-user.target does not hang"):
|
||||
machine.require_unit_state("multi-user.target", "active")
|
||||
|
||||
with subtest("usb0 is configured when plugged in"):
|
||||
machine.succeed("ip link add usb0 type sit local 1.2.3.4")
|
||||
machine.wait_until_succeeds("ip addr show dev usb0 | grep -q fd::1")
|
||||
|
||||
with subtest("Network is now online"):
|
||||
machine.systemctl("start network-online.target")
|
||||
machine.require_unit_state("network-online.target", "active")
|
||||
|
||||
with subtest("Default gateway is now set"):
|
||||
machine.succeed("ip -6 route show default | grep -q 'via fd::'")
|
||||
machine.succeed("ip -6 route show default | grep -q 'src fd::1'")
|
||||
'';
|
||||
};
|
||||
routeType = {
|
||||
name = "RouteType";
|
||||
nodes.client = clientConfig {
|
||||
|
||||
@@ -323,7 +323,7 @@ def build_and_activate_system(
|
||||
if args.diff:
|
||||
if current_config.exists():
|
||||
nix.diff_closures(
|
||||
current_config=current_config.readlink(),
|
||||
current_config=current_config,
|
||||
new_config=path_to_config,
|
||||
target_host=target_host,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user