]> git.openstreetmap.org Git - chef.git/blob - cookbooks/networking/recipes/default.rb
Fix typo
[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 if node[:networking][:engine] == "netplan"
29   package "netplan.io"
30
31   netplan = {
32     "network" => {
33       "version" => 2,
34       "renderer" => "networkd",
35       "ethernets" => {},
36       "bonds" => {},
37       "vlans" => {}
38     }
39   }
40
41   node[:networking][:interfaces].each do |name, interface|
42     if interface[:interface]
43       if interface[:role] && (role = node[:networking][:roles][interface[:role]])
44         if interface[:inet] && role[:inet]
45           node.default[:networking][:interfaces][name][:inet][:prefix] = role[:inet][:prefix]
46           node.default[:networking][:interfaces][name][:inet][:gateway] = role[:inet][:gateway]
47           node.default[:networking][:interfaces][name][:inet][:routes] = role[:inet][:routes]
48         end
49
50         if interface[:inet6] && role[:inet6]
51           node.default[:networking][:interfaces][name][:inet6][:prefix] = role[:inet6][:prefix]
52           node.default[:networking][:interfaces][name][:inet6][:gateway] = role[:inet6][:gateway]
53           node.default[:networking][:interfaces][name][:inet6][:routes] = role[:inet6][:routes]
54         end
55
56         node.default[:networking][:interfaces][name][:metric] = role[:metric]
57         node.default[:networking][:interfaces][name][:zone] = role[:zone]
58       end
59
60       interface = node[:networking][:interfaces][name]
61
62       deviceplan = if interface[:interface] =~ /^(.*)\.(\d+)$/
63                      netplan["network"]["vlans"][interface[:interface]] ||= {
64                        "id" => Regexp.last_match(2).to_i,
65                        "link" => Regexp.last_match(1),
66                        "accept-ra" => false,
67                        "addresses" => [],
68                        "routes" => []
69                      }
70                    elsif interface[:interface] =~ /^bond\d+$/
71                      netplan["network"]["bonds"][interface[:interface]] ||= {
72                        "accept-ra" => false,
73                        "addresses" => [],
74                        "routes" => []
75                      }
76                    else
77                      netplan["network"]["ethernets"][interface[:interface]] ||= {
78                        "accept-ra" => false,
79                        "addresses" => [],
80                        "routes" => []
81                      }
82                    end
83
84       if interface[:inet]
85         deviceplan["addresses"].push("#{interface[:inet][:address]}/#{interface[:inet][:prefix]}")
86       end
87
88       if interface[:inet6]
89         deviceplan["addresses"].push("#{interface[:inet6][:address]}/#{interface[:inet6][:prefix]}")
90       end
91
92       if interface[:mtu]
93         deviceplan["mtu"] = interface[:mtu]
94       end
95
96       if interface[:bond]
97         deviceplan["interfaces"] = interface[:bond][:slaves].to_a
98
99         deviceplan["parameters"] = {
100           "mode" => interface[:bond][:mode] || "active-backup",
101           "mii-monitor-interval" => interface[:bond][:miimon] || 100,
102           "down-delay" => interface[:bond][:downdelay] || 200,
103           "up-delay" => interface[:bond][:updelay] || 200
104         }
105
106         deviceplan["parameters"]["primary"] = interface[:bond][:slaves].first if deviceplan["parameters"]["mode"] == "active-backup"
107         deviceplan["parameters"]["transmit-hash-policy"] = interface[:bond][:xmithashpolicy] if interface[:bond][:xmithashpolicy]
108         deviceplan["parameters"]["lacp-rate"] = interface[:bond][:lacprate] if interface[:bond][:lacprate]
109       end
110
111       if interface[:inet]
112         if interface[:inet][:gateway] && interface[:inet][:gateway] != interface[:inet][:address]
113           deviceplan["routes"].push(
114             "to" => "0.0.0.0/0",
115             "via" => interface[:inet][:gateway],
116             "metric" => interface[:metric],
117             "on-link" => true
118           )
119         end
120
121         if interface[:inet][:routes]
122           interface[:inet][:routes].each do |to, parameters|
123             next if parameters[:via] == interface[:inet][:address]
124
125             route = {
126               "to" => to
127             }
128
129             route["type"] = parameters[:type] if parameters[:type]
130             route["via"] = parameters[:via] if parameters[:via]
131             route["metric"] = parameters[:metric] if parameters[:metric]
132
133             deviceplan["routes"].push(route)
134           end
135         end
136       end
137
138       if interface[:inet6]
139         if interface[:inet6][:gateway] && interface[:inet6][:gateway] != interface[:inet6][:address]
140           deviceplan["routes"].push(
141             "to" => "::/0",
142             "via" => interface[:inet6][:gateway],
143             "metric" => interface[:metric],
144             "on-link" => true
145           )
146
147           # This ordering relies on systemd-networkd adding routes
148           # in reverse order and will need moving before the previous
149           # route once that is fixed:
150           #
151           # https://github.com/systemd/systemd/issues/5430
152           # https://github.com/systemd/systemd/pull/10938
153           if !IPAddr.new(interface[:inet6][:address]).mask(interface[:inet6][:prefix]).include?(interface[:inet6][:gateway]) &&
154              !IPAddr.new("fe80::/64").include?(interface[:inet6][:gateway])
155             deviceplan["routes"].push(
156               "to" => interface[:inet6][:gateway],
157               "scope" => "link"
158             )
159           end
160         end
161
162         if interface[:inet6][:routes]
163           interface[:inet6][:routes].each do |to, parameters|
164             next if parameters[:via] == interface[:inet6][:address]
165
166             route = {
167               "to" => to
168             }
169
170             route["type"] = parameters[:type] if parameters[:type]
171             route["via"] = parameters[:via] if parameters[:via]
172             route["metric"] = parameters[:metric] if parameters[:metric]
173
174             deviceplan["routes"].push(route)
175           end
176         end
177       end
178     else
179       node.rm(:networking, :interfaces, name)
180     end
181   end
182
183   netplan["network"]["bonds"].each_value do |bond|
184     bond["interfaces"].each do |interface|
185       netplan["network"]["ethernets"][interface] ||= { "accept-ra" => false, "optional" => true }
186     end
187   end
188
189   netplan["network"]["vlans"].each_value do |vlan|
190     unless vlan["link"] =~ /^bond\d+$/
191       netplan["network"]["ethernets"][vlan["link"]] ||= { "accept-ra" => false }
192     end
193   end
194
195   file "/etc/netplan/00-installer-config.yaml" do
196     action :delete
197   end
198
199   file "/etc/netplan/01-netcfg.yaml" do
200     action :delete
201   end
202
203   file "/etc/netplan/50-cloud-init.yaml" do
204     action :delete
205   end
206
207   file "/etc/netplan/99-chef.yaml" do
208     owner "root"
209     group "root"
210     mode "644"
211     content YAML.dump(netplan)
212   end
213 elsif node[:networking][:engine] == "systemd-networkd"
214   file "/etc/netplan/99-chef.yaml" do
215     action :delete
216   end
217
218   package "netplan.io" do
219     action :purge
220   end
221
222   interfaces = node[:networking][:interfaces].collect do |name, interface|
223     [interface[:interface], name]
224   end.to_h
225
226   node[:networking][:interfaces].each do |name, interface|
227     if interface[:interface] =~ /^(.*)\.(\d+)$/
228       vlan_interface = Regexp.last_match(1)
229       vlan_id = Regexp.last_match(2)
230
231       parent = interfaces[vlan_interface] || "vlans_#{vlan_interface}"
232
233       node.default_unless[:networking][:interfaces][parent][:interface] = vlan_interface
234       node.default_unless[:networking][:interfaces][parent][:vlans] = []
235
236       node.default[:networking][:interfaces][parent][:vlans] << vlan_id
237     end
238
239     next unless interface[:role] && (role = node[:networking][:roles][interface[:role]])
240
241     if interface[:inet] && role[:inet]
242       node.default[:networking][:interfaces][name][:inet][:prefix] = role[:inet][:prefix]
243       node.default[:networking][:interfaces][name][:inet][:gateway] = role[:inet][:gateway]
244       node.default[:networking][:interfaces][name][:inet][:routes] = role[:inet][:routes]
245     end
246
247     if interface[:inet6] && role[:inet6]
248       node.default[:networking][:interfaces][name][:inet6][:prefix] = role[:inet6][:prefix]
249       node.default[:networking][:interfaces][name][:inet6][:gateway] = role[:inet6][:gateway]
250       node.default[:networking][:interfaces][name][:inet6][:routes] = role[:inet6][:routes]
251     end
252
253     node.default[:networking][:interfaces][name][:metric] = role[:metric]
254     node.default[:networking][:interfaces][name][:zone] = role[:zone]
255   end
256
257   node[:networking][:interfaces].each do |_, interface|
258     file "/run/systemd/network/10-netplan-#{interface[:interface]}.netdev" do
259       action :delete
260     end
261
262     if interface[:interface] =~ /^.*\.(\d+)$/
263       template "/etc/systemd/network/10-#{interface[:interface]}.netdev" do
264         source "vlan.netdev.erb"
265         owner "root"
266         group "root"
267         mode "644"
268         variables :interface => interface, :vlan => Regexp.last_match(1)
269         notifies :run, "notify_group[networkctl-reload]"
270       end
271     elsif interface[:interface] =~ /^bond\d+$/
272       template "/etc/systemd/network/10-#{interface[:interface]}.netdev" do
273         source "bond.netdev.erb"
274         owner "root"
275         group "root"
276         mode "644"
277         variables :interface => interface
278         notifies :run, "notify_group[networkctl-reload]"
279       end
280
281       interface[:bond][:slaves].each do |slave|
282         file "/run/systemd/network/10-netplan-#{slave}.network" do
283           action :delete
284         end
285
286         template "/etc/systemd/network/10-#{slave}.network" do
287           source "slave.network.erb"
288           owner "root"
289           group "root"
290           mode "644"
291           variables :master => interface, :slave => slave
292           notifies :run, "notify_group[networkctl-reload]"
293         end
294       end
295     end
296
297     file "/run/systemd/network/10-netplan-#{interface[:interface]}.network" do
298       action :delete
299     end
300
301     template "/etc/systemd/network/10-#{interface[:interface]}.network" do
302       source "network.erb"
303       owner "root"
304       group "root"
305       mode "644"
306       variables :interface => interface
307       notifies :run, "notify_group[networkctl-reload]"
308     end
309   end
310
311   service "systemd-networkd" do
312     action [:enable, :start]
313   end
314 end
315
316 package "cloud-init" do
317   action :purge
318 end
319
320 if node[:networking][:wireguard][:enabled]
321   wireguard_id = persistent_token("networking", "wireguard")
322
323   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]}"
324
325   package "wireguard-tools" do
326     compile_time true
327     options "--no-install-recommends"
328   end
329
330   directory "/var/lib/systemd/wireguard" do
331     owner "root"
332     group "systemd-network"
333     mode "750"
334     compile_time true
335   end
336
337   file "/var/lib/systemd/wireguard/private.key" do
338     action :create_if_missing
339     owner "root"
340     group "systemd-network"
341     mode "640"
342     content %x(wg genkey)
343     compile_time true
344   end
345
346   node.default[:networking][:wireguard][:public_key] = %x(wg pubkey < /var/lib/systemd/wireguard/private.key).chomp
347
348   file "/var/lib/systemd/wireguard/preshared.key" do
349     action :create_if_missing
350     owner "root"
351     group "systemd-network"
352     mode "640"
353     content keys["wireguard"]
354   end
355
356   if node[:roles].include?("gateway")
357     search(:node, "roles:gateway") do |gateway|
358       next if gateway.name == node.name
359       next unless gateway[:networking][:wireguard] && gateway[:networking][:wireguard][:enabled]
360
361       allowed_ips = gateway.ipaddresses(:role => :internal).map(&:subnet)
362
363       node.default[:networking][:wireguard][:peers] << {
364         :public_key => gateway[:networking][:wireguard][:public_key],
365         :allowed_ips => allowed_ips,
366         :endpoint => "#{gateway.name}:51820"
367       }
368     end
369
370     search(:node, "roles:prometheus") do |server|
371       allowed_ips = server.ipaddresses(:role => :internal).map(&:subnet)
372
373       if server[:networking][:private_address]
374         allowed_ips << "#{server[:networking][:private_address]}/32"
375       end
376
377       node.default[:networking][:wireguard][:peers] << {
378         :public_key => server[:networking][:wireguard][:public_key],
379         :allowed_ips => allowed_ips,
380         :endpoint => "#{server.name}:51820"
381       }
382     end
383
384     node.default[:networking][:wireguard][:peers] << {
385       :public_key => "7Oj9ufNlgidyH/xDc+aHQKMjJPqTmD/ab13agMh6AxA=",
386       :allowed_ips => "10.0.16.1/32",
387       :endpoint => "gate.compton.nu:51820"
388     }
389
390     # Grant home
391     node.default[:networking][:wireguard][:peers] << {
392       :public_key => "RofATnvlWxP3mt87+QKRXFE5MVxtoCcTsJ+yftZYEE4=",
393       :allowed_ips => "10.89.122.1/32",
394       :endpoint => "gate.firefishy.com:51820"
395     }
396
397     # Grant roaming
398     node.default[:networking][:wireguard][:peers] << {
399       :public_key => "YbUkREE9TAmomqgL/4Fh2e5u2Hh7drN/2o5qg3ndRxg=",
400       :allowed_ips => "10.89.123.1/32",
401       :endpoint => "roaming.firefishy.com:51820"
402     }
403   elsif node[:roles].include?("shenron")
404     search(:node, "roles:gateway") do |gateway|
405       allowed_ips = gateway.ipaddresses(:role => :internal).map(&:subnet)
406
407       node.default[:networking][:wireguard][:peers] << {
408         :public_key => gateway[:networking][:wireguard][:public_key],
409         :allowed_ips => allowed_ips,
410         :endpoint => "#{gateway.name}:51820"
411       }
412     end
413   end
414
415   file "/etc/systemd/network/wireguard.netdev" do
416     action :delete
417   end
418
419   template "/etc/systemd/network/10-wg0.netdev" do
420     source "wireguard.netdev.erb"
421     owner "root"
422     group "systemd-network"
423     mode "640"
424     notifies :run, "execute[networkctl-delete-wg0]"
425     notifies :run, "notify_group[networkctl-reload]"
426   end
427
428   file "/etc/systemd/network/wireguard.network" do
429     action :delete
430   end
431
432   template "/etc/systemd/network/10-wg0.network" do
433     source "wireguard.network.erb"
434     owner "root"
435     group "root"
436     mode "644"
437     notifies :run, "execute[networkctl-reload]"
438   end
439
440   execute "networkctl-delete-wg0" do
441     action :nothing
442     command "networkctl delete wg0"
443     only_if { ::File.exist?("/sys/class/net/wg0") }
444   end
445 end
446
447 notify_group "networkctl-reload"
448
449 execute "networkctl-reload" do
450   action :nothing
451   command "networkctl reload"
452   subscribes :run, "notify_group[networkctl-reload]"
453   not_if { kitchen? && node[:networking][:engine] == "netplan" }
454 end
455
456 ohai "reload-hostname" do
457   action :nothing
458   plugin "hostname"
459 end
460
461 execute "hostnamectl-set-hostname" do
462   command "hostnamectl set-hostname #{node[:networking][:hostname]}"
463   notifies :reload, "ohai[reload-hostname]"
464   not_if { kitchen? || node[:hostnamectl][:static_hostname] == node[:networking][:hostname] }
465 end
466
467 template "/etc/hosts" do
468   source "hosts.erb"
469   owner "root"
470   group "root"
471   mode "644"
472   not_if { kitchen? }
473 end
474
475 service "systemd-resolved" do
476   action [:enable, :start]
477 end
478
479 directory "/etc/systemd/resolved.conf.d" do
480   owner "root"
481   group "root"
482   mode "755"
483 end
484
485 template "/etc/systemd/resolved.conf.d/99-chef.conf" do
486   source "resolved.conf.erb"
487   owner "root"
488   group "root"
489   mode "644"
490   notifies :restart, "service[systemd-resolved]", :immediately
491 end
492
493 if node[:filesystem][:by_mountpoint][:"/etc/resolv.conf"]
494   execute "umount-resolve-conf" do
495     command "umount -c /etc/resolv.conf"
496   end
497 end
498
499 link "/etc/resolv.conf" do
500   to "../run/systemd/resolve/stub-resolv.conf"
501 end
502
503 hosts = { :inet => [], :inet6 => [] }
504
505 search(:node, "networking:interfaces").collect do |n|
506   next if n[:fqdn] == node[:fqdn]
507
508   n.interfaces.each do |interface|
509     next unless interface[:role] == "external"
510
511     hosts[:inet] << interface[:inet][:address] if interface[:inet]
512     hosts[:inet6] << interface[:inet6][:address] if interface[:inet6]
513   end
514 end
515
516 package "nftables"
517
518 interfaces = []
519
520 node.interfaces(:role => :external).each do |interface|
521   interfaces << interface[:interface]
522 end
523
524 template "/etc/nftables.conf" do
525   source "nftables.conf.erb"
526   owner "root"
527   group "root"
528   mode "755"
529   variables :interfaces => interfaces, :hosts => hosts
530   notifies :reload, "service[nftables]"
531 end
532
533 directory "/var/lib/nftables" do
534   owner "root"
535   group "root"
536   mode "755"
537 end
538
539 template "/usr/local/bin/nftables" do
540   source "nftables.erb"
541   owner "root"
542   group "root"
543   mode "755"
544 end
545
546 systemd_service "nftables-stop" do
547   action :delete
548   service "nftables"
549   dropin "stop"
550 end
551
552 systemd_service "nftables-chef" do
553   service "nftables"
554   dropin "chef"
555   exec_start "/usr/local/bin/nftables start"
556   exec_reload "/usr/local/bin/nftables reload"
557   exec_stop "/usr/local/bin/nftables stop"
558 end
559
560 if node[:networking][:firewall][:enabled]
561   service "nftables" do
562     action [:enable, :start]
563   end
564 else
565   service "nftables" do
566     action [:disable, :stop]
567   end
568 end
569
570 if node[:networking][:wireguard][:enabled]
571   firewall_rule "accept-wireguard" do
572     action :accept
573     context :incoming
574     protocol :udp
575     source :osm unless node[:roles].include?("gateway")
576     dest_ports "51820"
577     source_ports "51820"
578   end
579 end
580
581 firewall_rule "accept-http" do
582   action :accept
583   context :incoming
584   protocol :tcp
585   dest_ports %w[http https]
586   rate_limit node[:networking][:firewall][:http_rate_limit]
587   connection_limit node[:networking][:firewall][:http_connection_limit]
588 end