Skip to content
Snippets Groups Projects
Commit 3bd68ac0 authored by Rafael Guterres Jeffman's avatar Rafael Guterres Jeffman
Browse files

ipadnszone: Add support for per-zone privilege delegation

IPA DNS Zones management can be delegated by adding a "Manage DNS zone"
permission. The CLI commands that manage these permissions are
dnszone-add-permission and dnszone-remove-permission.

The ansible-freeipa module ipadnszone did not have this capability, and
it now support dnszone per-zone management delegation by setting the
module parameter 'permission'. If set to 'true' the permission will be
assigned to the zone, if set to false the permission will be removed.
parent 0f2c3761
No related branches found
No related tags found
No related merge requests found
...@@ -133,6 +133,22 @@ Example playbook to enable a zone: ...@@ -133,6 +133,22 @@ Example playbook to enable a zone:
state: enabled state: enabled
``` ```
Example playbook to allow per-zone privilege delegation:
``` yaml
---
- name: Playbook to enable per-zone privilege delegation
hosts: ipaserver
become: true
tasks:
- name: Enable privilege delegation.
ipadnszone:
ipaadmin_password: SomeADMINpassword
name: testzone.local
permission: true
```
Example playbook to remove a zone: Example playbook to remove a zone:
```yaml ```yaml
...@@ -223,6 +239,7 @@ Variable | Description | Required ...@@ -223,6 +239,7 @@ Variable | Description | Required
`ttl`| Time to live for records at zone apex | no `ttl`| Time to live for records at zone apex | no
`default_ttl`| Time to live for records without explicit TTL definition | no `default_ttl`| Time to live for records without explicit TTL definition | no
`nsec3param_rec`| NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt | no `nsec3param_rec`| NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt | no
`permission` \| `managedby` | Set per-zone access delegation permission. | no
`skip_overlap_check`| Force DNS zone creation even if it will overlap with an existing zone | no `skip_overlap_check`| Force DNS zone creation even if it will overlap with an existing zone | no
`skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no `skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
...@@ -238,4 +255,6 @@ Variable | Description | Returned When ...@@ -238,4 +255,6 @@ Variable | Description | Returned When
Authors Authors
======= =======
Sergio Oliveira Campos - Sergio Oliveira Campos
- Thomas Woerner
- Rafael Jeffman
...@@ -142,6 +142,11 @@ options: ...@@ -142,6 +142,11 @@ options:
salt. salt.
required: false required: false
type: str type: str
permission:
description: Set per-zone access delegation permission.
required: false
type: bool
aliases: ["managedby"]
skip_overlap_check: skip_overlap_check:
description: | description: |
Force DNS zone creation even if it will overlap with an existing zone Force DNS zone creation even if it will overlap with an existing zone
...@@ -154,6 +159,7 @@ options: ...@@ -154,6 +159,7 @@ options:
author: author:
- Sergio Oliveira Campos (@seocam) - Sergio Oliveira Campos (@seocam)
- Thomas Woerner (@t-woerner) - Thomas Woerner (@t-woerner)
- Rafael Jeffman (@rjeffman)
""" # noqa: E501 """ # noqa: E501
EXAMPLES = """ EXAMPLES = """
...@@ -253,6 +259,9 @@ class DNSZoneModule(IPAAnsibleModule): ...@@ -253,6 +259,9 @@ class DNSZoneModule(IPAAnsibleModule):
"idnsallowdynupdate": "dynamic_update", "idnsallowdynupdate": "dynamic_update",
"idnssecinlinesigning": "dnssec", "idnssecinlinesigning": "dnssec",
"idnsupdatepolicy": "update_policy", "idnsupdatepolicy": "update_policy",
# FreeIPA uses 'managedby' for dnszone and dnsforwardzone
# to manage 'permissions'.
"managedby": "permission",
# Mapping by method # Mapping by method
"idnsforwarders": self.get_ipa_idnsforwarders, "idnsforwarders": self.get_ipa_idnsforwarders,
"idnsallowtransfer": self.get_ipa_idnsallowtransfer, "idnsallowtransfer": self.get_ipa_idnsallowtransfer,
...@@ -434,7 +443,7 @@ class DNSZoneModule(IPAAnsibleModule): ...@@ -434,7 +443,7 @@ class DNSZoneModule(IPAAnsibleModule):
is_zone_active = False is_zone_active = False
else: else:
zone = response["result"] zone = response["result"]
# FreeIPA 4.9.10+ and 4.10 use proper mapping for boolean vaalues. # FreeIPA 4.9.10+ and 4.10 use proper mapping for boolean values.
# See: https://github.com/freeipa/freeipa/pull/6294 # See: https://github.com/freeipa/freeipa/pull/6294
is_zone_active = ( is_zone_active = (
str(zone.get("idnszoneactive")[0]).upper() == "TRUE" str(zone.get("idnszoneactive")[0]).upper() == "TRUE"
...@@ -462,18 +471,24 @@ class DNSZoneModule(IPAAnsibleModule): ...@@ -462,18 +471,24 @@ class DNSZoneModule(IPAAnsibleModule):
self.fail_json( self.fail_json(
msg="Either `name` or `name_from_ip` must be provided." msg="Either `name` or `name_from_ip` must be provided."
) )
# check invalid parameters
invalid = []
if self.ipa_params.state != "present": if self.ipa_params.state != "present":
invalid = ["name_from_ip"] invalid .extend(["name_from_ip"])
if self.ipa_params.state == "absent":
invalid.extend(["permission"])
self.params_fail_used_invalid(invalid, self.ipa_params.state) self.params_fail_used_invalid(invalid, self.ipa_params.state)
def define_ipa_commands(self): def define_ipa_commands(self):
for zone_name in self.get_zone_names(): for zone_name in self.get_zone_names():
# Look for existing zone in IPA # Look for existing zone in IPA
zone, is_zone_active = self.get_zone(zone_name) zone, is_zone_active = self.get_zone(zone_name)
args = self.ipa_params.get_ipa_command_args(zone=zone)
if self.ipa_params.state in ["present", "enabled", "disabled"]: if self.ipa_params.state in ["present", "enabled", "disabled"]:
args = self.ipa_params.get_ipa_command_args(zone=zone)
# We'll handle "managedby" after dnszone add/mod.
args.pop("managedby", None)
if not zone: if not zone:
# Since the zone doesn't exist we just create it # Since the zone doesn't exist we just create it
# with given args # with given args
...@@ -487,6 +502,16 @@ class DNSZoneModule(IPAAnsibleModule): ...@@ -487,6 +502,16 @@ class DNSZoneModule(IPAAnsibleModule):
if not compare_args_ipa(self, args, zone): if not compare_args_ipa(self, args, zone):
self.commands.append((zone_name, "dnszone_mod", args)) self.commands.append((zone_name, "dnszone_mod", args))
# Permissions must be set on existing zones.
if self.ipa_params.permission is not None:
is_managed = zone.get("managedby")
if self.ipa_params.permission and not is_managed:
self.commands.append(
(zone_name, "dnszone_add_permission", {}))
if not self.ipa_params.permission and is_managed:
self.commands.append(
(zone_name, "dnszone_remove_permission", {}))
if self.ipa_params.state == "enabled" and not is_zone_active: if self.ipa_params.state == "enabled" and not is_zone_active:
self.commands.append((zone_name, "dnszone_enable", {})) self.commands.append((zone_name, "dnszone_enable", {}))
...@@ -555,6 +580,8 @@ def get_argument_spec(): ...@@ -555,6 +580,8 @@ def get_argument_spec():
ttl=dict(type="int", required=False, default=None), ttl=dict(type="int", required=False, default=None),
default_ttl=dict(type="int", required=False, default=None), default_ttl=dict(type="int", required=False, default=None),
nsec3param_rec=dict(type="str", required=False, default=None), nsec3param_rec=dict(type="str", required=False, default=None),
permission=dict(type="bool", required=False, default=None,
aliases=["managedby"]),
skip_nameserver_check=dict(type="bool", required=False, default=None), skip_nameserver_check=dict(type="bool", required=False, default=None),
skip_overlap_check=dict(type="bool", required=False, default=None), skip_overlap_check=dict(type="bool", required=False, default=None),
) )
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
hosts: "{{ ipa_test_host | default('ipaserver') }}" hosts: "{{ ipa_test_host | default('ipaserver') }}"
become: true become: true
gather_facts: true gather_facts: true
module_defaults:
ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
tasks: tasks:
...@@ -13,8 +17,6 @@ ...@@ -13,8 +17,6 @@
# Tests # Tests
- name: Check if zone is present, when it shouldn't be. - name: Check if zone is present, when it shouldn't be.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: present state: present
check_mode: yes check_mode: yes
...@@ -23,8 +25,6 @@ ...@@ -23,8 +25,6 @@
- name: Check if zone is present again, when it shouldn't be. - name: Check if zone is present again, when it shouldn't be.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: present state: present
check_mode: yes check_mode: yes
...@@ -33,8 +33,6 @@ ...@@ -33,8 +33,6 @@
- name: Ensure zone is present. - name: Ensure zone is present.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: present state: present
register: result register: result
...@@ -42,8 +40,6 @@ ...@@ -42,8 +40,6 @@
- name: Check if zone is present, when it should be. - name: Check if zone is present, when it should be.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: present state: present
check_mode: yes check_mode: yes
...@@ -52,8 +48,6 @@ ...@@ -52,8 +48,6 @@
- name: Ensure zone is present, again. - name: Ensure zone is present, again.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: present state: present
register: result register: result
...@@ -61,8 +55,6 @@ ...@@ -61,8 +55,6 @@
- name: Ensure zone is disabled. - name: Ensure zone is disabled.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: disabled state: disabled
register: result register: result
...@@ -70,8 +62,6 @@ ...@@ -70,8 +62,6 @@
- name: Ensure zone is disabled, again. - name: Ensure zone is disabled, again.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: disabled state: disabled
register: result register: result
...@@ -79,8 +69,6 @@ ...@@ -79,8 +69,6 @@
- name: Ensure zone is enabled. - name: Ensure zone is enabled.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: enabled state: enabled
register: result register: result
...@@ -88,8 +76,6 @@ ...@@ -88,8 +76,6 @@
- name: Ensure zone is enabled, again. - name: Ensure zone is enabled, again.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
state: enabled state: enabled
register: result register: result
...@@ -97,8 +83,6 @@ ...@@ -97,8 +83,6 @@
- name: Ensure forward_policy is none. - name: Ensure forward_policy is none.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forward_policy: none forward_policy: none
register: result register: result
...@@ -106,8 +90,6 @@ ...@@ -106,8 +90,6 @@
- name: Ensure forward_policy is none, again. - name: Ensure forward_policy is none, again.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forward_policy: none forward_policy: none
register: result register: result
...@@ -115,8 +97,6 @@ ...@@ -115,8 +97,6 @@
- name: Ensure forward_policy is first. - name: Ensure forward_policy is first.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forward_policy: first forward_policy: first
register: result register: result
...@@ -124,8 +104,6 @@ ...@@ -124,8 +104,6 @@
- name: Ensure forward_policy is first, again. - name: Ensure forward_policy is first, again.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forward_policy: first forward_policy: first
register: result register: result
...@@ -133,8 +111,6 @@ ...@@ -133,8 +111,6 @@
- name: Ensure first forwarder is set. - name: Ensure first forwarder is set.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forwarders: forwarders:
- ip_address: 8.8.8.8 - ip_address: 8.8.8.8
...@@ -144,8 +120,6 @@ ...@@ -144,8 +120,6 @@
- name: Ensure first and second forwarder are set. - name: Ensure first and second forwarder are set.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forwarders: forwarders:
- ip_address: 8.8.8.8 - ip_address: 8.8.8.8
...@@ -156,8 +130,6 @@ ...@@ -156,8 +130,6 @@
- name: Ensure first and second forwarder are set, again. - name: Ensure first and second forwarder are set, again.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forwarders: forwarders:
- ip_address: 8.8.8.8 - ip_address: 8.8.8.8
...@@ -168,8 +140,6 @@ ...@@ -168,8 +140,6 @@
- name: Ensure only second forwarder is set. - name: Ensure only second forwarder is set.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forwarders: forwarders:
- ip_address: 2001:4860:4860::8888 - ip_address: 2001:4860:4860::8888
...@@ -178,16 +148,12 @@ ...@@ -178,16 +148,12 @@
- name: Nothing changes. - name: Nothing changes.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
register: result register: result
failed_when: result.changed or result.failed failed_when: result.changed or result.failed
- name: Ensure no forwarders are set. - name: Ensure no forwarders are set.
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: testzone.local name: testzone.local
forwarders: [] forwarders: []
register: result register: result
...@@ -195,56 +161,70 @@ ...@@ -195,56 +161,70 @@
- name: Create zones test1 - name: Create zones test1
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: test1.testzone.local name: test1.testzone.local
register: result register: result
failed_when: not result.changed or result.failed failed_when: not result.changed or result.failed
- name: Create zones test1, again - name: Create zones test1, again
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: test1.testzone.local name: test1.testzone.local
register: result register: result
failed_when: result.changed or result.failed failed_when: result.changed or result.failed
- name: Create zones test2 - name: Create zones test2
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: test2.testzone.local name: test2.testzone.local
register: result register: result
failed_when: not result.changed or result.failed failed_when: not result.changed or result.failed
- name: Create zones test2, again - name: Create zones test2, again
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: test2.testzone.local name: test2.testzone.local
register: result register: result
failed_when: result.changed or result.failed failed_when: result.changed or result.failed
- name: Create zones test3 - name: Create zones test3
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: test3.testzone.local name: test3.testzone.local
register: result register: result
failed_when: not result.changed or result.failed failed_when: not result.changed or result.failed
- name: Create zones test3, again - name: Create zones test3, again
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: test3.testzone.local name: test3.testzone.local
register: result register: result
failed_when: result.changed or result.failed failed_when: result.changed or result.failed
- name: Ensure zone test1.testzone.local has management permissioon
ipadnszone:
name: test1.testzone.local
permission: true
register: result
failed_when: not result.changed or result.failed
- name: Ensure zone test1.testzone.local has management permissioon
ipadnszone:
name: test1.testzone.local
permission: true
register: result
failed_when: result.changed or result.failed
- name: Ensure zone test1.testzone.local don't have management permissioon
ipadnszone:
name: test1.testzone.local
permission: false
register: result
failed_when: not result.changed or result.failed
- name: Ensure zone test1.testzone.local don't have management permissioon
ipadnszone:
name: test1.testzone.local
permission: false
register: result
failed_when: result.changed or result.failed
- name: Ensure multiple zones are absent - name: Ensure multiple zones are absent
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: name:
- test1.testzone.local - test1.testzone.local
- test2.testzone.local - test2.testzone.local
...@@ -255,8 +235,6 @@ ...@@ -255,8 +235,6 @@
- name: Ensure multiple zones are absent, again - name: Ensure multiple zones are absent, again
ipadnszone: ipadnszone:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
name: name:
- test1.testzone.local - test1.testzone.local
- test2.testzone.local - test2.testzone.local
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment