Merge pull request #1147 from rjeffman/dnszone_permission

ipadnszone: Add support for per-zone privilege delegation
This commit is contained in:
Thomas Woerner
2023-12-20 11:21:35 +01:00
committed by GitHub
3 changed files with 84 additions and 60 deletions

View File

@@ -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
`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
Authors Authors
======= =======
Sergio Oliveira Campos - Sergio Oliveira Campos
- Thomas Woerner
- Rafael Jeffman

View File

@@ -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:
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):
"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):
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):
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":
self.params_fail_used_invalid(invalid, self.ipa_params.state) invalid.extend(["permission"])
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):
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():
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),
) )

View File

@@ -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 @@
# 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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 @@
- 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