Skip to content

Commit 27be7a7

Browse files
committed
Support configuring network locations
This adds support for configuring DNS servers and search domains for each location. This is exposed through the networking.location option. The networking.knownNetworkServices option now applies globally across all locations. The networking.dns and networking.search options are now aliases under networking.location.Automatic, since the "Automatic" network location is the default location on an unconfigured system.
1 parent a35b08d commit 27be7a7

File tree

2 files changed

+151
-30
lines changed

2 files changed

+151
-30
lines changed

modules/networking/default.nix

+128-27
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,92 @@ let
77

88
hostnameRegEx = ''^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'';
99

10-
emptyList = lst: if lst != [] then lst else ["empty"];
10+
emptyList = lst: if lst != [ ] then lst else [ "empty" ];
1111

1212
onOff = cond: if cond then "on" else "off";
1313

14-
setNetworkServices = optionalString (cfg.knownNetworkServices != []) ''
15-
networkservices=$(networksetup -listallnetworkservices)
16-
${concatMapStringsSep "\n" (srv: ''
17-
case "$networkservices" in
18-
*${lib.escapeShellArg srv}*)
19-
networksetup -setdnsservers ${lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.dns))}
20-
networksetup -setsearchdomains ${lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.search))}
21-
;;
22-
esac
23-
'') cfg.knownNetworkServices}
14+
setLocations = optionalString (cfg.knownNetworkServices != [ ] && cfg.location != { }) ''
15+
curr_location=$(networksetup -getcurrentlocation)
16+
17+
readarray -t curr_locations_array < <(networksetup -listlocations)
18+
19+
declare -A curr_locations
20+
for location in "''${curr_locations_array[@]}"; do
21+
curr_locations[$location]=1
22+
done
23+
24+
declare -A goal_locations
25+
for location in ${strings.escapeShellArgs (builtins.attrNames cfg.location)}; do
26+
goal_locations[$location]=1
27+
done
28+
29+
for location in "''${!goal_locations[@]}"; do
30+
if [[ ! -v curr_locations[$location] ]]; then
31+
networksetup -createlocation "$location" populate > /dev/null
32+
fi
33+
done
34+
35+
# switch to a location that surely does not need to be deleted
36+
networksetup -switchtolocation ${strings.escapeShellArg (builtins.head (builtins.attrNames cfg.location))} > /dev/null
37+
38+
for location in "''${!curr_locations[@]}"; do
39+
if [[ ! -v goal_locations[$location] ]]; then
40+
networksetup -deletelocation "$location" > /dev/null
41+
fi
42+
done
43+
44+
${concatMapStringsSep "\n" (location: ''
45+
networksetup -switchtolocation ${strings.escapeShellArg location} > /dev/null
46+
47+
networkservices=$(networksetup -listallnetworkservices)
48+
${concatMapStringsSep "\n" (srv: ''
49+
case "$networkservices" in
50+
*${lib.escapeShellArg srv}*)
51+
networksetup -setdnsservers ${
52+
lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.location.${location}.dns))
53+
}
54+
networksetup -setsearchdomains ${
55+
lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.location.${location}.search))
56+
}
57+
;;
58+
esac
59+
'') cfg.knownNetworkServices}
60+
'') (builtins.attrNames cfg.location)}
61+
62+
if [[ -v goal_locations[$curr_location] ]]; then
63+
networksetup -switchtolocation "$curr_location" > /dev/null
64+
fi
2465
'';
2566
in
2667

2768
{
69+
imports = [
70+
(mkAliasOptionModule
71+
[
72+
"networking"
73+
"dns"
74+
]
75+
[
76+
"networking"
77+
"location"
78+
"Automatic"
79+
"dns"
80+
]
81+
)
82+
(mkAliasOptionModule
83+
[
84+
"networking"
85+
"search"
86+
]
87+
[
88+
"networking"
89+
"location"
90+
"Automatic"
91+
"search"
92+
]
93+
)
94+
];
95+
2896
options = {
2997
networking.computerName = mkOption {
3098
type = types.nullOr types.str;
@@ -73,27 +141,54 @@ in
73141

74142
networking.knownNetworkServices = mkOption {
75143
type = types.listOf types.str;
76-
default = [];
77-
example = [ "Wi-Fi" "Ethernet Adaptor" "Thunderbolt Ethernet" ];
144+
default = [ ];
145+
example = [
146+
"Wi-Fi"
147+
"Ethernet Adaptor"
148+
"Thunderbolt Ethernet"
149+
];
78150
description = ''
79-
List of networkservices that should be configured.
151+
List of network services that should be configured.
80152
81153
To display a list of all the network services on the server's
82154
hardware ports, use {command}`networksetup -listallnetworkservices`.
83155
'';
84156
};
85157

86-
networking.dns = mkOption {
87-
type = types.listOf types.str;
88-
default = [];
89-
example = [ "8.8.8.8" "8.8.4.4" "2001:4860:4860::8888" "2001:4860:4860::8844" ];
90-
description = "The list of dns servers used when resolving domain names.";
91-
};
158+
networking.location = mkOption {
159+
type = types.attrsOf (
160+
types.submodule {
161+
options = {
162+
dns = mkOption {
163+
type = types.listOf types.str;
164+
default = [ ];
165+
example = [
166+
"8.8.8.8"
167+
"8.8.4.4"
168+
"2001:4860:4860::8888"
169+
"2001:4860:4860::8844"
170+
];
171+
description = "The list of DNS servers used when resolving domain names.";
172+
};
173+
174+
search = mkOption {
175+
type = types.listOf types.str;
176+
default = [ ];
177+
description = "The list of search paths used when resolving domain names.";
178+
};
179+
};
180+
}
181+
);
182+
default = { };
183+
description = ''
184+
Set of network locations to configure.
92185
93-
networking.search = mkOption {
94-
type = types.listOf types.str;
95-
default = [];
96-
description = "The list of search paths used when resolving domain names.";
186+
By default, a system comes with a single location called "Automatic", but you can
187+
define additional locations to switch between different network configurations.
188+
189+
If you define any locations here, you must also explicitly define the "Automatic"
190+
location if you want it to exist.
191+
'';
97192
};
98193

99194
networking.wakeOnLan.enable = mkOption {
@@ -110,8 +205,14 @@ in
110205
config = {
111206

112207
warnings = [
113-
(mkIf (cfg.knownNetworkServices == [] && cfg.dns != []) "networking.knownNetworkServices is empty, dns servers will not be configured.")
114-
(mkIf (cfg.knownNetworkServices == [] && cfg.search != []) "networking.knownNetworkServices is empty, dns searchdomains will not be configured.")
208+
(mkIf (
209+
cfg.knownNetworkServices == [ ]
210+
&& (builtins.any (l: l.dns != [ ]) (builtins.attrValues cfg.location))
211+
) "networking.knownNetworkServices is empty, DNS servers will not be configured.")
212+
(mkIf (
213+
cfg.knownNetworkServices == [ ]
214+
&& (builtins.any (l: l.search != [ ]) (builtins.attrValues cfg.location))
215+
) "networking.knownNetworkServices is empty, DNS search domains will not be configured.")
115216
];
116217

117218
system.activationScripts.networking.text = ''
@@ -128,7 +229,7 @@ in
128229
scutil --set LocalHostName ${escapeShellArg cfg.localHostName}
129230
''}
130231
131-
${setNetworkServices}
232+
${setLocations}
132233
133234
${optionalString (cfg.wakeOnLan.enable != null) ''
134235
systemsetup -setWakeOnNetworkAccess '${onOff cfg.wakeOnLan.enable}' &> /dev/null

tests/networking-networkservices.nix

+23-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
11
{ config, lib, ... }:
22

33
{
4-
networking.knownNetworkServices = [ "Wi-Fi" "Thunderbolt Ethernet" ];
5-
networking.dns = [ "8.8.8.8" "8.8.4.4" ];
4+
networking.knownNetworkServices = [
5+
"Wi-Fi"
6+
"Thunderbolt Ethernet"
7+
];
8+
networking.dns = [
9+
"8.8.8.8"
10+
"8.8.4.4"
11+
];
12+
networking.location."Home Lab" = {
13+
search = [ "home.lab" ];
14+
};
615

716
test = ''
817
echo checking dns settings in /activate >&2
18+
19+
grep "networksetup -switchtolocation ${lib.escapeShellArg "Automatic"}" ${config.out}/activate
920
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Wi-Fi" "8.8.8.8" "8.8.4.4" ]}" ${config.out}/activate
1021
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "8.8.8.8" "8.8.4.4" ]}" ${config.out}/activate
11-
echo checking empty searchdomain settings in /activate >&2
22+
23+
grep "networksetup -switchtolocation ${lib.escapeShellArg "Home Lab"}" ${config.out}/activate
24+
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Wi-Fi" "empty" ]}" ${config.out}/activate
25+
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "empty" ]}" ${config.out}/activate
26+
27+
echo checking searchdomain settings in /activate >&2
28+
1229
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Wi-Fi" "empty" ]}" ${config.out}/activate
1330
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "empty" ]}" ${config.out}/activate
31+
32+
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Wi-Fi" "home.lab" ]}" ${config.out}/activate
33+
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "home.lab" ]}" ${config.out}/activate
1434
'';
1535
}

0 commit comments

Comments
 (0)