/
interface.pp
345 lines (333 loc) · 14.7 KB
/
interface.pp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#
# @summary manages a wireguard setup
#
# @param interface the title of the defined resource, will be used for the wg interface
# @param ensure will ensure that the files for the provider will be present or absent
# @param input_interface ethernet interface where the wireguard packages will enter the system, used for firewall rules
# @param manage_firewall if true, a nftables rule will be created
# @param dport destination for firewall rules / where our wg instance will listen on. defaults to the last digits from the title
# @param firewall_mark netfilter firewall mark to set on outgoing packages from this wireguard interface
# @param source_addresses an array of ip addresses from where we receive wireguard connections
# @param destination_addresses array of addresses where the remote peer connects to (our local ips), used for firewalling
# @param public_key base64 encoded pubkey from the remote peer
# @param endpoint fqdn:port or ip:port where we connect to
# @param addresses different addresses for the systemd-networkd configuration
# @param persistent_keepalive is set to 1 or greater, that's the interval in seconds wireguard sends a keepalive to the other peer(s). Useful if the sender is behind a NAT gateway or has a dynamic ip address
# @param description an optional string that will be added to the wireguard network interface
# @param mtu configure the MTU (maximum transision unit) for the wireguard tunnel. By default linux will figure this out. You might need to lower it if you're connection through a DSL line. MTU needs to be equal on both tunnel endpoints
# @param peers is an array of struct (Wireguard::Peers) for multiple peers
# @param routes different routes for the systemd-networkd configuration
# @param private_key Define private key which should be used for this interface, if not provided a private key will be generated
# @param preshared_key Define preshared key for the remote peer
# @param provider The specific backend to use for this `wireguard::interface` resource
# @param preup_cmds is an array of commands which should run as preup command (only supported by wgquick)
# @param postup_cmds is an array of commands which should run as preup command (only supported by wgquick)
# @param predown_cmds is an array of commands which should run as preup command (only supported by wgquick)
# @param postdown_cmds is an array of commands which should run as preup command (only supported by wgquick)
#
# @author Tim Meusel <tim@bastelfreak.de>
# @author Sebastian Rakel <sebastian@devunit.eu>
#
# @see https://www.freedesktop.org/software/systemd/man/systemd.netdev.html#%5BWireGuardPeer%5D%20Section%20Options
#
# @example Peer with one node and setup dualstack firewall rules
# wireguard::interface {'as2273':
# source_addresses => ['2003:4f8:c17:4cf::1', '149.9.255.4'],
# public_key => 'BcxLll1BVxGQ5DeijroesjroiesjrjvX+EBhS4vcDn0R0=',
# endpoint => 'wg.example.com:53668',
# addresses => [{'Address' => '192.168.123.6/30',},{'Address' => 'fe80::beef:1/64'},],
# }
#
# @example Peer with one node and setup dualstack firewall rules with peers in a different layer2
# wireguard::interface {'as2273':
# source_addresses => ['2003:4f8:c17:4cf::1', '149.9.255.4'],
# public_key => 'BcxLll1BVxGQ5DeijroesjroiesjrjvX+EBhS4vcDn0R0=',
# endpoint => 'wg.example.com:53668',
# addresses => [{'Address' => '192.168.218.87/32', 'Peer' => '172.20.53.97/32'}, {'Address' => 'fe80::ade1/64',},],
# }
#
# @example Create a passive wireguard interface that listens for incoming connections. Useful when the other side has a dynamic IP / is behind NAT
# wireguard::interface {'as2273':
# source_addresses => ['2003:4f8:c17:4cf::1', '149.9.255.4'],
# public_key => 'BcxLll1BVxGQ5DeijroesjroiesjrjvX+EBhS4vcDn0R0=',
# dport => 53668,
# addresses => [{'Address' => '192.168.218.87/32', 'Peer' => '172.20.53.97/32'}, {'Address' => 'fe80::ade1/64',},],
# }
#
# @example create a wireguard interface behind a DSL line with changing IP with lowered MTU
# wireguard::interface {'as3668-2':
# source_addresses => ['144.76.249.220', '2a01:4f8:171:1152::12'],
# public_key => 'Tci/bHoPCjTpYv8bw17xQ7P4OdqzGpEN+NDueNjUvBA=',
# endpoint => 'router02.bastelfreak.org:1338',
# dport => 1338,
# input_interface => $facts['networking']['primary'],
# addresses => [{'Address' => '169.254.0.10/32', 'Peer' =>'169.254.0.9/32'},{'Address' => 'fe80::beef:f/64'},],
# destination_addresses => [],
# persistent_keepalive => 5,
# mtu => 1412,
# }
#
# @example create a wireguard interface with multiple peers where one uses a preshared key
# wireguard::interface { 'wg0':
# dport => 1338,
# addresses => [{'Address' => '192.0.2.1/24'}],
# peers => [
# {
# public_key => 'foo==',
# preshared_key => '/22q9I+RpWRsU+zshW8skv1p00TvnEE6fTvPJuI2Cp4=',
# allowed_ips => ['192.0.2.2'],
# },
# {
# public_key => 'bar==',
# allowed_ips => ['192.0.2.3'],
# }
# ],
# }
#
# @example create two sides of a session using the public key from the other side
# wireguard::interface { 'wg0':
# source_addresses => ['2003:4f8:c17:4cf::1', '149.9.255.4'],
# public_key => $facts['wireguard_pubkeys']['nodeB'],
# endpoint => 'nodeB.example.com:53668',
# addresses => [{'Address' => '192.168.123.6/30',},{'Address' => 'fe80::beef:1/64'},],
# }
#
define wireguard::interface (
Enum['present', 'absent'] $ensure = 'present',
Wireguard::Peers $peers = [],
Optional[String[1]] $endpoint = undef,
Integer[0, 65535] $persistent_keepalive = 0,
Array[Stdlib::IP::Address] $destination_addresses = delete_undef_values([$facts['networking']['ip'], $facts['networking']['ip6'],]),
String[1] $interface = $title,
Integer[1024, 65000] $dport = Integer(regsubst($title, '^\D+(\d+)$', '\1')),
Optional[Integer[0, 4294967295]] $firewall_mark = undef,
String[1] $input_interface = $facts['networking']['primary'],
Boolean $manage_firewall = $facts['os']['family'] ? { 'Gentoo' => false, default => true },
Array[Stdlib::IP::Address] $source_addresses = [],
Array[Hash[String,Variant[Stdlib::IP::Address::V4,Stdlib::IP::Address::V6]]] $addresses = [],
Optional[String[1]] $description = undef,
Optional[Integer[1200, 9000]] $mtu = undef,
Optional[String[1]] $public_key = undef,
Array[Hash[String[1], Variant[String[1], Boolean]]] $routes = [],
Optional[String[1]] $private_key = undef,
Optional[String[1]] $preshared_key = undef,
Enum['systemd', 'wgquick'] $provider = 'systemd',
Array[String[1]] $preup_cmds = [],
Array[String[1]] $postup_cmds = [],
Array[String[1]] $predown_cmds = [],
Array[String[1]] $postdown_cmds = [],
) {
include wireguard
if empty($peers) and !$public_key {
warning('peers or public_key have to been set')
}
if $manage_firewall {
# ToDo: It would be nice if this would be a parameter
if $endpoint =~ /:(\d+)$/ {
$endpoint_port = Integer($1)
} else {
$endpoint_port = undef
}
$source_addresses.each |$index1, $saddr| {
if $saddr =~ Stdlib::IP::Address::V4 {
if empty($destination_addresses) {
nftables::simplerule { "allow_in_wg_${interface}-${index1}":
action => 'accept',
comment => "Allow traffic from interface ${input_interface} from IP ${saddr} for wireguard tunnel ${interface}",
dport => $dport,
sport => $endpoint_port,
proto => 'udp',
saddr => $saddr,
iifname => $input_interface,
notify => Service['systemd-networkd'],
}
nftables::simplerule { "allow_out_wg_${interface}-${index1}":
action => 'accept',
comment => "Allow traffic out interface ${input_interface} to IP ${saddr} for wireguard tunnel ${interface}",
dport => $endpoint_port,
sport => $dport,
proto => 'udp',
daddr => $saddr,
oifname => $input_interface,
chain => 'default_out',
notify => Service['systemd-networkd'],
}
} else {
$destination_addresses.each |$index2, $_daddr| {
if $_daddr =~ Stdlib::IP::Address::V4 {
nftables::simplerule { "allow_in_wg_${interface}-${index1}${index2}":
action => 'accept',
comment => "Allow traffic from interface ${input_interface} from IP ${saddr} for wireguard tunnel ${interface}",
dport => $dport,
sport => $endpoint_port,
proto => 'udp',
daddr => $_daddr,
saddr => $saddr,
iifname => $input_interface,
notify => Service['systemd-networkd'],
}
nftables::simplerule { "allow_out_wg_${interface}-${index1}${index2}":
action => 'accept',
comment => "Allow traffic out interface ${input_interface} to IP ${saddr} for wireguard tunnel ${interface}",
dport => $endpoint_port,
sport => $dport,
proto => 'udp',
daddr => $saddr,
saddr => $_daddr,
oifname => $input_interface,
chain => 'default_out',
notify => Service['systemd-networkd'],
}
}
}
}
} else {
if empty($destination_addresses) {
nftables::simplerule { "allow_in_wg_${interface}-${index1}":
action => 'accept',
comment => "Allow traffic from interface ${input_interface} from IP ${saddr} for wireguard tunnel ${interface}",
dport => $dport,
proto => 'udp',
saddr => $saddr,
iifname => $input_interface,
notify => Service['systemd-networkd'],
}
nftables::simplerule { "allow_out_wg_${interface}-${index1}":
action => 'accept',
comment => "Allow traffic out interface ${input_interface} to IP ${saddr} for wireguard tunnel ${interface}",
dport => $endpoint_port,
sport => $dport,
proto => 'udp',
daddr => $saddr,
oifname => $input_interface,
chain => 'default_out',
notify => Service['systemd-networkd'],
}
} else {
$destination_addresses.each |$index2, $_daddr| {
if $_daddr =~ Stdlib::IP::Address::V6 {
nftables::simplerule { "allow_in_wg_${interface}-${index1}${index2}":
action => 'accept',
comment => "Allow traffic from interface ${input_interface} from IP ${saddr} for wireguard tunnel ${interface}",
dport => $dport,
proto => 'udp',
daddr => $_daddr,
saddr => $saddr,
iifname => $input_interface,
notify => Service['systemd-networkd'],
}
nftables::simplerule { "allow_out_wg_${interface}-${index1}${index2}":
action => 'accept',
comment => "Allow traffic out interface ${input_interface} to IP ${saddr} for wireguard tunnel ${interface}",
dport => $endpoint_port,
sport => $dport,
proto => 'udp',
daddr => $saddr,
saddr => $_daddr,
oifname => $input_interface,
chain => 'default_out',
notify => Service['systemd-networkd'],
}
}
}
}
}
}
}
$private_key_path = "${wireguard::config_directory}/${interface}"
if $private_key {
file { $private_key_path:
ensure => 'file',
content => $private_key,
owner => 'root',
group => 'systemd-network',
mode => '0640',
notify => Exec["generate public key ${interface}"],
}
} else {
exec { "generate private key ${interface}":
command => "wg genkey > ${interface}",
cwd => $wireguard::config_directory,
creates => $private_key_path,
path => '/usr/bin',
before => File[$private_key_path],
notify => Exec["generate public key ${interface}"],
}
file { $private_key_path:
ensure => 'file',
owner => 'root',
group => 'systemd-network',
mode => '0640',
}
}
exec { "generate public key ${interface}":
command => "wg pubkey < ${interface} > ${interface}.pub",
cwd => $wireguard::config_directory,
creates => "${wireguard::config_directory}/${interface}.pub",
path => '/usr/bin',
}
file { "${wireguard::config_directory}/${interface}.pub":
ensure => 'file',
owner => 'root',
group => 'root',
mode => '0600',
require => Exec["generate public key ${interface}"],
}
if $public_key {
$peer = [{
public_key => $public_key,
endpoint => $endpoint,
preshared_key => $preshared_key,
persistent_keepalive => $persistent_keepalive,
}]
} else {
$peer = []
}
case $provider {
'systemd': {
if !empty($preup_cmds) {
warning('PreUp commands are not supported by systemd-networkd')
}
if !empty($postup_cmds) {
warning('PostUp commands are not supported by systemd-networkd')
}
if !empty($predown_cmds) {
warning('PreDown commands are not supported by systemd-networkd')
}
if !empty($postdown_cmds) {
warning('PostDown commands are not supported by systemd-networkd')
}
wireguard::provider::systemd { $interface :
ensure => $ensure,
interface => $interface,
peers => $peers + $peer,
dport => $dport,
firewall_mark => $firewall_mark,
addresses => $addresses,
description => $description,
mtu => $mtu,
routes => $routes,
default_allowlist => $wireguard::default_allowlist,
}
}
'wgquick': {
wireguard::provider::wgquick { $interface :
ensure => $ensure,
interface => $interface,
peers => $peers + $peer,
dport => $dport,
firewall_mark => $firewall_mark,
addresses => $addresses,
preup_cmds => $preup_cmds,
postup_cmds => $postup_cmds,
predown_cmds => $predown_cmds,
postdown_cmds => $postdown_cmds,
mtu => $mtu,
default_allowlist => $wireguard::default_allowlist,
}
}
default: {
fail("provider ${provider} not supported")
}
}
}