diff --git a/lib/facter/fw_builder_is_docker.rb b/lib/facter/fw_builder_is_docker.rb new file mode 100644 index 0000000000000000000000000000000000000000..6388b74e49a41b2117f45a65f52a6bd9316270d5 --- /dev/null +++ b/lib/facter/fw_builder_is_docker.rb @@ -0,0 +1,9 @@ +Facter.add(:fw_builder_is_docker) do + setcode do + if Facter::Util::Resolution.which('docker') + true + else + false + end + end +end diff --git a/manifests/chains.pp b/manifests/chains.pp new file mode 100644 index 0000000000000000000000000000000000000000..ff81dcdc7a1f32236687ccf901c1c5692b8fbe48 --- /dev/null +++ b/manifests/chains.pp @@ -0,0 +1,88 @@ +# == Class: fw_builder::chains +# +# Pre IPtables allows several icmp type, loopback connection +# either on IPv6 and IPv4 +# +# This class opens the firewall to Geant specific servers +# +# === Parameters +# +# === Requires +# +# === Examples +# +class fw_builder::chains ( + $ipv4_enable = $fw_builder::params::ipv4_enable, + $ipv6_enable = $fw_builder::params::ipv6_enable +) { + + assert_private() + + $fw_builder::ip_proto_array.each | String $provider | { + $trusted_net = $provider ? { + 'iptables' => 'trusted_networks_v4', + 'ip6tables' => 'trusted_networks_v6', + } + $icmp_proto = $provider ? { + 'iptables' => 'icmp', + 'ip6tables' => 'ipv6-icmp', + } + firewall { "001 accept all inbound to localhost for ${provider}": + chain => 'INPUT', + proto => all, + iniface => 'lo', + action => accept, + provider => $provider; + } + firewall { + default: + chain => 'INPUT', + action => accept, + provider => 'iptables'; + "010 accept all icmp for ${provider}": + proto => $icmp_proto; + "003 accept inbound related established rules for ${provider}": + proto => all, + state => ['RELATED', 'ESTABLISHED']; + } + + firewall { + default: + chain => 'INPUT', + jump => 'INPUT_public', + state => ['NEW'], + provider => $provider; + "090 UDP INPUT_public for all public services for ${provider}": + proto => 'udp'; + "090 TCP INPUT_public for all public services for ${provider}": + proto => 'tcp'; + } + firewall { "095 INPUT_trust this is for all ip ranges (mostly internal) for ${provider}": + chain => 'INPUT', + proto => all, + state => ['NEW'], + jump => 'INPUT_trust', + ipset => "${trusted_net} src", + provider => $provider; + } + + } + + if ($ipv4_enable) { + ['udp', 'tcp', 'trust', 'public'].each | $chain | { + firewallchain { "INPUT_${chain}:filter:IPv4": + ensure => present; + } + } + } + + if ($ipv6_enable) { + ['udp', 'tcp', 'trust', 'public'].each | $chain | { + firewallchain { "INPUT_${chain}:filter:IPv6": + ensure => present, + } + } + } + +} +# vim:ts=2:sw=2 diff --git a/manifests/docker.pp b/manifests/docker.pp new file mode 100644 index 0000000000000000000000000000000000000000..2a13aca0e92d621068f360b048167f1b65324ac9 --- /dev/null +++ b/manifests/docker.pp @@ -0,0 +1,63 @@ +# == Class: fw_builder::docker +# +# Pre IPtables allows several icmp type, loopback connection +# either on IPv6 and IPv4 +# +# This class opens the firewall to Geant specific servers +# +# === Parameters +# +# === Requires +# +# === Examples +# +# === ToDo +# +# ADD SUPPORT FOR IPv6 +# +class fw_builder::docker { + + assert_private() + + + firewallchain { ['INPUT:filter:IPv4', 'OUTPUT:filter:IPv4']: + purge => true, + ignore => ['docker', 'br-', 'cali-', 'KUBE'], + } + + firewallchain { 'FORWARD:filter:IPv4': + purge => true, + ignore => ['docker', 'br-', 'cali-', 'KUBE'], + } + + firewallchain { ['DOCKER:nat:IPv4', 'DOCKER:filter:IPv4']: + purge => false, + } + + firewallchain { 'POSTROUTING:nat:IPv4': + purge => false, + } + + firewallchain { [ + 'INPUT:nat:IPv4', 'PREROUTING:nat:IPv4', + 'OUTPUT:nat:IPv4', 'PREROUTING:mangle:IPv4', + 'POSTROUTING:mangle:IPv4', 'INPUT:mangle:IPv4', + 'FORWARD:mangle:IPv4', 'OUTPUT:mangle:IPv4', + 'OUTPUT:raw:IPv4', 'PREROUTING:raw:IPv4' + ]: + purge => true, + ignore => ['DOCKER', 'cali-', 'KUBE'], + } + + # this is is for kube / cali + firewallchain { [ + 'cali-PREROUTING:mangle:IPv4', 'cali-failsafe-in:mangle:IPv4', + 'cali-from-host-endpoint:mangle:IPv4', 'cali-failsafe-in:raw:IPv4', + 'cali-failsafe-out:raw:IPv4', 'cali-from-host-endpoint:raw:IPv4', + 'cali-to-host-endpoint:raw:IPv4', 'KUBE-SERVICES:filter:IPv4' + ]: + purge => false, + } + +} +# vim:ts=2:sw=2 diff --git a/manifests/init.pp b/manifests/init.pp index dad27e829e53f357322e69b9b619afaa253c3896..47077b4cc08bb803393470dae390d40ac69f9091 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,10 +1,105 @@ # == Class: fw_builder # +# == Parameters +# +# [*trusted_networks*] Fw_builder::Iplist +# Array of ipv4/ipv6 CIDR/Address +# +# [*purge_rules*] Boolean +# Purge rules not defined via Puppet +# +# [*manage_docker*] Boolean +# If purge rules is set to true, avoid purging rules set by Docker +# +# [*ipv4_enable*] Boolean +# enable iptables provider +# +# [*ipv6_enable*] Boolean +# enable ip6tables provider +# +# [*logging*] Boolean +# enable logging +# +# [*log_rotation_days*] Integer +# define log retention in days +# +# [*ipset_package_ensure*] String +# ipset version +# +# [*limit*] Variant[Undef, String] +# define limit for RST and Dropped connection on post.pp +# # == Authors: # # Pete Pedersen<pete.pedersen@geant.org> # Massimiliano Adamo<massimiliano.adamo@geant.org> # -class fw_builder { - # resources +class fw_builder ( + Fw_builder::Iplist $trusted_networks, + Boolean $manage_docker = $fw_builder::params::manage_docker, + Boolean $ipv4_enable = $fw_builder::params::ipv4_enable, + Boolean $ipv6_enable = $fw_builder::params::ipv6_enable, + Boolean $logging = $fw_builder::params::logging, + Boolean $purge_rules = $fw_builder::params::purge_rules, + Integer $log_rotation_days = $fw_builder::params::log_rotation_days, + Optional[String] $limit = $fw_builder::params::limit, + $ipset_package_ensure = $fw_builder::params::ipset_package_ensure +) { + + if ! ($purge_rules) and ($manage_docker) { + fail('cannot set purge_rules to false and manage_docker to true') + } elsif ! ($ipv4_enable) and ! ($ipv6_enable) { + fail('you cannot disable ipv4 and ipv6 at the same time') + } + + if ($ipv4_enable) and ($ipv6_enable) { + $ip_proto_array = ['ip6tables', 'iptables'] + } elsif ($ipv4_enable) and ! ($ipv6_enable) { + $ip_proto_array = ['iptables'] + } elsif ! ($ipv4_enable) and ($ipv6_enable) { + $ip_proto_array = ['iptables'] + } + + anchor { 'fw_builder::begin': } + -> class { 'firewall':; } + -> class { 'fw_builder::ipset':; } + -> class { 'fw_builder::chains':; } + -> class { 'fw_builder::post':; } + -> anchor { 'fw_builder::end': } + + include fw_builder::logrotate + + if ($purge_rules) { + if ($facts['fw_builder_is_docker']) and ($manage_docker) { + echo { 'Docker detected': + message => 'not purging iptables rules set by docker'; + } + resources { 'firewallchain': + purge => false; + } + class { 'fw_builder::docker': + before => Class['fw_builder::post'], + require => Class['fw_builder::ipset']; + } + } else { + if ($ipv4_enable) { + firewallchain { 'FORWARD:filter:IPv4': + ensure => present, + policy => drop, + purge => true; + } + } + if ($ipv6_enable) { + firewallchain { 'FORWARD:filter:IPv6': + ensure => present, + policy => drop, + purge => true; + } + } + resources { 'firewall': + purge => true; + } + } + } + } diff --git a/manifests/ipset.pp b/manifests/ipset.pp new file mode 100644 index 0000000000000000000000000000000000000000..491313eed3183a7e43c79c45ba538f0be280917f --- /dev/null +++ b/manifests/ipset.pp @@ -0,0 +1,49 @@ +# Class: fw_builder::ipset +# +# +class fw_builder::ipset ( + $ipv4_enable = $fw_builder::params::ipv4_enable, + $ipv6_enable = $fw_builder::params::ipv6_enable +) { + + assert_private() + + $trusted_net = $fw_builder::trusted_networks + + $firewall_service = $facts['os']['family'] ? { + 'Debian' => 'netfilter-persistent.service', + default => undef + } + + $packages = "${facts['os']['family']}_${facts['os']['release']['major']}" ? { + 'RedHat_6' => ['ipset'], + default => undef + } + + class { 'ipset': + packages => $packages, + package_ensure => $fw_builder::ipset_package_ensure, + firewall_service => $firewall_service + } + + if ($ipv4_enable) { + $trusted_networks_v4 = $trusted_net.filter |$ip_range| { $ip_range =~ Stdlib::IP::Address::V4 } + ipset::set { 'trusted_networks_v4': + ensure => 'present', + type => 'hash:net', + set => $trusted_networks_v4; + } + } + + if ($ipv6_enable) { + $trusted_networks_v6 = $trusted_net.filter |$ip_range| { $ip_range =~ Stdlib::IP::Address::V6 } + ipset::set { 'trusted_networks_v6': + ensure => 'present', + type => 'hash:net', + set => $trusted_networks_v6, + options => {'family' => 'inet6'} + } + } + +} +# vim:ts=2:sw=2 diff --git a/manifests/logrotate.pp b/manifests/logrotate.pp new file mode 100644 index 0000000000000000000000000000000000000000..db29cdd8f7d90c6b2d7d12b0c31b8a930485e09a --- /dev/null +++ b/manifests/logrotate.pp @@ -0,0 +1,29 @@ +# == Class: fw_builder +# +# == Authors: +# +# Pete Pedersen<pete.pedersen@geant.org> +# Massimiliano Adamo<massimiliano.adamo@geant.org> +# +class fw_builder::logrotate ( + $logging = $fw_builder::params::logging, + $log_rotation_days = $fw_builder::params::log_rotation_days +) { + + assert_private() + + file { ['/var/log/iptables.log', '/var/log/ip6tables.log']: ensure => file; } + + if ($fw_builder::logging) { + logrotate::rule { 'iptables': + rotate => $log_rotation_days, + dateext => true, + copytruncate => true, + missingok => true, + compress => true, + ifempty => false, + path => '/var/log/ip*tables.log'; + } + } + +} diff --git a/manifests/params.pp b/manifests/params.pp new file mode 100644 index 0000000000000000000000000000000000000000..623a667f5f427e639c2fce3feeb452629e74bd86 --- /dev/null +++ b/manifests/params.pp @@ -0,0 +1,34 @@ +# == Class: fw_builder +# +# == Authors: +# +# Pete Pedersen<pete.pedersen@geant.org> +# Massimiliano Adamo<massimiliano.adamo@geant.org> +# +class fw_builder::params { + + # whether to purge rule not defined in puppet + $purge_rules = true + + # avoid that docker rules are being overwritten if purge is set to true + $manage_docker = false + + # enable iptables provider + $ipv4_enable = true + + # enable ip6tables provider + $ipv6_enable = true + + # enable logging + $logging = true + + # define log retention daysn + $log_rotation_days = 7 + + # ipset package version + $ipset_package_ensure = 'present' + + # whether to limit RST and dropped connections on post.pp + $limit = '1000/sec' + +} diff --git a/manifests/post.pp b/manifests/post.pp new file mode 100644 index 0000000000000000000000000000000000000000..529af4b19f586950382a1ba12021c490a54b091e --- /dev/null +++ b/manifests/post.pp @@ -0,0 +1,43 @@ +# == Class: fw_builder::post +# +class fw_builder::post ( + $logging = $fw_builder::params::logging +) { + + assert_private() + + if ($logging) { + $fw_builder::ip_proto_array.each | String $provider | { + firewall { + default: + chain => 'INPUT', + provider => $provider, + jump => 'LOG', + limit => $fw_builder::limit, + log_level => '4'; + "889 log RST dropped inbound chain for provider ${provider}": + log_prefix => "[${provider.upcase()} RST RST] dropped"; + "900 log dropped inbound chain for provider ${provider}": + proto => all, + log_prefix => "[${provider.upcase()} INPUT] dropped ", + } + } + } + + $fw_builder::ip_proto_array.each | String $provider | { + firewall { + default: + chain => 'INPUT', + provider => $provider; + "910 deny all other inbound requests for provider ${provider}": + before => undef, + proto => all, + action => 'drop'; + "890 drop RST RST connections for provider ${provider}": + tcp_flags => 'RST RST', + action => 'drop'; + } + } + +} +# vim:ts=2:sw=2