Merge pull request #214 from saito-hideki/pr/add_firewalld_info

Add new firewalld_info module to ansible.posix collection

SUMMARY
Add new firewalld_info module to ansible.posix collection.

Gathering information about firewalld zone settings
fixes #98

ISSUE TYPE

Feature Pull Request

COMPONENT NAME

ansible.posix.firewalld_info

ADDITIONAL INFORMATION
None

Reviewed-by: Abhijeet Kasurde <None>
Reviewed-by: Hideki Saito <saito@fgrep.org>
This commit is contained in:
ansible-zuul[bot] 2021-07-21 05:19:13 +00:00 committed by GitHub
commit a03e652437
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 523 additions and 0 deletions

View file

@ -0,0 +1,3 @@
---
trivial:
- firewalld_info - add ``firewalld_info`` module to ``ansible.posix`` collection (https://github.com/ansible-collections/ansible.posix/issues/98)

View file

@ -0,0 +1,391 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Hideki Saito <saito@fgrep.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: firewalld_info
short_description: Gather information about firewalld
description:
- This module gathers information about firewalld rules.
options:
active_zones:
description: Gather information about active zones.
type: bool
default: no
zones:
description:
- Gather information about specific zones.
- If only works if C(active_zones) is set to C(false).
required: false
type: list
elements: str
requirements:
- firewalld >= 0.2.11
- python-firewall
- python-dbus
author:
- Hideki Saito (@saito-hideki)
'''
EXAMPLES = r'''
- name: Gather information about active zones
ansible.posix.firewalld_info:
active_zones: yes
- name: Gather information about specific zones
ansible.posix.firewalld_info:
zones:
- public
- external
- internal
'''
RETURN = r'''
active_zones:
description:
- Gather active zones only if turn it C(true).
returned: success
type: bool
sample: false
collected_zones:
description:
- A list of collected zones.
returned: success
type: list
sample: [external, internal]
undefined_zones:
description:
- A list of undefined zones in C(zones) option.
- C(undefined_zones) will be ignored for gathering process.
returned: success
type: list
sample: [foo, bar]
firewalld_info:
description:
- Returns various information about firewalld configuration.
returned: success
type: complex
contains:
version:
description:
- The version information of firewalld.
returned: success
type: str
sample: 0.8.2
default_zones:
description:
- The zone name of default zone.
returned: success
type: str
sample: public
zones:
description:
- A dict of zones to gather information.
returned: success
type: complex
contains:
zone:
description:
- The zone name registered in firewalld.
returned: success
type: complex
sample: external
contains:
target:
description:
- A list of services in the zone.
returned: success
type: str
sample: ACCEPT
icmp_block_inversion:
description:
- The ICMP block inversion to block
all ICMP requests.
returned: success
type: bool
sample: false
interfaces:
description:
- A list of network interfaces.
returned: success
type: list
sample:
- 'eth0'
- 'eth1'
sources:
description:
- A list of source network address.
returned: success
type: list
sample:
- '172.16.30.0/24'
- '172.16.31.0/24'
services:
description:
- A list of network services.
returned: success
type: list
sample:
- 'dhcp'
- 'dns'
- 'ssh'
ports:
description:
- A list of network port with protocol.
returned: success
type: list
sample:
- - "22"
- "tcp"
- - "80"
- "tcp"
protocols:
description:
- A list of network protocol.
returned: success
type: list
sample:
- "icmp"
- "ipv6-icmp"
forward:
description:
- The network interface forwarding.
- This parameter supports on python-firewall
0.9.0(or later) and is not collected in earlier
versions.
returned: success
type: bool
sample: false
masquerade:
description:
- The network interface masquerading.
returned: success
type: bool
sample: false
forward_ports:
description:
- A list of forwarding port pair with protocol.
returned: success
type: list
sample:
- "icmp"
- "ipv6-icmp"
source_ports:
description:
- A list of network source port with protocol.
returned: success
type: list
sample:
- - "30000"
- "tcp"
- - "30001"
- "tcp"
icmp_blocks:
description:
- A list of blocking icmp protocol.
returned: success
type: list
sample:
- "echo-request"
rich_rules:
description:
- A list of rich language rule.
returned: success
type: list
sample:
- "rule protocol value=\"icmp\" reject"
- "rule priority=\"32767\" reject"
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
from distutils.version import StrictVersion
try:
import dbus
HAS_DBUS = True
except ImportError:
HAS_DBUS = False
try:
import firewall.client as fw_client
import firewall.config as fw_config
HAS_FIREWALLD = True
except ImportError:
HAS_FIREWALLD = False
def get_version():
return fw_config.VERSION
def get_active_zones(client):
return client.getActiveZones().keys()
def get_all_zones(client):
return client.getZones()
def get_default_zone(client):
return client.getDefaultZone()
def get_zone_settings(client, zone):
return client.getZoneSettings(zone)
def get_zone_target(zone_settings):
return zone_settings.getTarget()
def get_zone_icmp_block_inversion(zone_settings):
return zone_settings.getIcmpBlockInversion()
def get_zone_interfaces(zone_settings):
return zone_settings.getInterfaces()
def get_zone_sources(zone_settings):
return zone_settings.getSources()
def get_zone_services(zone_settings):
return zone_settings.getServices()
def get_zone_ports(zone_settings):
return zone_settings.getPorts()
def get_zone_protocols(zone_settings):
return zone_settings.getProtocols()
# This function supports python-firewall 0.9.0(or later).
def get_zone_forward(zone_settings):
return zone_settings.getForward()
def get_zone_masquerade(zone_settings):
return zone_settings.getMasquerade()
def get_zone_forward_ports(zone_settings):
return zone_settings.getForwardPorts()
def get_zone_source_ports(zone_settings):
return zone_settings.getSourcePorts()
def get_zone_icmp_blocks(zone_settings):
return zone_settings.getIcmpBlocks()
def get_zone_rich_rules(zone_settings):
return zone_settings.getRichRules()
def main():
module_args = dict(
active_zones=dict(required=False, type='bool', default=False),
zones=dict(required=False, type='list', elements='str'),
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
)
firewalld_info = dict()
result = dict(
changed=False,
active_zones=module.params['active_zones'],
collected_zones=list(),
undefined_zones=list(),
warnings=list(),
)
# Exit with failure message if requirements modules are not installed.
if not HAS_DBUS:
module.fail_json(msg=missing_required_lib('python-dbus'))
if not HAS_FIREWALLD:
module.fail_json(msg=missing_required_lib('python-firewall'))
# If you want to show warning messages in the task running process,
# you can append the message to the 'warn' list.
warn = list()
try:
client = fw_client.FirewallClient()
# Gather general information of firewalld.
firewalld_info['version'] = get_version()
firewalld_info['default_zone'] = get_default_zone(client)
# Gather information for zones.
zones_info = dict()
collect_zones = list()
ignore_zones = list()
if module.params['active_zones']:
collect_zones = get_active_zones(client)
elif module.params['zones']:
all_zones = get_all_zones(client)
specified_zones = module.params['zones']
collect_zones = list(set(specified_zones) & set(all_zones))
ignore_zones = list(set(specified_zones) - set(collect_zones))
warn.append(
'Please note: zone:(%s) have been ignored in the gathering process.' % ','.join(ignore_zones))
else:
collect_zones = get_all_zones(client)
for zone in collect_zones:
# Gather settings for each zone based on the output of
# 'firewall-cmd --info-zone=<ZONE>' command.
zone_info = dict()
zone_settings = get_zone_settings(client, zone)
zone_info['target'] = get_zone_target(zone_settings)
zone_info['icmp_block_inversion'] = get_zone_icmp_block_inversion(zone_settings)
zone_info['interfaces'] = get_zone_interfaces(zone_settings)
zone_info['sources'] = get_zone_sources(zone_settings)
zone_info['services'] = get_zone_services(zone_settings)
zone_info['ports'] = get_zone_ports(zone_settings)
zone_info['protocols'] = get_zone_protocols(zone_settings)
zone_info['masquerade'] = get_zone_masquerade(zone_settings)
zone_info['forward_ports'] = get_zone_forward_ports(zone_settings)
zone_info['source_ports'] = get_zone_source_ports(zone_settings)
zone_info['icmp_blocks'] = get_zone_icmp_blocks(zone_settings)
zone_info['rich_rules'] = get_zone_rich_rules(zone_settings)
# The 'forward' parameter supports on python-firewall 0.9.0(or later).
if StrictVersion(firewalld_info['version']) >= StrictVersion('0.9.0'):
zone_info['forward'] = get_zone_forward(zone_settings)
zones_info[zone] = zone_info
firewalld_info['zones'] = zones_info
except AttributeError as e:
module.fail_json(msg=('firewalld probably not be running, Or the following method '
'is not supported with your python-firewall version. (Error: %s)') % to_native(e))
except dbus.exceptions.DBusException as e:
module.fail_json(msg=('Unable to gather firewalld settings.'
' You may need to run as the root user or'
' use become. (Error: %s)' % to_native(e)))
result['collected_zones'] = collect_zones
result['undefined_zones'] = ignore_zones
result['firewalld_info'] = firewalld_info
result['warnings'] = warn
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,5 @@
destructive
shippable/posix/group3
skip/aix
skip/freebsd
skip/osx

View file

@ -0,0 +1,52 @@
# Test playbook for the firewalld_info module
# (c) 2021, Hideki Saito <saito@fgrep.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This test is based on the integration test playbook for firewalld module.
- name: Run firewalld tests
block:
- name: Ensure firewalld is installed
package:
name: firewalld
state: present
# This doesn't work for CentOS 6 because firewalld doesn't exist in CentOS6
- name: Check to make sure the firewalld python module is available.
shell: "{{ansible_python.executable}} -c 'import firewall'"
register: check_output_firewall
ignore_errors: true
- name: Check to make sure the dbus python module is available.
shell: "{{ansible_python.executable}} -c 'import dbus'"
register: check_output_dbus
ignore_errors: true
- name: Test Online Operations
block:
- name: start firewalld
service:
name: firewalld
state: started
- import_tasks: run_tests_in_started.yml
when:
- check_output_firewall.rc == 0
- check_output_dbus.rc == 0
- name: Test Offline Operations
block:
- name: stop firewalld
service:
name: firewalld
state: stopped
- import_tasks: run_tests_in_stopped.yml
when:
- check_output_firewall.rc == 0
- check_output_dbus.rc == 0
when:
- ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('7', '>=')
- not (ansible_distribution == "Ubuntu" and ansible_distribution_version is version('14.04', '=='))
# Firewalld package on OpenSUSE (15+) require Python 3, so we skip on OpenSUSE running py2 on these newer distros
- not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and ansible_python.version.major != 3)

View file

@ -0,0 +1,32 @@
# Test playbook for the firewalld_info module
# (c) 2021, Hideki Saito <saito@fgrep.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- name: Ensure firewalld_info without options
firewalld_info:
register: result
- name: Assert collected_zones and undefined_zones
assert:
that:
- 'result.collected_zones and not result.undefined_zones'
- name: Ensure firewalld_info with active_zones
firewalld_info:
active_zones: yes
register: result
- name: Assert turn active_zones true
assert:
that:
- name: Ensure firewalld_zones with zone list
firewalld_info:
zones:
- public
- invalid_zone
register: result
- name: Assert specified zones
assert:
that:

View file

@ -0,0 +1,40 @@
# Test playbook for the firewalld_info module
# (c) 2021, Hideki Saito <saito@fgrep.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- name: Ensure firewalld_info without options
firewalld_info:
register: result
ignore_errors: yes
- name: Assert firewalld_info fails if firewalld is not running.
assert:
that:
- result.failed
- "'firewalld probably not be running,' in result.msg"
- name: Ensure firewalld_info with active_zones
firewalld_info:
active_zones: yes
register: result
ignore_errors: yes
- name: Assert firewalld_info with active_zones fails if firewalld is not running.
assert:
that:
- result.failed
- "'firewalld probably not be running,' in result.msg"
- name: Ensure firewalld_zones with zone list
firewalld_info:
zones:
- public
- invalid_zone
register: result
ignore_errors: yes
- name: Assert firewalld_info with zones list fails if firewalld is not running.
assert:
that:
- result.failed
- "'firewalld probably not be running,' in result.msg"