mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-01-11 23:25:28 +01:00
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:
commit
a03e652437
6 changed files with 523 additions and 0 deletions
3
changelogs/fragments/214-add_firewalld_info_module.yml
Normal file
3
changelogs/fragments/214-add_firewalld_info_module.yml
Normal 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)
|
||||||
391
plugins/modules/firewalld_info.py
Normal file
391
plugins/modules/firewalld_info.py
Normal 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()
|
||||||
5
tests/integration/targets/firewalld_info/aliases
Normal file
5
tests/integration/targets/firewalld_info/aliases
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
destructive
|
||||||
|
shippable/posix/group3
|
||||||
|
skip/aix
|
||||||
|
skip/freebsd
|
||||||
|
skip/osx
|
||||||
52
tests/integration/targets/firewalld_info/tasks/main.yml
Normal file
52
tests/integration/targets/firewalld_info/tasks/main.yml
Normal 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)
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -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"
|
||||||
Loading…
Reference in a new issue