Skip to content

Commit 5c135df

Browse files
authored
Merge pull request #40 from Sxderp/pr-add-dictionary-rich-rules2
Allow rich_rules to be specified as a dictionary
2 parents cf21cca + c69fd6b commit 5c135df

File tree

4 files changed

+186
-64
lines changed

4 files changed

+186
-64
lines changed

.rubocop.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Layout/LineLength:
88
Max: 88
99

1010
# Any offenses that should be fixed, e.g. collected via. `rubocop --auto-gen-config`
11+
Metrics/BlockLength:
12+
Max: 39

firewalld/files/zone.xml

Lines changed: 96 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,72 @@
44
Do not edit this file manually, it will be overwritten!
55
Modify the salt pillar for firewalld instead
66
-->
7+
{%- macro rich_rule(rule) -%}
8+
{%- if 'family' in rule %}
9+
<rule family="{{ rule.family }}">
10+
{%- else %}
11+
<rule>
12+
{%- endif %}
13+
{%- if 'ipset' in rule %}
14+
<source ipset="{{ rule.ipset.name }}" />
15+
{%- endif %}
16+
{%- if 'source' in rule %}
17+
<source address="{{ rule.source.address }}" {%- if 'invert' in rule.source %}invert="{{ rule.source.invert }}"{%- endif %} />
18+
{%- endif %}
19+
{%- if 'destination' in rule %}
20+
<destination address="{{ rule.destination.address }}" {%- if 'invert' in rule.destination %}invert="{{ rule.destination.invert }}"{%- endif %} />
21+
{%- endif %}
22+
{%- if 'service' in rule %}
23+
<service name="{{ rule.service }}" />
24+
{%- endif %}
25+
{%- if 'port' in rule %}
26+
<port port="{{ rule.port.portid }}" protocol="{{ rule.port.protocol }}" />
27+
{%- endif %}
28+
{%- if 'protocol' in rule %}
29+
<protocol value="{{ rule.protocol }}" />
30+
{%- endif %}
31+
{%- if 'icmp_block' in rule %}
32+
<icmp-block name="{{ rule.icmp_block }}" />
33+
{%- endif %}
34+
{%- if 'icmp_type' in rule %}
35+
<icmp-type name="{{ rule.icmp_type }}" />
36+
{%- endif %}
37+
{%- if 'masquerade' in rule %}
38+
{%- if rule.masquerade %}<masquerade/>{%- endif %}
39+
{%- endif %}
40+
{%- if 'forward_port' in rule %}
41+
{%- if 'comment' in rule.forward_port %}
42+
<!-- {{ rule.forward_port.comment }} -->
43+
{%- endif %}
44+
<forward-port port="{{ rule.forward_port.portid }}" protocol="{{ rule.forward_port.protocol }}"{%- if 'to_port' in rule.forward_port %} to-port="{{ rule.forward_port.to_port }}"{%- endif %}{%- if 'to_addr' in rule.forward_port %} to-addr="{{ rule.forward_port.to_addr }}"{%- endif %} />
45+
{%- endif %}
46+
{%- if 'source_port' in rule %}
47+
{%- if 'comment' in rule.source_port %}
48+
<!-- {{ rule.source_port.comment }} -->
49+
{%- endif %}
50+
<source-port port="{{ rule.source_port.portid }}" protocol="{{ rule.source_port.protocol }}"{%- if 'to_port' in rule.source_port %} to-port="{{ rule.source_port.to_port }}"{%- endif %}{%- if 'to_addr' in rule.source_port %} to-addr="{{ rule.source_port.to_addr }}"{%- endif %} />
51+
{%- endif %}
52+
{%- if 'log' in rule %}
53+
<log{%- if 'prefix' in rule.log %} prefix="{{ rule.log.prefix }}"{%- endif %}{%- if 'level' in rule.log %} level="{{ rule.log.level }}"{%- endif %}>
54+
{%- if 'limit' in rule.log %}
55+
<limit value="{{ rule.log.limit }}"/>
56+
{%- endif %}
57+
</log>
58+
{%- endif %}
59+
{%- if 'audit' in rule %}
60+
<audit>{%- if 'limit' in rule.audit %} <limit value="{{ rule.audit.limit }}"/>{%- endif %}</audit>
61+
{%- endif %}
62+
{%- if 'accept' in rule %}
63+
<accept/>
64+
{%- endif %}
65+
{%- if 'reject' in rule %}
66+
<reject{%- if 'type' in rule.reject %} type="{{ rule.reject.type }}"{%- endif %} />
67+
{%- endif %}
68+
{%- if 'drop' in rule %}
69+
<drop/>
70+
{%- endif %}
71+
</rule>
72+
{%- endmacro %}
773
<zone{%- if 'target' in zone %} target="{{ zone.target }}"{%- endif %}>
874
{% if 'short' in zone %}<short>{{ zone.short }}</short>{% else %}<short>{{ name }}</short>{% endif %}
975
{% if 'description' in zone %}<description>{{ zone.description }}</description>{% endif %}
@@ -82,73 +148,39 @@
82148
<source-port port="{{ v.port }}" protocol="{{ v.protocol }}" />
83149
{%- endfor %}
84150
{%- endif %}
85-
86151
{%- if 'rich_rules' in zone %}
87-
{%- for rule in zone.rich_rules %}
88-
{%- if 'family' in rule %}
89-
<rule family="{{ rule.family }}">
152+
{%- if zone.rich_rules is list %}
153+
{%- set rich_rules = zone.rich_rules %}
90154
{%- else %}
91-
<rule>
92-
{%- endif %}
93-
{%- if 'ipset' in rule %}
94-
<source ipset="{{ rule.ipset.name }}" />
95-
{%- endif %}
96-
{%- if 'source' in rule %}
97-
<source address="{{ rule.source.address }}" {%- if 'invert' in rule.source %}invert="{{ rule.source.invert }}"{%- endif %} />
98-
{%- endif %}
99-
{%- if 'destination' in rule %}
100-
<destination address="{{ rule.destination.address }}" {%- if 'invert' in rule.destination %}invert="{{ rule.destination.invert }}"{%- endif %} />
101-
{%- endif %}
102-
{%- if 'service' in rule %}
103-
<service name="{{ rule.service }}" />
104-
{%- endif %}
105-
{%- if 'port' in rule %}
106-
<port port="{{ rule.port.portid }}" protocol="{{ rule.port.protocol }}" />
107-
{%- endif %}
108-
{%- if 'protocol' in rule %}
109-
<protocol value="{{ rule.protocol }}" />
110-
{%- endif %}
111-
{%- if 'icmp_block' in rule %}
112-
<icmp-block name="{{ rule.icmp_block }}" />
113-
{%- endif %}
114-
{%- if 'icmp_type' in rule %}
115-
<icmp-type name="{{ rule.icmp_type }}" />
116-
{%- endif %}
117-
{%- if 'masquerade' in rule %}
118-
{%- if rule.masquerade %}<masquerade/>{%- endif %}
119-
{%- endif %}
120-
{%- if 'forward_port' in rule %}
121-
{%- if 'comment' in rule.forward_port %}
122-
<!-- {{ rule.forward_port.comment }} -->
123-
{%- endif %}
124-
<forward-port port="{{ rule.forward_port.portid }}" protocol="{{ rule.forward_port.protocol }}"{%- if 'to_port' in rule.forward_port %} to-port="{{ rule.forward_port.to_port }}"{%- endif %}{%- if 'to_addr' in rule.forward_port %} to-addr="{{ rule.forward_port.to_addr }}"{%- endif %} />
125-
{%- endif %}
126-
{%- if 'source_port' in rule %}
127-
{%- if 'comment' in rule.source_port %}
128-
<!-- {{ rule.source_port.comment }} -->
129-
{%- endif %}
130-
<source-port port="{{ rule.source_port.portid }}" protocol="{{ rule.source_port.protocol }}"{%- if 'to_port' in rule.source_port %} to-port="{{ rule.source_port.to_port }}"{%- endif %}{%- if 'to_addr' in rule.source_port %} to-addr="{{ rule.source_port.to_addr }}"{%- endif %} />
131-
{%- endif %}
132-
{%- if 'log' in rule %}
133-
<log{%- if 'prefix' in rule.log %} prefix="{{ rule.log.prefix }}"{%- endif %}{%- if 'level' in rule.log %} level="{{ rule.log.level }}"{%- endif %}>
134-
{%- if 'limit' in rule.log %}
135-
<limit value="{{ rule.log.limit }}"/>
136-
{%- endif %}
137-
</log>
138-
{%- endif %}
139-
{%- if 'audit' in rule %}
140-
<audit>{%- if 'limit' in rule.audit %} <limit value="{{ rule.audit.limit }}"/>{%- endif %}</audit>
141-
{%- endif %}
142-
{%- if 'accept' in rule %}
143-
<accept/>
155+
{%- set expanded_ipset_rules = [] %}
156+
{%- for name,rule in zone.rich_rules|dictsort %}
157+
{%- if 'ipsets' in rule %}
158+
{%- for ipset in rule.ipsets %}
159+
{%- set tmp_rule = {} %}
160+
{%- set _dummy = tmp_rule.update(rule) %}
161+
{%- set _dummy = tmp_rule.update({'ipset':{'name':ipset}}) %}
162+
{%- set _dummy = expanded_ipset_rules.append(tmp_rule) %}
163+
{%- endfor %}
164+
{%- else %}
165+
{%- set _dummy = expanded_ipset_rules.append(rule) %}
166+
{%- endif %}
167+
{%- endfor %}
168+
{%- set rich_rules = [] %}
169+
{%- for rule in expanded_ipset_rules %}
170+
{%- if 'services' in rule %}
171+
{%- for service in rule.services %}
172+
{%- set tmp_rule = {} %}
173+
{%- set _dummy = tmp_rule.update(rule) %}
174+
{%- set _dummy = tmp_rule.update({'service':service}) %}
175+
{%- set _dummy = rich_rules.append(tmp_rule) %}
176+
{%- endfor %}
177+
{%- else %}
178+
{%- set _dummy = rich_rules.append(rule) %}
179+
{%- endif %}
180+
{%- endfor %}
144181
{%- endif %}
145-
{%- if 'reject' in rule %}
146-
<reject{%- if 'type' in rule.reject %} type="{{ rule.reject.type }}"{%- endif %} />
147-
{%- endif %}
148-
{%- if 'drop' in rule %}
149-
<drop/>
150-
{%- endif %}
151-
</rule>
182+
{%- for rule in rich_rules %}
183+
{{- rich_rule(rule) }}
152184
{%- endfor %}
153185
{%- endif %}
154186
</zone>

pillar.example

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ firewalld:
151151
port: 4444
152152
protocol: tcp
153153

154+
rich_public:
155+
short: rich_public
156+
description: "Example"
157+
# Rich rules can be specified as a dictionary. All keys from standard rich rules
158+
# can be used. Special keys "ipsets" and "services", if defined, take precedence.
159+
# They will be auto-expanded into separate rich rules per value in the list.
160+
rich_rules:
161+
ssh-csg:
162+
accept: true
163+
ipsets:
164+
- fail2ban-ssh
165+
- other-ipset
166+
services:
167+
- ssh
168+
154169
direct:
155170
chain:
156171
MYCHAIN:
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# frozen_string_literal: true
2+
3+
control 'zones/public.xml configuration' do
4+
title 'should match desired lines'
5+
6+
describe file('/etc/firewalld/zones/public.xml') do
7+
it { should be_file }
8+
it { should be_owned_by 'root' }
9+
it { should be_grouped_into 'root' }
10+
its('mode') { should cmp '0644' }
11+
its('content') do
12+
should include <<~ZONE_XML
13+
<zone>
14+
<short>Public</short>
15+
<description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
16+
<service name="zabbixcustom" />
17+
<service name="http" />
18+
<service name="https" />
19+
<service name="ssh" />
20+
<service name="salt-minion" />
21+
<!-- zabbix-agent -->
22+
<port port="10050" protocol="tcp" />
23+
<!-- bacula-client -->
24+
<port port="9102" protocol="tcp" />
25+
<!-- vsftpd -->
26+
<port port="21" protocol="tcp" />
27+
<protocol value="igmp" />
28+
<!-- something -->
29+
<source-port port="2222" protocol="tcp" />
30+
<!-- something_else -->
31+
<source-port port="4444" protocol="tcp" />
32+
<rule family="ipv4">
33+
<source address="8.8.8.8/24" />
34+
<accept/>
35+
</rule>
36+
<rule family="ipv4">
37+
<source ipset="fail2ban-ssh" />
38+
<reject type="icmp-port-unreachable" />
39+
</rule>
40+
</zone>
41+
ZONE_XML
42+
end
43+
end
44+
end
45+
46+
control 'zones/rich_public.xml configuration' do
47+
title 'should match desired lines'
48+
49+
describe file('/etc/firewalld/zones/rich_public.xml') do
50+
it { should be_file }
51+
it { should be_owned_by 'root' }
52+
it { should be_grouped_into 'root' }
53+
its('mode') { should cmp '0644' }
54+
its('content') do
55+
should include <<~ZONE_XML
56+
<zone>
57+
<short>rich_public</short>
58+
<description>Example</description>
59+
<rule>
60+
<source ipset="fail2ban-ssh" />
61+
<service name="ssh" />
62+
<accept/>
63+
</rule>
64+
<rule>
65+
<source ipset="other-ipset" />
66+
<service name="ssh" />
67+
<accept/>
68+
</rule>
69+
</zone>
70+
ZONE_XML
71+
end
72+
end
73+
end

0 commit comments

Comments
 (0)