Initial commit.

This commit is contained in:
David Bezuidenhout 2014-08-23 16:44:48 +02:00
commit baa2afab61
12 changed files with 558 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
pkg/
metadata.json
*.idea
*.swp
*.tmp

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2014 David Bezuidenhout <tinuva@ipv6.za.org>
NOTICE THE LICENSE EXCEPTIONS BELOW.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
EXCEPTIONS:
You MAY NOT upload parts or the whole work of this product on Github or
any other platform again UNLESS you are in possession of an agreement
by the author OR use the function called "FORK" provided on Github itself.
Other parts of the specified license above are not affected.

68
README.rst Normal file
View File

@ -0,0 +1,68 @@
===========
firewalld-formula
===========
Salt Stack Formula to set up and configure Firewalld, dynamically managed firewall with support for network/firewall zones to define the trust level of network connections or interfaces
NOTICE BEFORE YOU USE
=====================
* This formula aims to follow the conventions and recommendations described at http://docs.saltstack.com/topics/conventions/formulas.html
TODO
====
* configure local pre-commit hooks (code syntax check based on file extension, check for ugly *utf-8 mac os white space*)
Instructions
============
1. Add this repository as a `GitFS <http://docs.saltstack.com/topics/tutorials/gitfs.html>`_ backend in your Salt master config.
2. Configure your Pillar top file (``/srv/pillar/top.sls``), see pillar.example
3. Include this Formula within another Formula or simply define your needed states within the Salt top file (``/srv/salt/top.sls``).
Available states
================
.. contents::
:local:
``firewalld``
-------
Manage firewalld
Additional resources
====================
None
Formula Dependencies
====================
None
Contributions
=============
Contributions are always welcome. All development guidelines you have to know are
* write clean code (proper YAML+Jinja syntax, no trailing whitespaces, no empty lines with whitespaces, LF only)
* set sane default settings
* test your code
* update README.rst doc
Salt Compatibility
==================
Tested with:
* 2014.1.x
OS Compatibility
================
Tested with:
* CentOS 7

1
VERSION Normal file
View File

@ -0,0 +1 @@
0.1.0

29
firewalld/_config.sls Normal file
View File

@ -0,0 +1,29 @@
# == State: firewalld._config
#
# This state configures firewalld.
#
/etc/firewalld/:
file.directory: # make sure this is a directory
- user: root
- group: root
- mode: 750
- require:
- pkg: firewalld # make sure package is installed
- watch_in:
- service: firewalld # restart service
/etc/firewalld/firewalld.conf:
file:
- managed
- name: /etc/firewalld/firewalld.conf
- user: root
- group: root
- mode: 640
- source: salt://firewalld/files/firewalld.conf
- template: jinja
- require:
- pkg: firewalld # make sure package is installed
- watch_in:
- service: firewalld # restart service

41
firewalld/_service.sls Normal file
View File

@ -0,0 +1,41 @@
# == State: firewalld._service
#
# This state ensures that /etc/firewalld/services/ exists.
#
/etc/firewalld/services:
file.directory: # make sure this is a directory
- user: root
- group: root
- mode: 750
- require:
- pkg: firewalld # make sure package is installed
- watch_in:
- service: firewalld # restart service
# == Define: firewalld._service
#
# This defines a service configuration, see firewalld.service (5) man page.
# You usually don't need this, you can simply add ports to zone.
{% for k, v in salt['pillar.get']('firewalld:services', {}).items() %}
{% set s_name = v.name|default(k) %}
/etc/firewalld/services/{{ s_name }}.xml:
file:
- managed
- name: /etc/firewalld/services/{{ s_name }}.xml
- user: root
- group: root
- mode: 644
- source: salt://firewalld/files/service.xml
- template: jinja
- require:
- pkg: firewalld # make sure package is installed
- watch_in:
- service: firewalld # restart service
- context:
name: {{ s_name }}
service: {{ v }}
{% endfor %}

140
firewalld/_zone.sls Normal file
View File

@ -0,0 +1,140 @@
# == State: firewalld._zone
#
# This state ensures that /etc/firewalld/zones/ exists.
#
/etc/firewalld/zones:
file.directory: # make sure this is a directory
- user: root
- group: root
- mode: 750
- require:
- pkg: firewalld # make sure package is installed
- watch_in:
- service: firewalld # restart service
# == Define: firewalld._zone
#
# This defines a zone configuration, see firewalld.zone (5) man page.
#
{% for k, v in salt['pillar.get']('firewalld:zones', {}).items() %}
{% set z_name = v.name|default(k) %}
/etc/firewalld/zones/{{ z_name }}.xml:
file:
- managed
- name: /etc/firewalld/zones/{{ z_name }}.xml
- user: root
- group: root
- mode: 644
- source: salt://firewalld/files/zone.xml
- template: jinja
- require:
- pkg: firewalld # make sure package is installed
- watch_in:
- service: firewalld # restart service
- context:
name: {{ z_name }}
zone: {{ v }}
{% endfor %}
# === Parameters
#
# [*target*] can be one of {'ACCEPT', '%%REJECT%%', 'DROP'}.
# Used to accept, reject or drop every packet that
# doesn't match any rule (port, service, etc.).
# Default (when target is not specified) is reject.
# [*short*] short readable name
# [*description*] long description of zone
# [*interfaces*] list of interfaces to bind to a zone
# [*sources*] list of source addresses or source address
# ranges ("address/mask") to bind to a zone
# [*ports*]
# list of ports to open
# ports => [{
# comment => optional, string
# port => mandatory, string, e.g. '1234'
# protocol => mandatory, string, e.g. 'tcp' },...]
# [*services*] list of predefined firewalld services
# [*icmp_blocks*] list of predefined icmp-types to block
# [*masquerade*] enable masquerading ?
# [*forward_ports*]
# list of ports to forward to other port and/or machine
# forward_ports => [{
# comment => optional, string
# portid => mandatory, string, e.g. '123'
# protocol => mandatory, string, e.g. 'tcp'
# to_port => mandatory to specify either to_port or/and to_addr
# to_addr => mandatory to specify either to_port or/and to_addr },...]
# [*rich_rules*]
# list of rich language rules (firewalld.richlanguage(5))
# You have to specify one (and only one)
# of {service, port, protocol, icmp_block, masquerade, forward_port}
# and one (and only one) of {accept, reject, drop}
# family - 'ipv4' or 'ipv6', optional, see Rule in firewalld.richlanguage(5)
# source => { optional, see Source in firewalld.richlanguage(5)
# address => mandatory, string, e.g. '192.168.1.0/24'
# invert => optional, bool, e.g. true }
# destination => { optional, see Destination in firewalld.richlanguage(5)
# address => mandatory, string
# invert => optional, bool, e.g. true }
# service - string, see Service in firewalld.richlanguage(5)
# port => { see Port in firewalld.richlanguage(5)
# portid => mandatory
# protocol => mandatory }
# protocol - string, see Protocol in firewalld.richlanguage(5)
# icmp_block - string, see ICMP-Block in firewalld.richlanguage(5)
# masquerade - bool, see Masquerade in firewalld.richlanguage(5)
# forward_port => { see Forward-Port in firewalld.richlanguage(5)
# portid => mandatory
# protocol => mandatory
# to_port => mandatory to specify either to_port or/and to_addr
# to_addr => mandatory to specify either to_port or/and to_addr }
# log => { see Log in firewalld.richlanguage(5)
# prefix => string, optional
# level => string, optional
# limit => string, optional }
# audit => { see Audit in firewalld.richlanguage(5)
# limit => string, optional }
# accept - any value, e.g. true, see Action in firewalld.richlanguage(5)
# reject => { see Action in firewalld.richlanguage(5)
# type => string, optional }
# drop - any value, e.g. true, see Action in firewalld.richlanguage(5)
#
# === Examples
#
# firewalld::zone { "custom":
# description => "This is an example zone",
# services => ["ssh", "dhcpv6-client"],
# ports => [{
# comment => "for our dummy service",
# port => "1234",
# protocol => "tcp",},],
# masquerade => true,
# forward_ports => [{
# comment => 'forward 123 to other machine',
# portid => '123',
# protocol => 'tcp',
# to_port => '321',
# to_addr => '1.2.3.4',},],
# rich_rules => [{
# family => 'ipv4',
# source => {
# address => '192.168.1.0/24',
# invert => true,},
# port => {
# portid => '123-321',
# protocol => 'udp',},
# log => {
# prefix => 'local',
# level => 'notice',
# limit => '3/s',},
# audit => {
# limit => '2/h',},
# reject => {
# type => 'icmp-host-prohibited',},
# },],}
#

View File

@ -0,0 +1,35 @@
{{pillar['headers']['salt']['file']}}
{% set firewalld = pillar.get('firewalld', {}) -%}
# firewalld config file
# default zone
# The default zone used if an empty zone string is used.
# Default: public
DefaultZone={{ firewalld.default_zone|default('public') }}
# Minimal mark
# Marks up to this minimum are free for use for example in the direct
# interface. If more free marks are needed, increase the minimum
# Default: 100
MinimalMark={{ firewalld.minimal_mark|default('100') }}
# Clean up on exit
# If set to no or false the firewall configuration will not get cleaned up
# on exit or stop of firewalld
# Default: yes
CleanupOnExit={{ firewalld.cleanup_on_exit|default('yes') }}
# Lockdown
# If set to enabled, firewall changes with the D-Bus interface will be limited
# to applications that are listed in the lockdown whitelist.
# The lockdown whitelist file is lockdown-whitelist.xml
# Default: no
Lockdown={{ firewalld.lockdown|default('no') }}
# IPv6_rpfilter
# Performs a reverse path filter test on a packet for IPv6. If a reply to the
# packet would be sent via the same interface that the packet arrived on, the
# packet will match and be accepted, otherwise dropped.
# The rp_filter for IPv4 is controlled using sysctl.
# Default: yes
IPv6_rpfilter={{ firewalld.IPv6_rpfilter|default('yes') }}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
{{pillar['headers']['salt']['xml']}}
<service>
{% if 'short' in service %}<short>{{ service.short }}</short>{% else %}<short>{{ name }}</short>{% endif %}
{% if 'description' in service %}<description>{{ service.description }}</description>{% endif %}
{% if 'ports' in service %}
{% if 'tcp' in service.ports %}
{% for v in service.ports.tcp %}<port port="{{ v }}" protocol="tcp" />{% endfor %}
{% endif %}
{% if 'udp' in service.ports %}
{% for v in service.ports.udp %}<port port="{{ v }}" protocol="udp" />{% endfor %}
{% endif %}
{% if 'modules' in service %}
{% for v in service.modules %}<module name="{{ v }}" />{% endfor %}
{% endif %}
{% endif %}
{% if 'destinations' in service %}
{% if 'ipv4' in service.destinations %}
{% for v in service.destinations.ipv4 %}<destination ipv4="{{ v }}" />{% endfor %}
{% endif %}
{% if 'ipv6' in service.destinations %}
{% for v in service.destinations.ipv6 %}<destination ipv6="{{ v }}" />{% endfor %}
{% endif %}
{% endif %}
</service>

97
firewalld/files/zone.xml Normal file
View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
{{pillar['headers']['salt']['xml']}}
<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 %}
{% if 'interfaces' in zone %}
{% for v in zone.interfaces %}<interface name="{{ v }}" />{% endfor %}
{% endif %}
{% if 'sources' in zone %}
{% for v in zone.sources %}<source name="{{ v }}" />{% endfor %}
{% endif %}
{% if 'services' in zone %}
{% for v in zone.services %}<service name="{{ v }}" />{% endfor %}
{% endif %}
{% if 'ports' in zone %}
{% for v in zone.ports %}
{% if 'comment' in v %}
<!-- {{ v.comment }} -->
{% endif %}
<port port="{{ v.port }}" protocol="{{ v.protocol }}"/>
{% endfor %}
{% endif %}
{% if 'icmp_blocks' in zone %}
{% for v in zone.icmp_blocks %}<icmp-block name="{{ v }}" />{% endfor %}
{% endif %}
{% if 'masquerade' in zone %}{% if zone.masquerade %}<masquerade/>{% endif %}{% endif %}
{% if 'forward_ports' in zone %}
{% for v in zone.forward_ports %}
{% if 'comment' in v %}
<!-- {{ v.comment }} -->
{% endif %}
<forward-port port="{{ v.portid }}" protocol="{{ v.protocol }}"{% if 'to_port' in v %} to-port="{{ v.to_port }}"{% endif %}{% if 'to_addr' in v %} to-addr="{{ v.to_addr }}"{% endif %} />
{% endfor %}
{% endif %}
{% if 'rich_rules' in zone %}
{% for rule in zone.rich_rules %}
{% if 'family' in rule %}
<rule family="{{ rule.family }}">
{% else %}
<rule>
{% 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 %}
<destination 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 '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 '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>
{% endfor %}
{% endif %}
</zone>

41
firewalld/init.sls Normal file
View File

@ -0,0 +1,41 @@
# == State: firewalld
#
# This state installs/runs firewalld.
#
{% if salt['pillar.get']('firewalld:enabled') %}
include:
- firewalld._config
- firewalld._service
- firewalld._zone
# iptables service that comes with rhel/centos
iptables:
service:
- disabled
- enable: False
ip6tables:
service:
- disabled
- enable: False
firewalld:
pkg:
- installed
service:
- running # ensure it's running
- enable: True # start on boot
- require:
- pkg: firewalld
- file: /etc/firewalld/firewalld.conf # require this file
- service: iptables # ensure it's stopped
- service: ip6tables # ensure it's stopped
{% else %}
firewalld:
service:
- dead # ensure it's not running
- enable: False # don't start on boot
{% endif %}

56
pillar.example.sls Normal file
View File

@ -0,0 +1,56 @@
# CentOS7 FirewallD firewall
firewalld:
enabled: True
default_zone: public
services:
sshcustom:
short: sshcustom
description: SSH on port 3232 and 5252. Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessing your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.
ports:
tcp:
- 3232
- 5252
modules:
- some_module_to_load
destinations:
ipv4:
- 224.0.0.251
- 224.0.0.252
ipv6:
- ff02::fb
- ff02::fc
zones:
public:
short: Public
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."
services:
- http
- https
- ssh
- dhcpv6-client
# ports:
# - comment: For our dummy service
# port: 1234
# protocol: tcp
# forward_ports:
# - comment: forward 123 to other machine
# portid: 123
# protocol: tcp
# to_port: 321
# to_addr: 1.2.3.4
# rich_rules:
# - family: ipv4
# source:
# address: 192.168.1.0/24
# invert: true
# port:
# portid: 123-321
# protocol: udp
# log:
# prefix: local
# level: notice
# limit: 3/s
# audit:
# limit: 2/h
# reject:
# type: icmp-host-prohibited