Skip to content

Commit

Permalink
Switch from voxpupuli/ferm to voxpupuli/nftables for firewalling
Browse files Browse the repository at this point in the history
  • Loading branch information
bastelfreak committed Dec 25, 2023
1 parent a69ae4a commit 50df59b
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ fixtures:
repositories:
systemd: https://github.com/voxpupuli/puppet-systemd.git
stdlib: https://github.com/puppetlabs/puppetlabs-stdlib.git
ferm: https://github.com/voxpupuli/puppet-ferm.git
nftables: https://github.com/voxpupuli/puppet-nftables.git
concat: https://github.com/puppetlabs/puppetlabs-concat.git
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ Puppet module to configure wireguard through systemd-networkd configs

## Setup

The module can create firewall rules with [voxpupuli/ferm](https://github.com/voxpupuli/puppet-ferm#puppet-ferm).
The module can create firewall rules with [voxpupuli/nftables](https://github.com/voxpupuli/puppet-nftables?tab=readme-ov-file#nftables-puppet-module).
This is enabled by default but can be disabled by setting the `manage_firewall`
parameter to false in the `wireguard::interface` defined resource. You need to
have the `ferm` class in your catalog to use the feature.
have the `nftables` class in your catalog to use the feature (Version 3.6.0 or
newer).

This module can uses [systemd-networkd](https://www.freedesktop.org/software/systemd/man/systemd-networkd.html) or [wg-quick](https://manpages.debian.org/wg-quick) to
**Version 3 and older of the module use voxpupuli/ferm to manage firewall rules**

This module can use [systemd-networkd](https://www.freedesktop.org/software/systemd/man/systemd-networkd.html) or [wg-quick](https://manpages.debian.org/wg-quick) to
configure tunnels. For the former, you need to have a systemd-networkd
service resource in your catalog. We recommend [voxpupuli/systemd](https://github.com/voxpupuli/puppet-systemd#systemd)
with `manage_networkd` set to true. You do not need to configure your
Expand Down
2 changes: 1 addition & 1 deletion REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ Default value: `$facts['networking']['primary']`

Data type: `Boolean`

if true, a ferm rule will be created
if true, a nftables rule will be created

Default value: `true`

Expand Down
74 changes: 64 additions & 10 deletions manifests/interface.pp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# @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 ferm rule will be created
# @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
Expand Down Expand Up @@ -128,15 +128,69 @@
true => undef,
default => $destination_addresses,
}
ferm::rule { "allow_wg_${interface}":
action => 'ACCEPT',
chain => 'INPUT',
proto => 'udp',
dport => $dport,
interface => $input_interface,
saddr => $source_addresses,
daddr => $daddr,
notify => Service['systemd-networkd'],
# 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 {
$daddr.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 => $_daddr,
saddr => $saddr,
oifname => $input_interface,
chain => 'default_out',
notify => Service['systemd-networkd'],
}
}
}
} else {
$daddr.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'],
}
}
}
}
}
}

Expand Down
33 changes: 13 additions & 20 deletions spec/defines/interface_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").without_content(%r{Address}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").without_content(%r{Description}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").without_content(%r{MTUBytes}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
it { is_expected.to have_nftables__simplerule_resource_count(0) }
end

context 'with required params (public_key) and without firewall rules and with PersistentKeepalive=5' do
Expand Down Expand Up @@ -105,12 +105,8 @@
end

context 'with required params and with firewall rules' do
# we need to set configfile/configdirectory because the ferm module doesn't provide defaults for all OSes we test against
let :pre_condition do
'class{"ferm":
configfile => "/etc/ferm.conf",
configdirectory => "/etc/ferm.d/"
}
'include nftables
class {"systemd":
manage_networkd => true
}'
Expand All @@ -135,7 +131,7 @@ class {"systemd":
it { is_expected.to contain_systemd__network("#{title}.netdev") }

Check failure on line 131 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 8 (Ruby 3.2)

wireguard::interface on gentoo-2-x86_64 with required params and with firewall rules is expected to contain Systemd::Network[as1234.netdev] Failure/Error: it { is_expected.to contain_systemd__network("#{title}.netdev") } Puppet::PreformattedError: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az525-405.gmvlfiqlijledoifuxan3ve3oh.dx.internal.cloudapp.net

Check failure on line 131 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 7 (Ruby 2.7)

wireguard::interface on gentoo-2-x86_64 with required params and with firewall rules is expected to contain Systemd::Network[as1234.netdev] Failure/Error: it { is_expected.to contain_systemd__network("#{title}.netdev") } Puppet::PreformattedError: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az984-80.r1dl3knd25iuznqlwxibdlge2f.cx.internal.cloudapp.net
it { is_expected.to contain_systemd__network("#{title}.network") }

Check failure on line 132 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 8 (Ruby 3.2)

wireguard::interface on gentoo-2-x86_64 with required params and with firewall rules is expected to contain Systemd::Network[as1234.network] Failure/Error: it { is_expected.to contain_systemd__network("#{title}.network") } Puppet::PreformattedError: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az525-405.gmvlfiqlijledoifuxan3ve3oh.dx.internal.cloudapp.net

Check failure on line 132 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 7 (Ruby 2.7)

wireguard::interface on gentoo-2-x86_64 with required params and with firewall rules is expected to contain Systemd::Network[as1234.network] Failure/Error: it { is_expected.to contain_systemd__network("#{title}.network") } Puppet::PreformattedError: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az984-80.r1dl3knd25iuznqlwxibdlge2f.cx.internal.cloudapp.net
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").without_content(%r{Address}) }

Check failure on line 133 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 8 (Ruby 3.2)

wireguard::interface on gentoo-2-x86_64 with required params and with firewall rules is expected to contain File[/etc/systemd/network/as1234.network] with content !~ /Address/ Failure/Error: it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").without_content(%r{Address}) } Puppet::PreformattedError: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az525-405.gmvlfiqlijledoifuxan3ve3oh.dx.internal.cloudapp.net

Check failure on line 133 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 7 (Ruby 2.7)

wireguard::interface on gentoo-2-x86_64 with required params and with firewall rules is expected to contain File[/etc/systemd/network/as1234.network] with content !~ /Address/ Failure/Error: it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").without_content(%r{Address}) } Puppet::PreformattedError: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az984-80.r1dl3knd25iuznqlwxibdlge2f.cx.internal.cloudapp.net
it { is_expected.to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.to contain_ferm__rule("allow_wg_#{title}") }
end

context 'with required params and without firewall rules and with configured addresses' do
Expand Down Expand Up @@ -163,15 +159,12 @@ class {"systemd":
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Address=192.168.218.87/32}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Peer=172.20.53.97/32}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Address=fe80::ade1/64}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'with empty destintion_addresses' do
let :pre_condition do
'class{"ferm":
configfile => "/etc/ferm.conf",
configdirectory => "/etc/ferm.d/"
}
'include nftables
class {"systemd":
manage_networkd => true
}'
Expand All @@ -186,7 +179,7 @@ class {"systemd":
end

it { is_expected.to compile.with_all_deps }

Check failure on line 181 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 8 (Ruby 3.2)

wireguard::interface on gentoo-2-x86_64 with empty destintion_addresses is expected to compile into a catalogue without dependency cycles Failure/Error: it { is_expected.to compile.with_all_deps } error during compilation: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az525-405.gmvlfiqlijledoifuxan3ve3oh.dx.internal.cloudapp.net

Check failure on line 181 in spec/defines/interface_spec.rb

View workflow job for this annotation

GitHub Actions / Puppet / 7 (Ruby 2.7)

wireguard::interface on gentoo-2-x86_64 with empty destintion_addresses is expected to compile into a catalogue without dependency cycles Failure/Error: it { is_expected.to compile.with_all_deps } error during compilation: Evaluation Error: Error while evaluating a Function Call, Class[Nftables]: expects a value for parameter 'echo' expects a value for parameter 'nft_path' (line: 2, column: 1) on node fv-az984-80.r1dl3knd25iuznqlwxibdlge2f.cx.internal.cloudapp.net
it { is_expected.to contain_ferm__rule("allow_wg_#{title}").without_daddr }
# it { is_expected.to contain_ferm__rule("allow_wg_#{title}").without_daddr }
end

context 'with description' do
Expand Down Expand Up @@ -297,7 +290,7 @@ class {"systemd":
it { is_expected.to contain_systemd__network("#{title}.network") }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.netdev").with_content(expected_netdev_content) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(expected_network_content) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'with required params and defined private key and without firewall rules and with configured addresses' do
Expand Down Expand Up @@ -326,7 +319,7 @@ class {"systemd":
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Address=192.168.218.87/32}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Peer=172.20.53.97/32}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Address=fe80::ade1/64}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'wgquick with required params (public_key) and without firewall rules' do
Expand All @@ -349,7 +342,7 @@ class {"systemd":
it { is_expected.to contain_file("/etc/wireguard/#{title}.pub") }
it { is_expected.to contain_file("/etc/wireguard/#{title}") }
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf") }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'with required params and defined private key and without firewall rules and with configured addresses with dns' do
Expand Down Expand Up @@ -379,7 +372,7 @@ class {"systemd":
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{DNS=192.168.218.1}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Peer=172.20.53.97/32}) }
it { is_expected.to contain_file("/etc/systemd/network/#{title}.network").with_content(%r{Address=fe80::ade1/64}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'wgquick with required params (public_key) and an address entry with dns also without firewall rules' do
Expand All @@ -404,7 +397,7 @@ class {"systemd":
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{[Interface]}) } # rubocop:disable Lint/DuplicateRegexpCharacterClassElement
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{Address=192.168.218.87/32}) }
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{DNS=192.168.218.1}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'wgquick with postup and predown commands and without firewall' do
Expand Down Expand Up @@ -436,7 +429,7 @@ class {"systemd":
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{Address=192.168.218.87/32}) }
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{PostUp=resolvectl dns %i 10.34.3.1; resolvectl domain %i "~hello"}) }
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{PreDown=resolvectl revert %i}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'wgquick with mtu and without firewall' do
Expand All @@ -462,7 +455,7 @@ class {"systemd":
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{[Interface]}) } # rubocop:disable Lint/DuplicateRegexpCharacterClassElement
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{Address=192.168.218.87/32}) }
it { is_expected.to contain_file("/etc/wireguard/#{title}.conf").with_content(%r{MTU=1280}) }
it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
# it { is_expected.not_to contain_ferm__rule("allow_wg_#{title}") }
end

context 'with required params and firewall mark and without firewall rules' do
Expand Down

0 comments on commit 50df59b

Please sign in to comment.