]> git.openstreetmap.org Git - chef.git/blob - cookbooks/networking/recipes/default.rb
Improve naming of wireguard configuration files
[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   file "/etc/systemd/network/wireguard.netdev" do
313     action :delete
314   end
315
316   template "/etc/systemd/network/10-wg0.netdev" do
317     source "wireguard.netdev.erb"
318     owner "root"
319     group "systemd-network"
320     mode "640"
321     notifies :run, "execute[networkctl-delete-wg0]"
322     notifies :run, "execute[networkctl-reload]"
323   end
324
325   file "/etc/systemd/network/wireguard.network" do
326     action :delete
327   end
328
329   template "/etc/systemd/network/10-wg0.network" do
330     source "wireguard.network.erb"
331     owner "root"
332     group "root"
333     mode "644"
334     notifies :run, "execute[networkctl-reload]"
335   end
336
337   execute "networkctl-delete-wg0" do
338     action :nothing
339     command "networkctl delete wg0"
340     only_if { ::File.exist?("/sys/class/net/wg0") }
341   end
342
343   execute "networkctl-reload" do
344     action :nothing
345     command "networkctl reload"
346     not_if { kitchen? }
347   end
348 end
349
350 ohai "reload-hostname" do
351   action :nothing
352   plugin "hostname"
353 end
354
355 execute "hostnamectl-set-hostname" do
356   command "hostnamectl set-hostname #{node[:networking][:hostname]}"
357   notifies :reload, "ohai[reload-hostname]"
358   not_if { kitchen? || node[:hostnamectl][:static_hostname] == node[:networking][:hostname] }
359 end
360
361 template "/etc/hosts" do
362   source "hosts.erb"
363   owner "root"
364   group "root"
365   mode "644"
366   not_if { kitchen? }
367 end
368
369 service "systemd-resolved" do
370   action [:enable, :start]
371 end
372
373 directory "/etc/systemd/resolved.conf.d" do
374   owner "root"
375   group "root"
376   mode "755"
377 end
378
379 template "/etc/systemd/resolved.conf.d/99-chef.conf" do
380   source "resolved.conf.erb"
381   owner "root"
382   group "root"
383   mode "644"
384   notifies :restart, "service[systemd-resolved]", :immediately
385 end
386
387 if node[:filesystem][:by_mountpoint][:"/etc/resolv.conf"]
388   execute "umount-resolve-conf" do
389     command "umount -c /etc/resolv.conf"
390   end
391 end
392
393 link "/etc/resolv.conf" do
394   to "../run/systemd/resolve/stub-resolv.conf"
395 end
396
397 hosts = { :inet => [], :inet6 => [] }
398
399 search(:node, "networking:interfaces").collect do |n|
400   next if n[:fqdn] == node[:fqdn]
401
402   n.interfaces.each do |interface|
403     next unless interface[:role] == "external"
404
405     hosts[:inet] << interface[:inet][:address] if interface[:inet]
406     hosts[:inet6] << interface[:inet6][:address] if interface[:inet6]
407   end
408 end
409
410 package "nftables"
411
412 interfaces = []
413
414 node.interfaces(:role => :external).each do |interface|
415   interfaces << interface[:interface]
416 end
417
418 template "/etc/nftables.conf" do
419   source "nftables.conf.erb"
420   owner "root"
421   group "root"
422   mode "755"
423   variables :interfaces => interfaces, :hosts => hosts
424   notifies :reload, "service[nftables]"
425 end
426
427 directory "/var/lib/nftables" do
428   owner "root"
429   group "root"
430   mode "755"
431 end
432
433 template "/usr/local/bin/nftables" do
434   source "nftables.erb"
435   owner "root"
436   group "root"
437   mode "755"
438 end
439
440 systemd_service "nftables-stop" do
441   action :delete
442   service "nftables"
443   dropin "stop"
444 end
445
446 systemd_service "nftables-chef" do
447   service "nftables"
448   dropin "chef"
449   exec_start "/usr/local/bin/nftables start"
450   exec_reload "/usr/local/bin/nftables reload"
451   exec_stop "/usr/local/bin/nftables stop"
452 end
453
454 if node[:networking][:firewall][:enabled]
455   service "nftables" do
456     action [:enable, :start]
457   end
458 else
459   service "nftables" do
460     action [:disable, :stop]
461   end
462 end
463
464 if node[:networking][:wireguard][:enabled]
465   firewall_rule "accept-wireguard" do
466     action :accept
467     context :incoming
468     protocol :udp
469     source :osm unless node[:roles].include?("gateway")
470     dest_ports "51820"
471     source_ports "51820"
472   end
473 end
474
475 firewall_rule "accept-http" do
476   action :accept
477   context :incoming
478   protocol :tcp
479   dest_ports %w[http https]
480   rate_limit node[:networking][:firewall][:http_rate_limit]
481   connection_limit node[:networking][:firewall][:http_connection_limit]
482 end