Defined Type: wireguard::interface

Defined in:
manifests/interface.pp

Summary

manages a wireguard setup

Overview

}

Examples:

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'},],
}

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',},],
}

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',},],
}

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,

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'],
    }
  ],
}

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'},],
}

Parameters:

  • interface (String[1]) (defaults to: $title)

    the title of the defined resource, will be used for the wg interface

  • ensure (Enum['present', 'absent']) (defaults to: 'present')

    will ensure that the files for the provider will be present or absent

  • input_interface (String[1]) (defaults to: $facts['networking']['primary'])

    ethernet interface where the wireguard packages will enter the system, used for firewall rules

  • manage_firewall (Boolean) (defaults to: $facts['os']['family'] ? { 'Gentoo' => false, default => true)

    if true, a nftables rule will be created

  • dport (Integer[1024, 65000]) (defaults to: Integer(regsubst($title, '^\D+(\d+)$', '\1')))

    destination for firewall rules / where our wg instance will listen on. defaults to the last digits from the title

  • table (Optional[String[1]]) (defaults to: undef)

    Routing table to add routes to

  • firewall_mark (Optional[Integer[0, 4294967295]]) (defaults to: undef)

    netfilter firewall mark to set on outgoing packages from this wireguard interface

  • source_addresses (Array[Stdlib::IP::Address]) (defaults to: [])

    an array of ip addresses from where we receive wireguard connections

  • destination_addresses (Array[Stdlib::IP::Address]) (defaults to: delete_undef_values([$facts['networking']['ip'], $facts['networking']['ip6'],]))

    array of addresses where the remote peer connects to (our local ips), used for firewalling

  • public_key (Optional[String[1]]) (defaults to: undef)

    base64 encoded pubkey from the remote peer

  • endpoint (Optional[String[1]]) (defaults to: undef)

    fqdn:port or ip:port where we connect to

  • addresses (Array[Hash[String,Variant[Stdlib::IP::Address::V4,Stdlib::IP::Address::V6]]]) (defaults to: [])

    different addresses for the systemd-networkd configuration

  • persistent_keepalive (Integer[0, 65535]) (defaults to: 0)

    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

  • description (Optional[String[1]]) (defaults to: undef)

    an optional string that will be added to the wireguard network interface

  • mtu (Optional[Integer[1200, 9000]]) (defaults to: undef)

    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

  • peers (Wireguard::Peers) (defaults to: [])

    is an array of struct (Wireguard::Peers) for multiple peers

  • routes (Array[Hash[String[1], Variant[String[1], Boolean]]]) (defaults to: [])

    different routes for the systemd-networkd configuration

  • private_key (Optional[String[1]]) (defaults to: undef)

    Define private key which should be used for this interface, if not provided a private key will be generated

  • preshared_key (Optional[String[1]]) (defaults to: undef)

    Define preshared key for the remote peer

  • provider (Enum['systemd', 'wgquick']) (defaults to: 'systemd')

    The specific backend to use for this ‘wireguard::interface` resource

  • preup_cmds (Array[String[1]]) (defaults to: [])

    is an array of commands which should run as preup command (only supported by wgquick)

  • postup_cmds (Array[String[1]]) (defaults to: [])

    is an array of commands which should run as preup command (only supported by wgquick)

  • predown_cmds (Array[String[1]]) (defaults to: [])

    is an array of commands which should run as preup command (only supported by wgquick)

  • postdown_cmds (Array[String[1]]) (defaults to: [])

    is an array of commands which should run as preup command (only supported by wgquick)

  • endpoint_port (Optional[Stdlib::Port]) (defaults to: undef)

    optional outgoing port from the other endpoint. Will be used for firewalling. If not set, we will try to parse $endpoint

See Also:

Author:

  • Tim Meusel <tim@bastelfreak.de>

  • Sebastian Rakel <sebastian@devunit.eu>



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
346
347
348
349
350
351
352
353
354
# File 'manifests/interface.pp', line 97

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[String[1]] $table = undef,
  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 = [],
  Optional[Stdlib::Port] $endpoint_port = undef,
) {
  include wireguard

  if empty($peers) and !$public_key {
    warning('peers or public_key have to been set')
  }

  $_endpoint_port = if $endpoint_port {
    $endpoint_port
  } elsif ($endpoint and $endpoint =~ /:(\d+)$/) {
    Integer($1)
  } else {
    undef
  }
  if $manage_firewall {
    $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,
            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::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,
                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'],
              }
            }
          }
        }
      }
    }
  }

  $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,
        table             => $table,
        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")
    }
  }
}