has_feature :iptables
has_feature :connection_limiting
+ has_feature :conntrack
has_feature :hop_limiting
has_feature :rate_limiting
has_feature :recent_limiting
connlimit_above: '-m connlimit --connlimit-above',
connlimit_mask: '--connlimit-mask',
connmark: '-m connmark --mark',
- ctstate: '-m conntrack --ctstate',
+ ctstate: '--ctstate',
+ ctproto: '--ctproto',
+ ctorigsrc: '--ctorigsrc',
+ ctorigdst: '--ctorigdst',
+ ctreplsrc: '--ctreplsrc',
+ ctrepldst: '--ctrepldst',
+ ctorigsrcport: '--ctorigsrcport',
+ ctorigdstport: '--ctorigdstport',
+ ctreplsrcport: '--ctreplsrcport',
+ ctrepldstport: '--ctrepldstport',
+ ctstatus: '--ctstatus',
+ ctexpire: '--ctexpire',
+ ctdir: '--ctdir',
destination: '-d',
dport: ['-m multiport --dports', '--dport'],
dst_range: '--dst-range',
addrtype: [:src_type, :dst_type],
iprange: [:src_range, :dst_range],
owner: [:uid, :gid],
+ conntrack: [:ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst,
+ :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir],
time: [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone],
geoip: [:src_cc, :dst_cc],
hashlimit: [:hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst, :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask,
:proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :src_range, :dst_range,
:tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port, :src_type,
:dst_type, :socket, :pkttype, :ipsec_dir, :ipsec_policy, :state,
- :ctstate, :icmp, :hop_limit, :limit, :burst, :length, :recent, :rseconds, :reap,
+ :ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst,
+ :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir,
+ :icmp, :hop_limit, :limit, :burst, :length, :recent, :rseconds, :reap,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :string, :string_algo,
:string_from, :string_to, :jump, :clamp_mss_to_pmtu, :gateway, :todest,
:tosource, :toports, :checksum_fill, :log_level, :log_prefix, :log_uid, :reject, :set_mss, :set_dscp, :set_dscp_class, :mss, :queue_num, :queue_bypass,
has_feature :iptables
has_feature :connection_limiting
+ has_feature :conntrack
has_feature :rate_limiting
has_feature :recent_limiting
has_feature :snat
connlimit_above: '-m connlimit --connlimit-above',
connlimit_mask: '--connlimit-mask',
connmark: '-m connmark --mark',
- ctstate: '-m conntrack --ctstate',
+ ctstate: '--ctstate',
+ ctproto: '--ctproto',
+ ctorigsrc: '--ctorigsrc',
+ ctorigdst: '--ctorigdst',
+ ctreplsrc: '--ctreplsrc',
+ ctrepldst: '--ctrepldst',
+ ctorigsrcport: '--ctorigsrcport',
+ ctorigdstport: '--ctorigdstport',
+ ctreplsrcport: '--ctreplsrcport',
+ ctrepldstport: '--ctrepldstport',
+ ctstatus: '--ctstatus',
+ ctexpire: '--ctexpire',
+ ctdir: '--ctdir',
destination: '-d',
dport: ['-m multiport --dports', '--dport'],
dst_range: '--dst-range',
addrtype: [:src_type, :dst_type],
iprange: [:src_range, :dst_range],
owner: [:uid, :gid],
+ conntrack: [:ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst,
+ :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir],
time: [:time_start, :time_stop, :month_days, :week_days, :date_start, :date_stop, :time_contiguous, :kernel_timezone],
geoip: [:src_cc, :dst_cc],
hashlimit: [:hashlimit_upto, :hashlimit_above, :hashlimit_name, :hashlimit_burst, :hashlimit_mode, :hashlimit_srcmask, :hashlimit_dstmask,
:proto, :isfragment, :stat_mode, :stat_every, :stat_packet, :stat_probability,
:src_range, :dst_range, :tcp_flags, :uid, :gid, :mac_source, :sport, :dport, :port,
:src_type, :dst_type, :socket, :pkttype, :ipsec_dir, :ipsec_policy,
- :state, :ctstate, :icmp, :limit, :burst, :length, :recent, :rseconds, :reap,
+ :state, :ctstate, :ctproto, :ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst,
+ :ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport, :ctstatus, :ctexpire, :ctdir,
+ :icmp, :limit, :burst, :length, :recent, :rseconds, :reap,
:rhitcount, :rttl, :rname, :mask, :rsource, :rdest, :ipset, :string, :string_algo,
:string_from, :string_to, :jump, :goto, :clusterip_new, :clusterip_hashmode,
:clusterip_clustermac, :clusterip_total_nodes, :clusterip_local_node, :clusterip_hash_init, :queue_num, :queue_bypass,
# POST PARSE CLUDGING
#####################
- [:dport, :sport, :port, :state, :ctstate].each do |prop|
+ [:dport, :sport, :port, :state, :ctstate, :ctstatus].each do |prop|
hash[prop] = hash[prop].split(',') unless hash[prop].nil?
end
[
:connmark,
:ctstate,
+ :ctproto,
+ :ctorigsrc,
+ :ctorigdst,
+ :ctreplsrc,
+ :ctrepldst,
+ :ctorigsrcport,
+ :ctorigdstport,
+ :ctreplsrcport,
+ :ctrepldstport,
+ :ctstatus,
+ :ctexpire,
:destination,
:dport,
:dst_range,
# iptables-save and user supplied resources is consistent.
hash[:state] = hash[:state].sort unless hash[:state].nil?
hash[:ctstate] = hash[:ctstate].sort unless hash[:ctstate].nil?
+ hash[:ctstatus] = hash[:ctstatus].sort unless hash[:ctstatus].nil?
# This forces all existing, commentless rules or rules with invalid comments to be moved
# to the bottom of the stack.
* ip6tables: Ip6tables type provider
* Required binaries: ip6tables-save, ip6tables.
- * Supported features: address_type, connection_limiting, dnat, hop_limiting, icmp_match,
+ * Supported features: address_type, connection_limiting, conntrack, dnat, hop_limiting, icmp_match,
interface_match, iprange, ipsec_dir, ipsec_policy, ipset, iptables, isfirstfrag,
ishasmorefrags, islastfrag, length, log_level, log_prefix, log_uid, mark, mask, mss,
owner, pkttype, queue_bypass, queue_num, rate_limiting, recent_limiting, reject_type,
* Required binaries: iptables-save, iptables.
* Default for kernel == linux.
- * Supported features: address_type, clusterip, connection_limiting, dnat, icmp_match,
+ * Supported features: address_type, clusterip, connection_limiting, conntrack, dnat, icmp_match,
interface_match, iprange, ipsec_dir, ipsec_policy, ipset, iptables, isfragment, length,
log_level, log_prefix, log_uid, mark, mask, mss, netmap, nflog_group, nflog_prefix,
nflog_range, nflog_threshold, owner, pkttype, queue_bypass, queue_num, rate_limiting,
* connection_limiting: Connection limiting features.
+ * conntrack: Connection tracking features.
+
* dnat: Destination NATing.
* hop_limiting: Hop limiting features.
PUPPETCODE
feature :connection_limiting, 'Connection limiting features.'
+ feature :conntrack, 'Connection tracking features.'
feature :hop_limiting, 'Hop limiting features.'
feature :rate_limiting, 'Rate limiting features.'
feature :recent_limiting, 'The netfilter recent module'
end
end
- newproperty(:ctstate, array_matching: :all, required_features: :state_match) do
+ newproperty(:ctstate, array_matching: :all, required_features: :conntrack) do
desc <<-PUPPETCODE
Matches a packet based on its state in the firewall stateful inspection
table, using the conntrack module. Values can be:
* NEW
* RELATED
* UNTRACKED
+ * SNAT
+ * DNAT
PUPPETCODE
- newvalues(:INVALID, :ESTABLISHED, :NEW, :RELATED, :UNTRACKED)
+ newvalues(:INVALID, :ESTABLISHED, :NEW, :RELATED, :UNTRACKED, :SNAT, :DNAT)
# States should always be sorted. This normalizes the resource states to
# keep it consistent with the sorted result from iptables-save.
end
end
+ newproperty(:ctproto, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The specific layer-4 protocol number to match for this rule using the
+ conntrack module.
+ PUPPETCODE
+ newvalue(%r{^!?\s?\d+$})
+ end
+
+ newproperty(:ctorigsrc, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The original source address using the conntrack module. For example:
+
+ ctorigsrc => '192.168.2.0/24'
+
+ You can also negate a mask by putting ! in front. For example:
+
+ ctorigsrc => '! 192.168.2.0/24'
+
+ The ctorigsrc can also be an IPv6 address if your provider supports it.
+ PUPPETCODE
+
+ munge do |value|
+ case @resource[:provider]
+ when :iptables
+ protocol = :IPv4
+ when :ip6tables
+ protocol = :IPv6
+ else
+ raise('cannot work out protocol family')
+ end
+
+ begin
+ @resource.host_to_mask(value, protocol)
+ if protocol == :IPv4
+ value.chomp("/32")
+ elsif protocol == :IPv6
+ value.chomp("/128")
+ end
+ rescue StandardError => e
+ raise("host_to_ip failed for #{value}, exception #{e}")
+ end
+ end
+ end
+
+ newproperty(:ctorigdst, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The original destination address using the conntrack module. For example:
+
+ ctorigdst => '192.168.2.0/24'
+
+ You can also negate a mask by putting ! in front. For example:
+
+ ctorigdst => '! 192.168.2.0/24'
+
+ The ctorigdst can also be an IPv6 address if your provider supports it.
+ PUPPETCODE
+
+ munge do |value|
+ case @resource[:provider]
+ when :iptables
+ protocol = :IPv4
+ when :ip6tables
+ protocol = :IPv6
+ else
+ raise('cannot work out protocol family')
+ end
+
+ begin
+ @resource.host_to_mask(value, protocol)
+ if protocol == :IPv4
+ value.chomp("/32")
+ elsif protocol == :IPv6
+ value.chomp("/128")
+ end
+ rescue StandardError => e
+ raise("host_to_ip failed for #{value}, exception #{e}")
+ end
+ end
+ end
+
+ newproperty(:ctreplsrc, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The reply source address using the conntrack module. For example:
+
+ ctreplsrc => '192.168.2.0/24'
+
+ You can also negate a mask by putting ! in front. For example:
+
+ ctreplsrc => '! 192.168.2.0/24'
+
+ The ctreplsrc can also be an IPv6 address if your provider supports it.
+ PUPPETCODE
+
+ munge do |value|
+ case @resource[:provider]
+ when :iptables
+ protocol = :IPv4
+ when :ip6tables
+ protocol = :IPv6
+ else
+ raise('cannot work out protocol family')
+ end
+
+ begin
+ @resource.host_to_mask(value, protocol)
+ if protocol == :IPv4
+ value.chomp("/32")
+ elsif protocol == :IPv6
+ value.chomp("/128")
+ end
+ rescue StandardError => e
+ raise("host_to_ip failed for #{value}, exception #{e}")
+ end
+ end
+ end
+
+ newproperty(:ctrepldst, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The reply destination address using the conntrack module. For example:
+
+ ctrepldst => '192.168.2.0/24'
+
+ You can also negate a mask by putting ! in front. For example:
+
+ ctrepldst => '! 192.168.2.0/24'
+
+ The ctrepldst can also be an IPv6 address if your provider supports it.
+ PUPPETCODE
+
+ munge do |value|
+ case @resource[:provider]
+ when :iptables
+ protocol = :IPv4
+ when :ip6tables
+ protocol = :IPv6
+ else
+ raise('cannot work out protocol family')
+ end
+
+ begin
+ @resource.host_to_mask(value, protocol)
+ if protocol == :IPv4
+ value.chomp("/32")
+ elsif protocol == :IPv6
+ value.chomp("/128")
+ end
+ rescue StandardError => e
+ raise("host_to_ip failed for #{value}, exception #{e}")
+ end
+ end
+ end
+
+ newproperty(:ctorigsrcport, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The original source port to match for this filter using the conntrack module.
+ For example:
+
+ ctorigsrcport => '80'
+
+ You can also specify a port range: For example:
+
+ ctorigsrcport => '80:81'
+
+ You can also negate a port by putting ! in front. For example:
+
+ ctorigsrcport => '! 80'
+
+ PUPPETCODE
+ newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+ end
+
+ newproperty(:ctorigdstport, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The original destination port to match for this filter using the conntrack module.
+ For example:
+
+ ctorigdstport => '80'
+
+ You can also specify a port range: For example:
+
+ ctorigdstport => '80:81'
+
+ You can also negate a port by putting ! in front. For example:
+
+ ctorigdstport => '! 80'
+
+ PUPPETCODE
+ newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+ end
+
+ newproperty(:ctreplsrcport, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The reply source port to match for this filter using the conntrack module.
+ For example:
+
+ ctreplsrcport => '80'
+
+ You can also specify a port range: For example:
+
+ ctreplsrcport => '80:81'
+
+ You can also negate a port by putting ! in front. For example:
+
+ ctreplsrcport => '! 80'
+
+ PUPPETCODE
+ newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+ end
+
+ newproperty(:ctrepldstport, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ The reply destination port to match for this filter using the conntrack module.
+ For example:
+
+ ctrepldstport => '80'
+
+ You can also specify a port range: For example:
+
+ ctrepldstport => '80:81'
+
+ You can also negate a port by putting ! in front. For example:
+
+ ctrepldstport => '! 80'
+
+ PUPPETCODE
+ newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+ end
+
+ newproperty(:ctstatus, array_matching: :all, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ Matches a packet based on its status using the conntrack module. Values can be:
+
+ * EXPECTED
+ * SEEN_REPLY
+ * ASSURED
+ * CONFIRMED
+ PUPPETCODE
+
+ newvalues(:NONE, :EXPECTED, :SEEN_REPLY, :ASSURED, :CONFIRMED)
+
+ # Statuses should always be sorted. This normalizes the resource status to
+ # keep it consistent with the sorted result from iptables-save.
+ def should=(values)
+ @should = super(values).sort_by { |sym| sym.to_s }
+ end
+
+ def to_s?(value)
+ should_to_s(value)
+ end
+
+ def should_to_s(value)
+ value = [value] unless value.is_a?(Array)
+ value.join(',')
+ end
+ end
+
+ newproperty(:ctexpire, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ Matches a packet based on lifetime remaining in seconds or range of values
+ using the conntrack module. For example:
+
+ ctexpire => '100:150'
+
+ PUPPETCODE
+ newvalue(%r{^!?\s?\d+$|^!?\s?\d+\:\d+$})
+ end
+
+ newproperty(:ctdir, required_features: :conntrack) do
+ desc <<-PUPPETCODE
+ Matches a packet that is flowing in the specified direction using the
+ conntrack module. If this flag is not specified at all, matches packets
+ in both directions. Values can be:
+
+ * REPLY
+ * ORIGINAL
+ PUPPETCODE
+
+ newvalues(:REPLY, :ORIGINAL)
+ end
+
# Connection mark
newproperty(:connmark, required_features: :mark) do
desc <<-PUPPETCODE
end
end
+ describe ':ctproto' do
+ it 'accepts numeric value' do
+ resource[:ctproto] = 6
+ expect(resource[:ctproto]).to eql 6
+ end
+ it 'accepts negated string value' do
+ resource[:ctproto] = '! 6'
+ expect(resource[:ctproto]).to eql '! 6'
+ end
+ end
+
+ [:ctorigsrc, :ctorigdst, :ctreplsrc, :ctrepldst].each do |addr|
+ describe addr do
+ it "should accept a #{addr} as a string without /32" do
+ resource[addr] = '127.0.0.1'
+ expect(resource[addr]).to eql '127.0.0.1'
+ end
+ it "should accept a #{addr} as a string with /32" do
+ resource[addr] = '127.0.0.1/32'
+ expect(resource[addr]).to eql '127.0.0.1'
+ end
+ it "should accept a #{addr} as a string with cidr" do
+ resource[addr] = '10.0.0.0/8'
+ expect(resource[addr]).to eql '10.0.0.0/8'
+ end
+ it "should accept a #{addr} as a string with ipv6 cidr" do
+ resource[addr] = '2001:DB8::/64'
+ expect(resource[addr]).to eql '2001:DB8::/64'
+ end
+ it "should accept a negated #{addr} as a string" do
+ resource[addr] = '! 127.0.0.1'
+ expect(resource[addr]).to eql '! 127.0.0.1'
+ end
+ it "should accept a negated #{addr} as a string with cidr" do
+ resource[addr] = '! 10.0.0.0/8'
+ expect(resource[addr]).to eql '! 10.0.0.0/8'
+ end
+ end
+ end
+
+ [:ctorigsrcport, :ctorigdstport, :ctreplsrcport, :ctrepldstport].each do |port|
+ describe port do
+ it "should accept #{port} as numeric value" do
+ resource[port] = 80
+ expect(resource[port]).to eql 80
+ end
+ it "should accept #{port} as range value" do
+ resource[port] = '80:81'
+ expect(resource[port]).to eql '80:81'
+ end
+ it "should accept a negated #{port} as string value" do
+ resource[port] = '! 80'
+ expect(resource[port]).to eql '! 80'
+ end
+ it "should accept a negated #{port} as range value" do
+ resource[port] = '! 80:81'
+ expect(resource[port]).to eql '! 80:81'
+ end
+ end
+ end
+
+ describe ':ctstatus' do
+ it 'accepts value as a string - EXPECTED' do
+ resource[:ctstatus] = :EXPECTED
+ expect(resource[:ctstatus]).to eql [:EXPECTED]
+ end
+
+ it 'accepts value as an array - EXPECTED, SEEN_REPLY' do
+ resource[:ctstatus] = [:EXPECTED, :SEEN_REPLY]
+ expect(resource[:ctstatus]).to eql [:EXPECTED, :SEEN_REPLY]
+ end
+
+ it 'sorts values alphabetically - SEEN_REPLY, EXPECTED' do
+ resource[:ctstatus] = [:SEEN_REPLY, :EXPECTED]
+ expect(resource[:ctstatus]).to eql [:EXPECTED, :SEEN_REPLY]
+ end
+ end
+
+ describe ':ctexpire' do
+ it 'accepts numeric values' do
+ resource[:ctexpire] = 100
+ expect(resource[:ctexpire]).to eql 100
+ end
+
+ it 'accepts numeric range values' do
+ resource[:ctexpire] = '100:120'
+ expect(resource[:ctexpire]).to eql '100:120'
+ end
+ end
+
+ describe ':ctdir' do
+ it 'accepts value as a string - REPLY' do
+ resource[:ctdir] = :REPLY
+ expect(resource[:ctdir]).to be :REPLY
+ end
+
+ it 'accepts value as a string - ORIGINAL' do
+ resource[:ctdir] = :ORIGINAL
+ expect(resource[:ctdir]).to be :ORIGINAL
+ end
+ end
+
describe ':burst' do
it 'accepts numeric values' do
resource[:burst] = 12