Skip to content

Commit 16d8e83

Browse files
committed
Merge pull request puppetlabs#615 from nabam/multiple-ipset
support for multiple ipsets in a rule
2 parents 799da98 + 4af1f13 commit 16d8e83

File tree

4 files changed

+47
-14
lines changed

4 files changed

+47
-14
lines changed

README.markdown

+1-1
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ If Puppet is managing the iptables or iptables-persistent packages, and the prov
576576

577577
* `ipsec_policy`: Sets the ipsec policy type. Valid values are 'none', 'ipsec'. Requires the `ipsec_policy` feature.
578578

579-
* `ipset`: Matches IP sets. Value must be 'ipset_name (src|dst|src,dst)' and can be negated by putting ! in front. Requires ipset kernel module.
579+
* `ipset`: Matches IP sets. Value must be 'ipset_name (src|dst|src,dst)' and can be negated by putting ! in front. Requires ipset kernel module. Will accept a single element or an array.
580580

581581
* `isfirstfrag`: If true, matches when the packet is the first fragment of a fragmented ipv6 packet. Cannot be negated. Supported by ipv6 only. Valid values are 'true', 'false'. Requires the `isfirstfrag` feature.
582582

lib/puppet/provider/firewall/iptables.rb

+22-5
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,14 @@ def self.rule_to_hash(line, table, counter)
352352
# --tcp-flags takes two values; we cheat by adding " around it
353353
# so it behaves like --comment
354354
values = values.gsub(/(!\s+)?--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1\2 \3"')
355-
# ditto for --match-set
356-
values = values.sub(/(!\s+)?--match-set (\S*) (\S*)/, '--match-set "\1\2 \3"')
355+
# --match-set can have multiple values with weird iptables format
356+
if values =~ /-m set --match-set/
357+
values = values.gsub(/(!\s+)?--match-set (\S*) (\S*)/, '--match-set \1\2 \3')
358+
ind = values.index('-m set --match-set')
359+
sets = values.scan(/-m set --match-set ((?:!\s+)?\S* \S*)/)
360+
values = values.gsub(/-m set --match-set (!\s+)?\S* \S* /, '')
361+
values.insert(ind, "-m set --match-set \"#{sets.join(';')}\" ")
362+
end
357363
# we do a similar thing for negated address masks (source and destination).
358364
values = values.gsub(/(-\S+) (!)\s?(\S*)/,'\1 "\2 \3"')
359365
# the actual rule will have the ! mark before the option.
@@ -446,6 +452,8 @@ def self.rule_to_hash(line, table, counter)
446452
hash[prop] = hash[prop].split(',') if ! hash[prop].nil?
447453
end
448454

455+
hash[:ipset] = hash[:ipset].split(';') if ! hash[:ipset].nil?
456+
449457
## clean up DSCP class to HEX mappings
450458
valid_dscp_classes = {
451459
'0x0a' => 'af11',
@@ -507,7 +515,6 @@ def self.rule_to_hash(line, table, counter)
507515
:dport,
508516
:dst_range,
509517
:dst_type,
510-
:ipset,
511518
:port,
512519
:proto,
513520
:source,
@@ -649,7 +656,7 @@ def general_args
649656
#ruby 1.8.7 can't .match Symbols ------------------ ^
650657
resource_value = resource_value.to_s.sub!(/^!\s*/, '').to_sym
651658
args.insert(-2, '!')
652-
elsif resource_value.is_a?(Array)
659+
elsif resource_value.is_a?(Array) and res != :ipset
653660
should_negate = resource_value.index do |value|
654661
#ruby 1.8.7 can't .match symbols
655662
value.to_s.match(/^(!)\s+/)
@@ -680,10 +687,20 @@ def general_args
680687
end
681688
end
682689

690+
# ipset can accept multiple values with weird iptables arguments
691+
if res == :ipset
692+
resource_value.join(" #{[resource_map[res]].flatten.first} ").split(' ').each do |a|
693+
if a.sub!(/^!\s*/, '')
694+
# Negate ipset options
695+
args.insert(-2, '!')
696+
end
697+
698+
args << a if a.length > 0
699+
end
683700
# our tcp_flags takes a single string with comma lists separated
684701
# by space
685702
# --tcp-flags expects two arguments
686-
if res == :tcp_flags or res == :ipset
703+
elsif res == :tcp_flags
687704
one, two = resource_value.split(' ')
688705
args << one
689706
args << two

lib/puppet/type/firewall.rb

+11-2
Original file line numberDiff line numberDiff line change
@@ -1190,14 +1190,23 @@ def insync?(is)
11901190
EOS
11911191
end
11921192

1193-
newproperty(:ipset, :required_features => :ipset) do
1193+
newproperty(:ipset, :required_features => :ipset, :array_matching => :all) do
11941194
desc <<-EOS
11951195
Matches against the specified ipset list.
1196-
Requires ipset kernel module.
1196+
Requires ipset kernel module. Will accept a single element or an array.
11971197
The value is the name of the blacklist, followed by a space, and then
11981198
'src' and/or 'dst' separated by a comma.
11991199
For example: 'blacklist src,dst'
12001200
EOS
1201+
1202+
def is_to_s(value)
1203+
should_to_s(value)
1204+
end
1205+
1206+
def should_to_s(value)
1207+
value = [value] unless value.is_a?(Array)
1208+
value.join(', ')
1209+
end
12011210
end
12021211

12031212
newproperty(:checksum_fill, :required_features => :iptables) do

spec/acceptance/firewall_spec.rb

+13-6
Original file line numberDiff line numberDiff line change
@@ -1531,24 +1531,31 @@ class { '::firewall': }
15311531
require => Package['ipset'],
15321532
}
15331533
class { '::firewall': }
1534-
exec { 'create ipset':
1534+
exec { 'create ipset blacklist':
15351535
command => 'ipset create blacklist hash:ip,port family inet6 maxelem 1024 hashsize 65535 timeout 120',
15361536
path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
15371537
require => Package['ipset'],
15381538
}
1539-
exec { 'add blacklist':
1539+
-> exec { 'create ipset honeypot':
1540+
command => 'ipset create honeypot hash:ip family inet6 maxelem 1024 hashsize 65535 timeout 120',
1541+
path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
1542+
}
1543+
-> exec { 'add blacklist':
15401544
command => 'ipset add blacklist 2001:db8::1,80',
15411545
path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
1542-
require => Exec['create ipset'],
1546+
}
1547+
-> exec { 'add honeypot':
1548+
command => 'ipset add honeypot 2001:db8::5',
1549+
path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
15431550
}
15441551
firewall { '612 - test':
15451552
ensure => present,
15461553
chain => 'INPUT',
15471554
proto => tcp,
15481555
action => drop,
1549-
ipset => 'blacklist src,src',
1556+
ipset => ['blacklist src,dst', '! honeypot dst'],
15501557
provider => 'ip6tables',
1551-
require => Exec['add blacklist'],
1558+
require => Exec['add honeypot'],
15521559
}
15531560
EOS
15541561

@@ -1557,7 +1564,7 @@ class { '::firewall': }
15571564

15581565
it 'should contain the rule' do
15591566
shell('ip6tables-save') do |r|
1560-
expect(r.stdout).to match(/-A INPUT -p tcp -m comment --comment "612 - test" -m set --match-set blacklist src,src -j DROP/)
1567+
expect(r.stdout).to match(/-A INPUT -p tcp -m comment --comment "612 - test" -m set --match-set blacklist src,dst -m set ! --match-set honeypot dst -j DROP/)
15611568
end
15621569
end
15631570
end

0 commit comments

Comments
 (0)