]> git.openstreetmap.org Git - chef.git/blob - cookbooks/networking/recipes/default.rb
4d3974b961c0fd43ea77252603f0abed86ef5024
[chef.git] / cookbooks / networking / recipes / default.rb
1 #
2 # Cookbook:: networking
3 # Recipe:: default
4 #
5 # Copyright:: 2010, OpenStreetMap Foundation.
6 # Copyright:: 2009, Opscode, Inc.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License");
9 # you may not use this file except in compliance with the License.
10 # You may obtain a copy of the License at
11 #
12 #     https://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 #
20 # = Requires
21 # * node[:networking][:nameservers]
22
23 require "ipaddr"
24 require "yaml"
25
26 keys = data_bag_item("networking", "keys")
27
28 package "netplan.io"
29
30 netplan = {
31   "network" => {
32     "version" => 2,
33     "renderer" => "networkd",
34     "ethernets" => {},
35     "bonds" => {},
36     "vlans" => {}
37   }
38 }
39
40 node[:networking][:interfaces].each do |name, interface|
41   if interface[:interface]
42     if interface[:role] && (role = node[:networking][:roles][interface[:role]])
43       if interface[:inet] && role[:inet]
44         node.default[:networking][:interfaces][name][:inet][:prefix] = role[:inet][:prefix]
45         node.default[:networking][:interfaces][name][:inet][:gateway] = role[:inet][:gateway]
46         node.default[:networking][:interfaces][name][:inet][:routes] = role[:inet][:routes]
47       end
48
49       if interface[:inet6] && role[:inet6]
50         node.default[:networking][:interfaces][name][:inet6][:prefix] = role[:inet6][:prefix]
51         node.default[:networking][:interfaces][name][:inet6][:gateway] = role[:inet6][:gateway]
52         node.default[:networking][:interfaces][name][:inet6][:routes] = role[:inet6][:routes]
53       end
54
55       node.default[:networking][:interfaces][name][:metric] = role[:metric]
56       node.default[:networking][:interfaces][name][:zone] = role[:zone]
57     end
58
59     interface = node[:networking][:interfaces][name]
60
61     deviceplan = if interface[:interface] =~ /^(.*)\.(\d+)$/
62                    netplan["network"]["vlans"][interface[:interface]] ||= {
63                      "id" => Regexp.last_match(2).to_i,
64                      "link" => Regexp.last_match(1),
65                      "accept-ra" => false,
66                      "addresses" => [],
67                      "routes" => []
68                    }
69                  elsif interface[:interface] =~ /^bond\d+$/
70                    netplan["network"]["bonds"][interface[:interface]] ||= {
71                      "accept-ra" => false,
72                      "addresses" => [],
73                      "routes" => []
74                    }
75                  else
76                    netplan["network"]["ethernets"][interface[:interface]] ||= {
77                      "accept-ra" => false,
78                      "addresses" => [],
79                      "routes" => []
80                    }
81                  end
82
83     if interface[:inet]
84       deviceplan["addresses"].push("#{interface[:inet][:address]}/#{interface[:inet][:prefix]}")
85     end
86
87     if interface[:inet6]
88       deviceplan["addresses"].push("#{interface[:inet6][:address]}/#{interface[:inet6][:prefix]}")
89     end
90
91     if interface[:mtu]
92       deviceplan["mtu"] = interface[:mtu]
93     end
94
95     if interface[:bond]
96       deviceplan["interfaces"] = interface[:bond][:slaves].to_a
97
98       deviceplan["parameters"] = {
99         "mode" => interface[:bond][:mode] || "active-backup",
100         "mii-monitor-interval" => interface[:bond][:miimon] || 100,
101         "down-delay" => interface[:bond][:downdelay] || 200,
102         "up-delay" => interface[:bond][:updelay] || 200
103       }
104
105       deviceplan["parameters"]["primary"] = interface[:bond][:slaves].first if deviceplan["parameters"]["mode"] == "active-backup"
106       deviceplan["parameters"]["transmit-hash-policy"] = interface[:bond][:xmithashpolicy] if interface[:bond][:xmithashpolicy]
107       deviceplan["parameters"]["lacp-rate"] = interface[:bond][:lacprate] if interface[:bond][:lacprate]
108     end
109
110     if interface[:inet]
111       if interface[:inet][:gateway] && interface[:inet][:gateway] != interface[:inet][:address]
112         deviceplan["routes"].push(
113           "to" => "0.0.0.0/0",
114           "via" => interface[:inet][:gateway],
115           "metric" => interface[:metric],
116           "on-link" => true
117         )
118       end
119
120       if interface[:inet][:routes]
121         interface[:inet][:routes].each do |to, parameters|
122           next if parameters[:via] == interface[:inet][:address]
123
124           route = {
125             "to" => to
126           }
127
128           route["type"] = parameters[:type] if parameters[:type]
129           route["via"] = parameters[:via] if parameters[:via]
130           route["metric"] = parameters[:metric] if parameters[:metric]
131
132           deviceplan["routes"].push(route)
133         end
134       end
135     end
136
137     if interface[:inet6]
138       if interface[:inet6][:gateway] && interface[:inet6][:gateway] != interface[:inet6][:address]
139         deviceplan["routes"].push(
140           "to" => "::/0",
141           "via" => interface[:inet6][:gateway],
142           "metric" => interface[:metric],
143           "on-link" => true
144         )
145
146         # This ordering relies on systemd-networkd adding routes
147         # in reverse order and will need moving before the previous
148         # route once that is fixed:
149         #
150         # https://github.com/systemd/systemd/issues/5430
151         # https://github.com/systemd/systemd/pull/10938
152         if !IPAddr.new(interface[:inet6][:address]).mask(interface[:inet6][:prefix]).include?(interface[:inet6][:gateway]) &&
153            !IPAddr.new("fe80::/64").include?(interface[:inet6][:gateway])
154           deviceplan["routes"].push(
155             "to" => interface[:inet6][:gateway],
156             "scope" => "link"
157           )
158         end
159       end
160
161       if interface[:inet6][:routes]
162         interface[:inet6][:routes].each do |to, parameters|
163           next if parameters[:via] == interface[:inet6][:address]
164
165           route = {
166             "to" => to
167           }
168
169           route["type"] = parameters[:type] if parameters[:type]
170           route["via"] = parameters[:via] if parameters[:via]
171           route["metric"] = parameters[:metric] if parameters[:metric]
172
173           deviceplan["routes"].push(route)
174         end
175       end
176     end
177   else
178     node.rm(:networking, :interfaces, name)
179   end
180 end
181
182 netplan["network"]["bonds"].each_value do |bond|
183   bond["interfaces"].each do |interface|
184     netplan["network"]["ethernets"][interface] ||= { "accept-ra" => false, "optional" => true }
185   end
186 end
187
188 netplan["network"]["vlans"].each_value do |vlan|
189   unless vlan["link"] =~ /^bond\d+$/
190     netplan["network"]["ethernets"][vlan["link"]] ||= { "accept-ra" => false }
191   end
192 end
193
194 file "/etc/netplan/00-installer-config.yaml" do
195   action :delete
196 end
197
198 file "/etc/netplan/01-netcfg.yaml" do
199   action :delete
200 end
201
202 file "/etc/netplan/50-cloud-init.yaml" do
203   action :delete
204 end
205
206 file "/etc/netplan/99-chef.yaml" do
207   owner "root"
208   group "root"
209   mode "644"
210   content YAML.dump(netplan)
211 end
212
213 package "cloud-init" do
214   action :purge
215 end
216
217 if node[:networking][:wireguard][:enabled]
218   wireguard_id = persistent_token("networking", "wireguard")
219
220   node.default[:networking][:wireguard][:address] = "fd43:e709:ea6d:1:#{wireguard_id[0, 4]}:#{wireguard_id[4, 4]}:#{wireguard_id[8, 4]}:#{wireguard_id[12, 4]}"
221
222   package "wireguard-tools" do
223     compile_time true
224     options "--no-install-recommends"
225   end
226
227   directory "/var/lib/systemd/wireguard" do
228     owner "root"
229     group "systemd-network"
230     mode "750"
231     compile_time true
232   end
233
234   file "/var/lib/systemd/wireguard/private.key" do
235     action :create_if_missing
236     owner "root"
237     group "systemd-network"
238     mode "640"
239     content %x(wg genkey)
240     compile_time true
241   end
242
243   node.default[:networking][:wireguard][:public_key] = %x(wg pubkey < /var/lib/systemd/wireguard/private.key).chomp
244
245   file "/var/lib/systemd/wireguard/preshared.key" do
246     action :create_if_missing
247     owner "root"
248     group "systemd-network"
249     mode "640"
250     content keys["wireguard"]
251   end
252
253   if node[:roles].include?("gateway")
254     search(:node, "roles:gateway") do |gateway|
255       next if gateway.name == node.name
256       next unless gateway[:networking][:wireguard] && gateway[:networking][:wireguard][:enabled]
257
258       allowed_ips = gateway.ipaddresses(:role => :internal).map(&:subnet)
259
260       node.default[:networking][:wireguard][:peers] << {
261         :public_key => gateway[:networking][:wireguard][:public_key],
262         :allowed_ips => allowed_ips,
263         :endpoint => "#{gateway.name}:51820"
264       }
265     end
266
267     search(:node, "roles:prometheus") do |server|
268       allowed_ips = server.ipaddresses(:role => :internal).map(&:subnet)
269
270       if server[:networking][:private_address]
271         allowed_ips << "#{server[:networking][:private_address]}/32"
272       end
273
274       node.default[:networking][:wireguard][:peers] << {
275         :public_key => server[:networking][:wireguard][:public_key],
276         :allowed_ips => allowed_ips,
277         :endpoint => "#{server.name}:51820"
278       }
279     end
280
281     node.default[:networking][:wireguard][:peers] << {
282       :public_key => "7Oj9ufNlgidyH/xDc+aHQKMjJPqTmD/ab13agMh6AxA=",
283       :allowed_ips => "10.0.16.1/32",
284       :endpoint => "gate.compton.nu:51820"
285     }
286
287     # Grant home
288     node.default[:networking][:wireguard][:peers] << {
289       :public_key => "RofATnvlWxP3mt87+QKRXFE5MVxtoCcTsJ+yftZYEE4=",
290       :allowed_ips => "10.89.122.1/32",
291       :endpoint => "gate.firefishy.com:51820"
292     }
293
294     # Grant roaming
295     node.default[:networking][:wireguard][:peers] << {
296       :public_key => "YbUkREE9TAmomqgL/4Fh2e5u2Hh7drN/2o5qg3ndRxg=",
297       :allowed_ips => "10.89.123.1/32",
298       :endpoint => "roaming.firefishy.com:51820"
299     }
300   elsif node[:roles].include?("shenron")
301     search(:node, "roles:gateway") do |gateway|
302       allowed_ips = gateway.ipaddresses(:role => :internal).map(&:subnet)
303
304       node.default[:networking][:wireguard][:peers] << {
305         :public_key => gateway[:networking][:wireguard][:public_key],
306         :allowed_ips => allowed_ips,
307         :endpoint => "#{gateway.name}:51820"
308       }
309     end
310   end
311
312   template "/etc/systemd/network/wireguard.netdev" do
313     source "wireguard.netdev.erb"
314     owner "root"
315     group "systemd-network"
316     mode "640"
317   end
318
319   template "/etc/systemd/network/wireguard.network" do
320     source "wireguard.network.erb"
321     owner "root"
322     group "root"
323     mode "644"
324   end
325
326   execute "networkctl-delete-wg0" do
327     action :nothing
328     command "networkctl delete wg0"
329     subscribes :run, "template[/etc/systemd/network/wireguard.netdev]"
330     only_if { ::File.exist?("/sys/class/net/wg0") }
331   end
332
333   execute "networkctl-reload" do
334     action :nothing
335     command "networkctl reload"
336     subscribes :run, "template[/etc/systemd/network/wireguard.netdev]"
337     subscribes :run, "template[/etc/systemd/network/wireguard.network]"
338     not_if { kitchen? }
339   end
340 end
341
342 ohai "reload-hostname" do
343   action :nothing
344   plugin "hostname"
345 end
346
347 execute "hostnamectl-set-hostname" do
348   command "hostnamectl set-hostname #{node[:networking][:hostname]}"
349   notifies :reload, "ohai[reload-hostname]"
350   not_if { kitchen? || node[:hostnamectl][:static_hostname] == node[:networking][:hostname] }
351 end
352
353 template "/etc/hosts" do
354   source "hosts.erb"
355   owner "root"
356   group "root"
357   mode "644"
358   not_if { kitchen? }
359 end
360
361 service "systemd-resolved" do
362   action [:enable, :start]
363 end
364
365 directory "/etc/systemd/resolved.conf.d" do
366   owner "root"
367   group "root"
368   mode "755"
369 end
370
371 template "/etc/systemd/resolved.conf.d/99-chef.conf" do
372   source "resolved.conf.erb"
373   owner "root"
374   group "root"
375   mode "644"
376   notifies :restart, "service[systemd-resolved]", :immediately
377 end
378
379 if node[:filesystem][:by_mountpoint][:"/etc/resolv.conf"]
380   execute "umount-resolve-conf" do
381     command "umount -c /etc/resolv.conf"
382   end
383 end
384
385 link "/etc/resolv.conf" do
386   to "../run/systemd/resolve/stub-resolv.conf"
387 end
388
389 hosts = { :inet => [], :inet6 => [] }
390
391 search(:node, "networking:interfaces").collect do |n|
392   next if n[:fqdn] == node[:fqdn]
393
394   n.interfaces.each do |interface|
395     next unless interface[:role] == "external"
396
397     hosts[:inet] << interface[:inet][:address] if interface[:inet]
398     hosts[:inet6] << interface[:inet6][:address] if interface[:inet6]
399   end
400 end
401
402 package "nftables"
403
404 interfaces = []
405
406 node.interfaces(:role => :external).each do |interface|
407   interfaces << interface[:interface]
408 end
409
410 template "/etc/nftables.conf" do
411   source "nftables.conf.erb"
412   owner "root"
413   group "root"
414   mode "755"
415   variables :interfaces => interfaces, :hosts => hosts
416   notifies :reload, "service[nftables]"
417 end
418
419 directory "/var/lib/nftables" do
420   owner "root"
421   group "root"
422   mode "755"
423 end
424
425 template "/usr/local/bin/nftables" do
426   source "nftables.erb"
427   owner "root"
428   group "root"
429   mode "755"
430 end
431
432 systemd_service "nftables-stop" do
433   action :delete
434   service "nftables"
435   dropin "stop"
436 end
437
438 systemd_service "nftables-chef" do
439   service "nftables"
440   dropin "chef"
441   exec_start "/usr/local/bin/nftables start"
442   exec_reload "/usr/local/bin/nftables reload"
443   exec_stop "/usr/local/bin/nftables stop"
444 end
445
446 if node[:networking][:firewall][:enabled]
447   service "nftables" do
448     action [:enable, :start]
449   end
450 else
451   service "nftables" do
452     action [:disable, :stop]
453   end
454 end
455
456 if node[:networking][:wireguard][:enabled]
457   firewall_rule "accept-wireguard" do
458     action :accept
459     context :incoming
460     protocol :udp
461     source :osm unless node[:roles].include?("gateway")
462     dest_ports "51820"
463     source_ports "51820"
464   end
465 end
466
467 firewall_rule "accept-http" do
468   action :accept
469   context :incoming
470   protocol :tcp
471   dest_ports %w[http https]
472   rate_limit node[:networking][:firewall][:http_rate_limit]
473   connection_limit node[:networking][:firewall][:http_connection_limit]
474 end