Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ Layout/LineLength:
Max: 88

# Any offenses that should be fixed, e.g. collected via. `rubocop --auto-gen-config`
Metrics/BlockLength:
Max: 39
160 changes: 96 additions & 64 deletions firewalld/files/zone.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,72 @@
Do not edit this file manually, it will be overwritten!
Modify the salt pillar for firewalld instead
-->
{%- macro rich_rule(rule) -%}
{%- if 'family' in rule %}
<rule family="{{ rule.family }}">
{%- else %}
<rule>
{%- endif %}
{%- if 'ipset' in rule %}
<source ipset="{{ rule.ipset.name }}" />
{%- endif %}
{%- if 'source' in rule %}
<source address="{{ rule.source.address }}" {%- if 'invert' in rule.source %}invert="{{ rule.source.invert }}"{%- endif %} />
{%- endif %}
{%- if 'destination' in rule %}
<destination address="{{ rule.destination.address }}" {%- if 'invert' in rule.destination %}invert="{{ rule.destination.invert }}"{%- endif %} />
{%- endif %}
{%- if 'service' in rule %}
<service name="{{ rule.service }}" />
{%- endif %}
{%- if 'port' in rule %}
<port port="{{ rule.port.portid }}" protocol="{{ rule.port.protocol }}" />
{%- endif %}
{%- if 'protocol' in rule %}
<protocol value="{{ rule.protocol }}" />
{%- endif %}
{%- if 'icmp_block' in rule %}
<icmp-block name="{{ rule.icmp_block }}" />
{%- endif %}
{%- if 'icmp_type' in rule %}
<icmp-type name="{{ rule.icmp_type }}" />
{%- endif %}
{%- if 'masquerade' in rule %}
{%- if rule.masquerade %}<masquerade/>{%- endif %}
{%- endif %}
{%- if 'forward_port' in rule %}
{%- if 'comment' in rule.forward_port %}
<!-- {{ rule.forward_port.comment }} -->
{%- endif %}
<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 %} />
{%- endif %}
{%- if 'source_port' in rule %}
{%- if 'comment' in rule.source_port %}
<!-- {{ rule.source_port.comment }} -->
{%- endif %}
<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 %} />
{%- endif %}
{%- if 'log' in rule %}
<log{%- if 'prefix' in rule.log %} prefix="{{ rule.log.prefix }}"{%- endif %}{%- if 'level' in rule.log %} level="{{ rule.log.level }}"{%- endif %}>
{%- if 'limit' in rule.log %}
<limit value="{{ rule.log.limit }}"/>
{%- endif %}
</log>
{%- endif %}
{%- if 'audit' in rule %}
<audit>{%- if 'limit' in rule.audit %} <limit value="{{ rule.audit.limit }}"/>{%- endif %}</audit>
{%- endif %}
{%- if 'accept' in rule %}
<accept/>
{%- endif %}
{%- if 'reject' in rule %}
<reject{%- if 'type' in rule.reject %} type="{{ rule.reject.type }}"{%- endif %} />
{%- endif %}
{%- if 'drop' in rule %}
<drop/>
{%- endif %}
</rule>
{%- endmacro %}
<zone{%- if 'target' in zone %} target="{{ zone.target }}"{%- endif %}>
{% if 'short' in zone %}<short>{{ zone.short }}</short>{% else %}<short>{{ name }}</short>{% endif %}
{% if 'description' in zone %}<description>{{ zone.description }}</description>{% endif %}
Expand Down Expand Up @@ -82,73 +148,39 @@
<source-port port="{{ v.port }}" protocol="{{ v.protocol }}" />
{%- endfor %}
{%- endif %}

{%- if 'rich_rules' in zone %}
{%- for rule in zone.rich_rules %}
{%- if 'family' in rule %}
<rule family="{{ rule.family }}">
{%- if zone.rich_rules is list %}
{%- set rich_rules = zone.rich_rules %}
{%- else %}
<rule>
{%- endif %}
{%- if 'ipset' in rule %}
<source ipset="{{ rule.ipset.name }}" />
{%- endif %}
{%- if 'source' in rule %}
<source address="{{ rule.source.address }}" {%- if 'invert' in rule.source %}invert="{{ rule.source.invert }}"{%- endif %} />
{%- endif %}
{%- if 'destination' in rule %}
<destination address="{{ rule.destination.address }}" {%- if 'invert' in rule.destination %}invert="{{ rule.destination.invert }}"{%- endif %} />
{%- endif %}
{%- if 'service' in rule %}
<service name="{{ rule.service }}" />
{%- endif %}
{%- if 'port' in rule %}
<port port="{{ rule.port.portid }}" protocol="{{ rule.port.protocol }}" />
{%- endif %}
{%- if 'protocol' in rule %}
<protocol value="{{ rule.protocol }}" />
{%- endif %}
{%- if 'icmp_block' in rule %}
<icmp-block name="{{ rule.icmp_block }}" />
{%- endif %}
{%- if 'icmp_type' in rule %}
<icmp-type name="{{ rule.icmp_type }}" />
{%- endif %}
{%- if 'masquerade' in rule %}
{%- if rule.masquerade %}<masquerade/>{%- endif %}
{%- endif %}
{%- if 'forward_port' in rule %}
{%- if 'comment' in rule.forward_port %}
<!-- {{ rule.forward_port.comment }} -->
{%- endif %}
<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 %} />
{%- endif %}
{%- if 'source_port' in rule %}
{%- if 'comment' in rule.source_port %}
<!-- {{ rule.source_port.comment }} -->
{%- endif %}
<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 %} />
{%- endif %}
{%- if 'log' in rule %}
<log{%- if 'prefix' in rule.log %} prefix="{{ rule.log.prefix }}"{%- endif %}{%- if 'level' in rule.log %} level="{{ rule.log.level }}"{%- endif %}>
{%- if 'limit' in rule.log %}
<limit value="{{ rule.log.limit }}"/>
{%- endif %}
</log>
{%- endif %}
{%- if 'audit' in rule %}
<audit>{%- if 'limit' in rule.audit %} <limit value="{{ rule.audit.limit }}"/>{%- endif %}</audit>
{%- endif %}
{%- if 'accept' in rule %}
<accept/>
{%- set expanded_ipset_rules = [] %}
{%- for name,rule in zone.rich_rules|dictsort %}
{%- if 'ipsets' in rule %}
{%- for ipset in rule.ipsets %}
{%- set tmp_rule = {} %}
{%- set _dummy = tmp_rule.update(rule) %}
{%- set _dummy = tmp_rule.update({'ipset':{'name':ipset}}) %}
{%- set _dummy = expanded_ipset_rules.append(tmp_rule) %}
{%- endfor %}
{%- else %}
{%- set _dummy = expanded_ipset_rules.append(rule) %}
{%- endif %}
{%- endfor %}
{%- set rich_rules = [] %}
{%- for rule in expanded_ipset_rules %}
{%- if 'services' in rule %}
{%- for service in rule.services %}
{%- set tmp_rule = {} %}
{%- set _dummy = tmp_rule.update(rule) %}
{%- set _dummy = tmp_rule.update({'service':service}) %}
{%- set _dummy = rich_rules.append(tmp_rule) %}
{%- endfor %}
{%- else %}
{%- set _dummy = rich_rules.append(rule) %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- if 'reject' in rule %}
<reject{%- if 'type' in rule.reject %} type="{{ rule.reject.type }}"{%- endif %} />
{%- endif %}
{%- if 'drop' in rule %}
<drop/>
{%- endif %}
</rule>
{%- for rule in rich_rules %}
{{- rich_rule(rule) }}
{%- endfor %}
{%- endif %}
</zone>
15 changes: 15 additions & 0 deletions pillar.example
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,21 @@ firewalld:
port: 4444
protocol: tcp

rich_public:
short: rich_public
description: "Example"
# Rich rules can be specified as a dictionary. All keys from standard rich rules
# can be used. Special keys "ipsets" and "services", if defined, take precedence.
# They will be auto-expanded into separate rich rules per value in the list.
rich_rules:
ssh-csg:
accept: true
ipsets:
- fail2ban-ssh
- other-ipset
services:
- ssh

direct:
chain:
MYCHAIN:
Expand Down
73 changes: 73 additions & 0 deletions test/integration/default/controls/zones_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

control 'zones/public.xml configuration' do
title 'should match desired lines'

describe file('/etc/firewalld/zones/public.xml') do
it { should be_file }
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
its('mode') { should cmp '0644' }
its('content') do
should include <<~ZONE_XML
<zone>
<short>Public</short>
<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>
<service name="zabbixcustom" />
<service name="http" />
<service name="https" />
<service name="ssh" />
<service name="salt-minion" />
<!-- zabbix-agent -->
<port port="10050" protocol="tcp" />
<!-- bacula-client -->
<port port="9102" protocol="tcp" />
<!-- vsftpd -->
<port port="21" protocol="tcp" />
<protocol value="igmp" />
<!-- something -->
<source-port port="2222" protocol="tcp" />
<!-- something_else -->
<source-port port="4444" protocol="tcp" />
<rule family="ipv4">
<source address="8.8.8.8/24" />
<accept/>
</rule>
<rule family="ipv4">
<source ipset="fail2ban-ssh" />
<reject type="icmp-port-unreachable" />
</rule>
</zone>
ZONE_XML
end
end
end

control 'zones/rich_public.xml configuration' do
title 'should match desired lines'

describe file('/etc/firewalld/zones/rich_public.xml') do
it { should be_file }
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
its('mode') { should cmp '0644' }
its('content') do
should include <<~ZONE_XML
<zone>
<short>rich_public</short>
<description>Example</description>
<rule>
<source ipset="fail2ban-ssh" />
<service name="ssh" />
<accept/>
</rule>
<rule>
<source ipset="other-ipset" />
<service name="ssh" />
<accept/>
</rule>
</zone>
ZONE_XML
end
end
end