vmware_host_firewall_manager: fix #61332
Since https://github.com/ansible/ansible/pull/56733, we were not able to apply firewall rules with no `allowed_hosts` key. closes: #61332 In addition, this patch ensures the `allowed_hosts` key accepts a dict, instead of a dict in a single entry list. ```yaml vmware_host_firewall_manager: esxi_hostname: "{{ esxi1 }}" rules: - name: NFC enabled: True allowed_hosts: - all_ip: False ip_address: - "1.2.3.4" ``` Should be written: ```yaml vmware_host_firewall_manager: esxi_hostname: "{{ esxi1 }}" rules: - name: NFC enabled: True allowed_hosts: all_ip: False ip_address: - "1.2.3.4" ```
This commit is contained in:
parent
358574d57f
commit
039c770a95
4 changed files with 170 additions and 109 deletions
|
@ -0,0 +1,4 @@
|
|||
bugfixes:
|
||||
- vmware_host_firewall_manager - Ensure we can set rule with no ``allowed_hosts`` key (https://github.com/ansible/ansible/issues/61332)
|
||||
minor_changes:
|
||||
- vmware_host_firewall_manager - ``allowed_hosts`` excpects a dict as parameter, list is deprecated
|
|
@ -99,23 +99,24 @@ EXAMPLES = r'''
|
|||
- name: gdbserver
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
- all_ip: False
|
||||
ip_address:
|
||||
192.168.20.10
|
||||
all_ip: False
|
||||
ip_address:
|
||||
- 192.168.20.10
|
||||
- 192.168.20.11
|
||||
- name: CIMHttpServer
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
- all_ip: False
|
||||
ip_network:
|
||||
192.168.100.0/24
|
||||
all_ip: False
|
||||
ip_network:
|
||||
- 192.168.100.0/24
|
||||
- name: remoteSerialPort
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
- all_ip: False
|
||||
ip_address:
|
||||
192.168.100.11
|
||||
ip_network:
|
||||
192.168.200.0/24
|
||||
all_ip: False
|
||||
ip_address:
|
||||
- 192.168.100.11
|
||||
ip_network:
|
||||
- 192.168.200.0/24
|
||||
delegate_to: localhost
|
||||
'''
|
||||
|
||||
|
@ -203,6 +204,43 @@ class VmwareFirewallManager(PyVmomi):
|
|||
temp_rule_dict['allowed_hosts'] = rule_allow_host
|
||||
self.firewall_facts[host.name][rule_set_obj.key] = temp_rule_dict
|
||||
|
||||
def check_params(self):
|
||||
rules_by_host = {}
|
||||
for host in self.hosts:
|
||||
rules_by_host[host.name] = self.firewall_facts[host.name].keys()
|
||||
|
||||
for rule_option in self.rule_options:
|
||||
rule_name = rule_option.get('name')
|
||||
if rule_name is None:
|
||||
self.module.fail_json(msg="Please specify rule.name for rule set"
|
||||
" as it is required parameter.")
|
||||
hosts_with_rule_name = [h for h, r in rules_by_host.items() if rule_name in r]
|
||||
hosts_without_rule_name = set([i.name for i in self.hosts]) - set(hosts_with_rule_name)
|
||||
if hosts_without_rule_name:
|
||||
self.module.fail_json(msg="rule named '%s' wasn't found on hosts: %s" % (
|
||||
rule_name, hosts_without_rule_name))
|
||||
|
||||
if 'enabled' not in rule_option:
|
||||
self.module.fail_json(msg="Please specify rules.enabled for rule set"
|
||||
" %s as it is required parameter." % rule_name)
|
||||
|
||||
allowed_hosts = rule_option.get('allowed_hosts', {})
|
||||
ip_addresses = allowed_hosts.get('ip_address', [])
|
||||
ip_networks = allowed_hosts.get('ip_network', [])
|
||||
for ip_address in ip_addresses:
|
||||
try:
|
||||
ipaddress.ip_address(ip_address)
|
||||
except ValueError:
|
||||
self.module.fail_json(msg="The provided IP address %s is not a valid IP"
|
||||
" for the rule %s" % (ip_address, rule_name))
|
||||
|
||||
for ip_network in ip_networks:
|
||||
try:
|
||||
ipaddress.ip_network(ip_network)
|
||||
except ValueError:
|
||||
self.module.fail_json(msg="The provided IP network %s is not a valid network"
|
||||
" for the rule %s" % (ip_network, rule_name))
|
||||
|
||||
def ensure(self):
|
||||
"""
|
||||
Function to ensure rule set configuration
|
||||
|
@ -216,47 +254,15 @@ class VmwareFirewallManager(PyVmomi):
|
|||
firewall_system = host.configManager.firewallSystem
|
||||
if firewall_system is None:
|
||||
continue
|
||||
|
||||
results['rule_set_state'][host.name] = dict()
|
||||
|
||||
results['rule_set_state'][host.name] = {}
|
||||
for rule_option in self.rule_options:
|
||||
rule_name = rule_option.get('name', None)
|
||||
if rule_name is None:
|
||||
self.module.fail_json(msg="Please specify rule.name for rule set"
|
||||
" as it is required parameter.")
|
||||
if rule_name not in self.firewall_facts[host.name]:
|
||||
self.module.fail_json(msg="rule named '%s' wasn't found." % rule_name)
|
||||
|
||||
rule_enabled = rule_option.get('enabled', None)
|
||||
if rule_enabled is None:
|
||||
self.module.fail_json(msg="Please specify rules.enabled for rule set"
|
||||
" %s as it is required parameter." % rule_name)
|
||||
|
||||
# validate IP addresses are valid
|
||||
rule_config = rule_option.get('allowed_hosts', None)
|
||||
|
||||
if 'ip_address' in rule_config[0].keys():
|
||||
for ip_addr in rule_config[0]['ip_address']:
|
||||
try:
|
||||
ip = ipaddress.ip_address(ip_addr)
|
||||
except ValueError:
|
||||
self.module.fail_json(msg="The provided IP address %s is not a valid IP"
|
||||
" for the rule %s" % (ip_addr, rule_name))
|
||||
|
||||
# validate provided subnets are valid networks
|
||||
if 'ip_network' in rule_config[0].keys():
|
||||
for ip_net in rule_config[0]['ip_network']:
|
||||
try:
|
||||
network_validation = ipaddress.ip_network(ip_net)
|
||||
except ValueError:
|
||||
self.module.fail_json(msg="The provided network %s is not a valid network"
|
||||
" for the rule %s" % (ip_net, rule_name))
|
||||
|
||||
current_rule_state = self.firewall_facts[host.name][rule_name]['enabled']
|
||||
if current_rule_state != rule_enabled:
|
||||
if current_rule_state != rule_option['enabled']:
|
||||
try:
|
||||
if not self.module.check_mode:
|
||||
if rule_enabled:
|
||||
if rule_option['enabled']:
|
||||
firewall_system.EnableRuleset(id=rule_name)
|
||||
else:
|
||||
firewall_system.DisableRuleset(id=rule_name)
|
||||
|
@ -264,27 +270,31 @@ class VmwareFirewallManager(PyVmomi):
|
|||
enable_disable_changed = True
|
||||
except vim.fault.NotFound as not_found:
|
||||
self.module.fail_json(msg="Failed to enable rule set %s as"
|
||||
" rule set id is unknown : %s" % (rule_name,
|
||||
to_native(not_found.msg)))
|
||||
" rule set id is unknown : %s" % (
|
||||
rule_name,
|
||||
to_native(not_found.msg)))
|
||||
except vim.fault.HostConfigFault as host_config_fault:
|
||||
self.module.fail_json(msg="Failed to enabled rule set %s as an internal"
|
||||
" error happened while reconfiguring"
|
||||
" rule set : %s" % (rule_name,
|
||||
to_native(host_config_fault.msg)))
|
||||
" rule set : %s" % (
|
||||
rule_name,
|
||||
to_native(host_config_fault.msg)))
|
||||
|
||||
# save variables here for comparison later and change tracking
|
||||
# also covers cases where inputs may be null
|
||||
permitted_networking = self.firewall_facts[host.name][rule_name]
|
||||
rule_allows_all = permitted_networking['allowed_hosts']['all_ip']
|
||||
playbook_allows_all = rule_config[0]['all_ip']
|
||||
rule_allowed_ip = set(permitted_networking['allowed_hosts']['ip_address'])
|
||||
playbook_allowed_ip = set(rule_config[0].get('ip_address', ''))
|
||||
rule_allowed_ips = set(permitted_networking['allowed_hosts']['ip_address'])
|
||||
rule_allowed_networks = set(permitted_networking['allowed_hosts']['ip_network'])
|
||||
playbook_allowed_networks = set(rule_config[0].get('ip_network', ''))
|
||||
|
||||
allowed_hosts = rule_option.get('allowed_hosts', {})
|
||||
playbook_allows_all = allowed_hosts.get('all_ip', False)
|
||||
playbook_allowed_ips = set(allowed_hosts.get('ip_address', []))
|
||||
playbook_allowed_networks = set(allowed_hosts.get('ip_network', []))
|
||||
|
||||
# compare what is configured on the firewall rule with what the playbook provides
|
||||
allowed_all_ips_different = bool(rule_allows_all != playbook_allows_all)
|
||||
ip_list_different = bool(rule_allowed_ip != playbook_allowed_ip)
|
||||
ip_list_different = bool(rule_allowed_ips != playbook_allowed_ips)
|
||||
ip_network_different = bool(rule_allowed_networks != playbook_allowed_networks)
|
||||
|
||||
# apply everything here in one function call
|
||||
|
@ -295,16 +305,16 @@ class VmwareFirewallManager(PyVmomi):
|
|||
# setup spec
|
||||
firewall_spec = vim.host.Ruleset.RulesetSpec()
|
||||
firewall_spec.allowedHosts = vim.host.Ruleset.IpList()
|
||||
firewall_spec.allowedHosts.allIp = rule_config[0].get('all_ip', True)
|
||||
firewall_spec.allowedHosts.ipAddress = rule_config[0].get('ip_address', None)
|
||||
firewall_spec.allowedHosts.allIp = playbook_allows_all
|
||||
firewall_spec.allowedHosts.ipAddress = list(playbook_allowed_ips)
|
||||
firewall_spec.allowedHosts.ipNetwork = []
|
||||
|
||||
if 'ip_network' in rule_config[0].keys():
|
||||
for allowed_network in rule_config[0].get('ip_network', None):
|
||||
tmp_ip_network_spec = vim.host.Ruleset.IpNetwork()
|
||||
tmp_ip_network_spec.network = allowed_network.split("/")[0]
|
||||
tmp_ip_network_spec.prefixLength = int(allowed_network.split("/")[1])
|
||||
firewall_spec.allowedHosts.ipNetwork.append(tmp_ip_network_spec)
|
||||
for i in playbook_allowed_networks:
|
||||
address, mask = i.split('/')
|
||||
tmp_ip_network_spec = vim.host.Ruleset.IpNetwork()
|
||||
tmp_ip_network_spec.network = address
|
||||
tmp_ip_network_spec.prefixLength = int(mask)
|
||||
firewall_spec.allowedHosts.ipNetwork.append(tmp_ip_network_spec)
|
||||
|
||||
firewall_system.UpdateRuleset(id=rule_name, spec=firewall_spec)
|
||||
except vim.fault.NotFound as not_found:
|
||||
|
@ -321,19 +331,22 @@ class VmwareFirewallManager(PyVmomi):
|
|||
" error happened while applying the reconfiguration:"
|
||||
" %s" % (rule_name, to_native(runtime_fault.msg)))
|
||||
|
||||
results['rule_set_state'][host.name][rule_name] = dict(current_state=rule_enabled,
|
||||
previous_state=current_rule_state,
|
||||
desired_state=rule_enabled,
|
||||
current_allowed_all=playbook_allows_all,
|
||||
previous_allowed_all=permitted_networking['allowed_hosts']['all_ip'],
|
||||
desired_allowed_all=playbook_allows_all,
|
||||
current_allowed_ip=playbook_allowed_ip,
|
||||
previous_allowed_ip=set(permitted_networking['allowed_hosts']['ip_address']),
|
||||
desired_allowed_ip=playbook_allowed_ip,
|
||||
current_allowed_networks=playbook_allowed_networks,
|
||||
previous_allowed_networks=set(permitted_networking['allowed_hosts']['ip_network']),
|
||||
desired_allowed_networks=playbook_allowed_networks
|
||||
)
|
||||
results['rule_set_state'][host.name][rule_name] = {
|
||||
'current_state': rule_option['enabled'],
|
||||
'previous_state': current_rule_state,
|
||||
'desired_state': rule_option['enabled'],
|
||||
'allowed_hosts': {
|
||||
'current_allowed_all': playbook_allows_all,
|
||||
'previous_allowed_all': permitted_networking['allowed_hosts']['all_ip'],
|
||||
'desired_allowed_all': playbook_allows_all,
|
||||
'current_allowed_ip': playbook_allowed_ips,
|
||||
'previous_allowed_ip': set(permitted_networking['allowed_hosts']['ip_address']),
|
||||
'desired_allowed_ip': playbook_allowed_ips,
|
||||
'current_allowed_networks': playbook_allowed_networks,
|
||||
'previous_allowed_networks': set(permitted_networking['allowed_hosts']['ip_network']),
|
||||
'desired_allowed_networks': playbook_allowed_networks,
|
||||
}
|
||||
}
|
||||
|
||||
if enable_disable_changed or allowed_ip_changed:
|
||||
fw_change_list.append(True)
|
||||
|
@ -359,7 +372,16 @@ def main():
|
|||
supports_check_mode=True
|
||||
)
|
||||
|
||||
for rule_option in module.params.get("rules", []):
|
||||
if 'allowed_hosts' in rule_option:
|
||||
if isinstance(rule_option['allowed_hosts'], list):
|
||||
if len(rule_option['allowed_hosts']) == 1:
|
||||
allowed_hosts = rule_option['allowed_hosts'][0]
|
||||
rule_option['allowed_hosts'] = allowed_hosts
|
||||
module.deprecate('allowed_hosts should be a dict, not a list', '2.13')
|
||||
|
||||
vmware_firewall_manager = VmwareFirewallManager(module)
|
||||
vmware_firewall_manager.check_params()
|
||||
vmware_firewall_manager.ensure()
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
---
|
||||
|
||||
- name: Clean up the firewall rules
|
||||
vmware_host_firewall_manager:
|
||||
cluster_name: '{{ ccr1 }}'
|
||||
rules:
|
||||
- name: vvold
|
||||
enabled: False
|
||||
- name: CIMHttpServer
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
all_ip: True
|
||||
- name: NFC
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
all_ip: True
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Remove the VM prepared by prepare_vmware_tests
|
||||
vmware_guest:
|
||||
name: "{{ item.name }}"
|
||||
|
|
|
@ -8,17 +8,6 @@
|
|||
vars:
|
||||
setup_attach_host: true
|
||||
|
||||
- name: Disable the Firewall vvold rule on the ESXi
|
||||
vmware_host_firewall_manager:
|
||||
hostname: '{{ vcenter_hostname }}'
|
||||
username: '{{ vcenter_username }}'
|
||||
password: '{{ vcenter_password }}'
|
||||
cluster_name: '{{ ccr1 }}'
|
||||
validate_certs: no
|
||||
rules:
|
||||
- name: vvold
|
||||
enabled: False
|
||||
|
||||
- name: Enable vvold rule set on all hosts of {{ ccr1 }}
|
||||
vmware_host_firewall_manager:
|
||||
hostname: "{{ vcenter_hostname }}"
|
||||
|
@ -139,11 +128,12 @@
|
|||
- name: CIMHttpServer
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
- all_ip: False
|
||||
ip_address:
|
||||
- "192.168.100.11"
|
||||
ip_network:
|
||||
- "192.168.200.0/24"
|
||||
all_ip: False
|
||||
ip_address:
|
||||
- "192.168.100.11"
|
||||
- "192.168.100.12"
|
||||
ip_network:
|
||||
- "192.168.200.0/24"
|
||||
register: all_hosts_ip_specific
|
||||
- debug: var=all_hosts_ip_specific
|
||||
- name: ensure everything is changed for all hosts of {{ ccr1 }}
|
||||
|
@ -161,9 +151,11 @@
|
|||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['current_allowed_all'] == False
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['previous_allowed_all'] == True
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['desired_allowed_all'] == False
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['current_allowed_ip'] == ["192.168.100.11"]
|
||||
- "'192.168.100.11' in all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['current_allowed_ip']"
|
||||
- "'192.168.100.12' in all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['current_allowed_ip']"
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['previous_allowed_ip'] == []
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['desired_allowed_ip'] == ["192.168.100.11"]
|
||||
- "'192.168.100.11' in all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['desired_allowed_ip']"
|
||||
- "'192.168.100.12' in all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['desired_allowed_ip']"
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['current_allowed_networks'] == ["192.168.200.0/24"]
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['previous_allowed_networks'] == []
|
||||
- all_hosts_ip_specific.rule_set_state[item]['CIMHttpServer']['allowed_hosts']['desired_allowed_networks'] == ["192.168.200.0/24"]
|
||||
|
@ -182,23 +174,49 @@
|
|||
- name: NFC
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
- all_ip: False
|
||||
ip_address:
|
||||
- "192.168.100.11"
|
||||
all_ip: False
|
||||
ip_address:
|
||||
- "192.168.100.11"
|
||||
register: single_host_ip_specific
|
||||
- set_fact:
|
||||
nfc_state: "{{ single_host_ip_specific.rule_set_state[esxi1]['NFC'] }}"
|
||||
- debug: var=single_host_ip_specific
|
||||
- debug: var=nfc_state
|
||||
- name: ensure NFC is configured on that host
|
||||
assert:
|
||||
that:
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['current_state'] == True
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['desired_state'] == True
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['previous_state'] == True
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['current_allowed_all'] == False
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['previous_allowed_all'] == True
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['desired_allowed_all'] == False
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['current_allowed_ip'] == ["192.168.100.11"]
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['previous_allowed_ip'] == None
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['desired_allowed_ip'] == ["192.168.100.11"]
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['current_allowed_networks'] == []
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['previous_allowed_networks'] == []
|
||||
- single_host_ip_specific.rule_set_state[{{ esxi1 }}]['NFC']['allowed_hosts']['desired_allowed_networks'] == []
|
||||
- nfc_state.current_state == true
|
||||
- nfc_state.desired_state == true
|
||||
- nfc_state.previous_state == true
|
||||
- nfc_state.allowed_hosts.current_allowed_all == False
|
||||
- nfc_state.allowed_hosts.previous_allowed_all == True
|
||||
- nfc_state.allowed_hosts.desired_allowed_all == False
|
||||
- nfc_state.allowed_hosts.current_allowed_ip == ["192.168.100.11"]
|
||||
- nfc_state.allowed_hosts.previous_allowed_all == True
|
||||
- nfc_state.allowed_hosts.desired_allowed_ip == ["192.168.100.11"]
|
||||
- nfc_state.allowed_hosts.current_allowed_networks == []
|
||||
- nfc_state.allowed_hosts.previous_allowed_networks == []
|
||||
- nfc_state.allowed_hosts.desired_allowed_networks == []
|
||||
|
||||
- name: Ensure we can still pass the allowed_hosts configuration through a list for compat
|
||||
vmware_host_firewall_manager:
|
||||
hostname: "{{ vcenter_hostname }}"
|
||||
username: "{{ vcenter_username }}"
|
||||
password: "{{ vcenter_password }}"
|
||||
validate_certs: no
|
||||
esxi_hostname: "{{ esxi1 }}"
|
||||
rules:
|
||||
- name: NFC
|
||||
enabled: True
|
||||
allowed_hosts:
|
||||
- all_ip: False
|
||||
ip_address:
|
||||
- "1.2.3.4"
|
||||
register: using_list
|
||||
- debug: var=using_list
|
||||
- set_fact:
|
||||
nfc_state: "{{ using_list.rule_set_state[esxi1]['NFC'] }}"
|
||||
- name: ensure the correct host is set
|
||||
assert:
|
||||
that:
|
||||
- nfc_state.allowed_hosts.current_allowed_ip == ["1.2.3.4"]
|
||||
|
|
Loading…
Reference in a new issue