import ansible-freeipa-0.1.12-6.el8

This commit is contained in:
CentOS Sources 2020-11-03 06:58:13 -05:00 committed by Stepan Oksanichenko
parent 0695dc66b6
commit d2eec6cc08
27 changed files with 4993 additions and 3669 deletions

View File

@ -1 +1 @@
583ac570c030eb68a2026a506054f2f93587beb4 SOURCES/ansible-freeipa-0.1.8.tar.gz 5d09d3b590e8568d04edb288c9c515e308f3168f SOURCES/ansible-freeipa-0.1.12.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/ansible-freeipa-0.1.8.tar.gz SOURCES/ansible-freeipa-0.1.12.tar.gz

View File

@ -0,0 +1,556 @@
From abbd15e6f50718119b4dd0380913d2d646eb7638 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Mon, 3 Aug 2020 19:23:07 -0300
Subject: [PATCH] Add support for option `name_from_ip` in ipadnszone module.
IPA CLI has an option `name_from_ip` that provide a name for a zone
from the reverse IP address, so that it can be used to, for example,
manage PTR DNS records.
This patch adds a similar attribute to ipadnszone module, where it
will try to find the proper zone name, using DNS resolve, or provide
a sane default, if a the zone name cannot be resolved.
The option `name_from_ip` must be used instead of `name` in playbooks,
and it is a string, and not a list.
A new example playbook was added:
playbooks/dnszone/dnszone-reverse-from-ip.yml
A new test playbook was added:
tests/dnszone/test_dnszone_name_from_ip.yml
---
README-dnszone.md | 3 +-
playbooks/dnszone/dnszone-reverse-from-ip.yml | 10 ++
plugins/modules/ipadnszone.py | 65 +++++++++-
tests/dnszone/test_dnszone_name_from_ip.yml | 112 ++++++++++++++++++
4 files changed, 186 insertions(+), 4 deletions(-)
create mode 100644 playbooks/dnszone/dnszone-reverse-from-ip.yml
create mode 100644 tests/dnszone/test_dnszone_name_from_ip.yml
diff --git a/README-dnszone.md b/README-dnszone.md
index 9c9b12c..48b019a 100644
--- a/README-dnszone.md
+++ b/README-dnszone.md
@@ -163,7 +163,8 @@ Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
-`name` \| `zone_name` | The zone name string or list of strings. | yes
+`name` \| `zone_name` | The zone name string or list of strings. | no
+`name_from_ip` | Derive zone name from reverse of IP (PTR). | no
`forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
&nbsp; | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
&nbsp; | `port` - The custom port that should be used on this server. | no
diff --git a/playbooks/dnszone/dnszone-reverse-from-ip.yml b/playbooks/dnszone/dnszone-reverse-from-ip.yml
new file mode 100644
index 0000000..5693872
--- /dev/null
+++ b/playbooks/dnszone/dnszone-reverse-from-ip.yml
@@ -0,0 +1,10 @@
+---
+- name: Playbook to ensure DNS zone exist
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Ensure zone exist, finding zone name from IP address.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 10.1.2.3
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index c5e812a..901bfef 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -43,6 +43,10 @@ options:
required: true
type: list
alises: ["zone_name"]
+ name_from_ip:
+ description: Derive zone name from reverse of IP (PTR).
+ required: false
+ type: str
forwarders:
description: The list of global DNS forwarders.
required: false
@@ -197,6 +201,12 @@ from ansible.module_utils.ansible_freeipa_module import (
is_ipv6_addr,
is_valid_port,
) # noqa: E402
+import netaddr
+import six
+
+
+if six.PY3:
+ unicode = str
class DNSZoneModule(FreeIPABaseModule):
@@ -354,6 +364,31 @@ class DNSZoneModule(FreeIPABaseModule):
if not zone and self.ipa_params.skip_nameserver_check is not None:
return self.ipa_params.skip_nameserver_check
+ def __reverse_zone_name(self, ipaddress):
+ """
+ Infer reverse zone name from an ip address.
+
+ This function uses the same heuristics as FreeIPA to infer the zone
+ name from ip.
+ """
+ try:
+ ip = netaddr.IPAddress(str(ipaddress))
+ except (netaddr.AddrFormatError, ValueError):
+ net = netaddr.IPNetwork(ipaddress)
+ items = net.ip.reverse_dns.split('.')
+ prefixlen = net.prefixlen
+ ip_version = net.version
+ else:
+ items = ip.reverse_dns.split('.')
+ prefixlen = 24 if ip.version == 4 else 64
+ ip_version = ip.version
+ if ip_version == 4:
+ return u'.'.join(items[4 - prefixlen // 8:])
+ elif ip_version == 6:
+ return u'.'.join(items[32 - prefixlen // 4:])
+ else:
+ self.fail_json(msg="Invalid IP version for reverse zone.")
+
def get_zone(self, zone_name):
get_zone_args = {"idnsname": zone_name, "all": True}
response = self.api_command("dnszone_find", args=get_zone_args)
@@ -368,14 +403,33 @@ class DNSZoneModule(FreeIPABaseModule):
return zone, is_zone_active
def get_zone_names(self):
- if len(self.ipa_params.name) > 1 and self.ipa_params.state != "absent":
+ zone_names = self.__get_zone_names_from_params()
+ if len(zone_names) > 1 and self.ipa_params.state != "absent":
self.fail_json(
msg=("Please provide a single name. Multiple values for 'name'"
"can only be supplied for state 'absent'.")
)
+ return zone_names
+
+ def __get_zone_names_from_params(self):
+ if not self.ipa_params.name:
+ return [self.__reverse_zone_name(self.ipa_params.name_from_ip)]
return self.ipa_params.name
+ def check_ipa_params(self):
+ if not self.ipa_params.name and not self.ipa_params.name_from_ip:
+ self.fail_json(
+ msg="Either `name` or `name_from_ip` must be provided."
+ )
+ if self.ipa_params.state != "present" and self.ipa_params.name_from_ip:
+ self.fail_json(
+ msg=(
+ "Cannot use argument `name_from_ip` with state `%s`."
+ % self.ipa_params.state
+ )
+ )
+
def define_ipa_commands(self):
for zone_name in self.get_zone_names():
# Look for existing zone in IPA
@@ -434,8 +488,9 @@ def get_argument_spec():
ipaadmin_principal=dict(type="str", default="admin"),
ipaadmin_password=dict(type="str", required=False, no_log=True),
name=dict(
- type="list", default=None, required=True, aliases=["zone_name"]
+ type="list", default=None, required=False, aliases=["zone_name"]
),
+ name_from_ip=dict(type="str", default=None, required=False),
forwarders=dict(
type="list",
default=None,
@@ -475,7 +530,11 @@ def get_argument_spec():
def main():
- DNSZoneModule(argument_spec=get_argument_spec()).ipa_run()
+ DNSZoneModule(
+ argument_spec=get_argument_spec(),
+ mutually_exclusive=[["name", "name_from_ip"]],
+ required_one_of=[["name", "name_from_ip"]],
+ ).ipa_run()
if __name__ == "__main__":
diff --git a/tests/dnszone/test_dnszone_name_from_ip.yml b/tests/dnszone/test_dnszone_name_from_ip.yml
new file mode 100644
index 0000000..9bd2eb0
--- /dev/null
+++ b/tests/dnszone/test_dnszone_name_from_ip.yml
@@ -0,0 +1,112 @@
+---
+- name: Test dnszone
+ hosts: ipaserver
+ become: yes
+ gather_facts: yes
+
+ tasks:
+
+ # Setup
+ - name: Ensure zone is absent.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - 2.0.192.in-addr.arpa.
+ - 0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f.ip6.arpa.
+ - 1.0.0.0.e.f.a.c.8.b.d.0.1.0.0.2.ip6.arpa.
+
+ # tests
+ - name: Ensure zone exists for reverse IP.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 192.0.2.3/24
+ register: ipv4_zone
+ failed_when: not ipv4_zone.changed or ipv4_zone.failed
+
+ - name: Ensure zone exists for reverse IP, again.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 192.0.2.3/24
+ register: result
+ failed_when: result.changed or result.failed
+
+ - name: Ensure zone exists for reverse IP, given the zone name.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: "{{ ipv4_zone.dnszone.name }}"
+ register: result
+ failed_when: result.changed or result.failed
+
+ - name: Modify existing zone, using `name_from_ip`.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 192.0.2.3/24
+ default_ttl: 1234
+ register: result
+ failed_when: not result.changed
+
+ - name: Modify existing zone, using `name_from_ip`, again.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 192.0.2.3/24
+ default_ttl: 1234
+ register: result
+ failed_when: result.changed or result.failed
+
+ - name: Ensure ipv6 zone exists for reverse IPv6.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: fd00::0001
+ register: ipv6_zone
+ failed_when: not ipv6_zone.changed or ipv6_zone.failed
+
+ # - debug:
+ # msg: "{{ipv6_zone}}"
+
+ - name: Ensure ipv6 zone was created.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: "{{ ipv6_zone.dnszone.name }}"
+ register: result
+ failed_when: result.changed or result.failed
+
+ - name: Ensure ipv6 zone exists for reverse IPv6, again.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: fd00::0001
+ register: result
+ failed_when: result.changed
+
+ - name: Ensure second ipv6 zone exists for reverse IPv6.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 2001:db8:cafe:1::1
+ register: ipv6_sec_zone
+ failed_when: not ipv6_sec_zone.changed or ipv6_zone.failed
+
+ - name: Ensure second ipv6 zone was created.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: "{{ ipv6_sec_zone.dnszone.name }}"
+ register: result
+ failed_when: result.changed or result.failed
+
+ - name: Ensure second ipv6 zone exists for reverse IPv6, again.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 2001:db8:cafe:1::1
+ register: result
+ failed_when: result.changed
+
+ # Cleanup
+ - name: Ensure zone is absent.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - "{{ ipv6_zone.dnszone.name }}"
+ - "{{ ipv6_sec_zone.dnszone.name }}"
+ - "{{ ipv4_zone.dnszone.name }}"
--
2.26.2
From 531e544b30e69f436d14c4ce18c67998c1a0774b Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Wed, 5 Aug 2020 15:13:46 -0300
Subject: [PATCH] Added support for client defined result data in
FReeIPABaseModule
Modified support for processing result of IPA API commands so that
client code can define its own processing and add return values to
self.exit_args based on command result.
If a subclass need to process the result of IPA API commands it should
override the method `process_command_result`. The default implementation
will simply evaluate if `changed` should be true.
---
.../module_utils/ansible_freeipa_module.py | 22 +++++++++++++------
plugins/modules/ipadnszone.py | 8 +++++++
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 4799e5a..30302b4 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -619,7 +619,7 @@ class FreeIPABaseModule(AnsibleModule):
if exc_val:
self.fail_json(msg=str(exc_val))
- self.exit_json(changed=self.changed, user=self.exit_args)
+ self.exit_json(changed=self.changed, **self.exit_args)
def get_command_errors(self, command, result):
"""Look for erros into command results."""
@@ -658,14 +658,22 @@ class FreeIPABaseModule(AnsibleModule):
except Exception as excpt:
self.fail_json(msg="%s: %s: %s" % (command, name, str(excpt)))
else:
- if "completed" in result:
- if result["completed"] > 0:
- self.changed = True
- else:
- self.changed = True
-
+ self.process_command_result(name, command, args, result)
self.get_command_errors(command, result)
+ def process_command_result(self, name, command, args, result):
+ """
+ Process an API command result.
+
+ This method can be overriden in subclasses, and change self.exit_values
+ to return data in the result for the controller.
+ """
+ if "completed" in result:
+ if result["completed"] > 0:
+ self.changed = True
+ else:
+ self.changed = True
+
def require_ipa_attrs_change(self, command_args, ipa_attrs):
"""
Compare given args with current object attributes.
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index 901bfef..6a90fa2 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -472,6 +472,14 @@ class DNSZoneModule(FreeIPABaseModule):
}
self.add_ipa_command("dnszone_mod", zone_name, args)
+ def process_command_result(self, name, command, args, result):
+ super(DNSZoneModule, self).process_command_result(
+ name, command, args, result
+ )
+ if command == "dnszone_add" and self.ipa_params.name_from_ip:
+ dnszone_exit_args = self.exit_args.setdefault('dnszone', {})
+ dnszone_exit_args['name'] = name
+
def get_argument_spec():
forwarder_spec = dict(
--
2.26.2
From 41e8226d0c03e06816626d78cecbc2aebf547691 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Wed, 5 Aug 2020 15:14:43 -0300
Subject: [PATCH] Return the zone_name when adding a zone with name_from_ip.
When adding a zone using the option name_from_ip, the user have
little control over the final name of the zone, and if this name
is to be used in further processing in a playbook it might lead to
errors if the inferred name does not match what the user wanted to.
By returning the actual inferred zone name, the name can be safely
used for other tasks in the playbook.
---
README-dnszone.md | 11 +++++++++++
playbooks/dnszone/dnszone-reverse-from-ip.yml | 7 ++++++-
plugins/modules/ipadnszone.py | 8 ++++++++
3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/README-dnszone.md b/README-dnszone.md
index 48b019a..3f4827b 100644
--- a/README-dnszone.md
+++ b/README-dnszone.md
@@ -190,6 +190,17 @@ Variable | Description | Required
`skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
+Return Values
+=============
+
+ipadnszone
+----------
+
+Variable | Description | Returned When
+-------- | ----------- | -------------
+`dnszone` | DNS Zone dict with zone name infered from `name_from_ip`. <br>Options: | If `state` is `present`, `name_from_ip` is used, and a zone was created.
+&nbsp; | `name` - The name of the zone created, inferred from `name_from_ip`. | Always
+
Authors
=======
diff --git a/playbooks/dnszone/dnszone-reverse-from-ip.yml b/playbooks/dnszone/dnszone-reverse-from-ip.yml
index 5693872..218a318 100644
--- a/playbooks/dnszone/dnszone-reverse-from-ip.yml
+++ b/playbooks/dnszone/dnszone-reverse-from-ip.yml
@@ -7,4 +7,9 @@
- name: Ensure zone exist, finding zone name from IP address.
ipadnszone:
ipaadmin_password: SomeADMINpassword
- name_from_ip: 10.1.2.3
+ name_from_ip: 10.1.2.3/24
+ register: result
+
+ - name: Zone name inferred from `name_from_ip`
+ debug:
+ msg: "Zone created: {{ result.dnszone.name }}"
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index 6a90fa2..93eac07 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -192,6 +192,14 @@ EXAMPLES = """
"""
RETURN = """
+dnszone:
+ description: DNS Zone dict with zone name infered from `name_from_ip`.
+ returned:
+ If `state` is `present`, `name_from_ip` is used, and a zone was created.
+ options:
+ name:
+ description: The name of the zone created, inferred from `name_from_ip`.
+ returned: always
"""
from ipapython.dnsutil import DNSName # noqa: E402
--
2.26.2
From 46bbc7bbd7a4e01d07b0390aee8c799aaa5ac895 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Mon, 17 Aug 2020 15:52:38 -0300
Subject: [PATCH] Document usage of `name_from_ip`.
Since `name_from_ip` has a similar, but not equal, behavior to `name`,
and as the inferred DNS zone might depend on DNS configuration and
can be different than the user expects, it has some limited usage,
and the user must be aware of its effects.
This change to the documentation enhance the documentation including
more details on the attribute usage.
---
README-dnszone.md | 42 ++++++++++++++++++++++++++++++++++-
plugins/modules/ipadnszone.py | 4 +++-
2 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/README-dnszone.md b/README-dnszone.md
index 3f4827b..c5a7ab3 100644
--- a/README-dnszone.md
+++ b/README-dnszone.md
@@ -152,6 +152,46 @@ Example playbook to remove a zone:
```
+Example playbook to create a zone for reverse DNS lookup, from an IP address:
+
+```yaml
+
+---
+- name: dnszone present
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Ensure zone for reverse DNS lookup is present.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 192.168.1.2
+ state: present
+```
+
+Note that, on the previous example the zone created with `name_from_ip` might be "1.168.192.in-addr.arpa.", "168.192.in-addr.arpa.", or "192.in-addr.arpa.", depending on the DNS response the system get while querying for zones, and for this reason, when creating a zone using `name_from_ip`, the inferred zone name is returned to the controller, in the attribute `dnszone.name`. Since the zone inferred might not be what a user expects, `name_from_ip` can only be used with `state: present`. To have more control over the zone name, the prefix length for the IP address can be provided.
+
+Example playbook to create a zone for reverse DNS lookup, from an IP address, given the prefix length and displaying the resulting zone name:
+
+```yaml
+
+---
+- name: dnszone present
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Ensure zone for reverse DNS lookup is present.
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name_from_ip: 192.168.1.2/24
+ state: present
+ register: result
+ - name: Display inferred zone name.
+ debug:
+ msg: "Zone name: {{ result.dnszone.name }}"
+```
+
Variables
=========
@@ -164,7 +204,7 @@ Variable | Description | Required
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
`name` \| `zone_name` | The zone name string or list of strings. | no
-`name_from_ip` | Derive zone name from reverse of IP (PTR). | no
+`name_from_ip` | Derive zone name from reverse of IP (PTR). Can only be used with `state: present`. | no
`forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
&nbsp; | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
&nbsp; | `port` - The custom port that should be used on this server. | no
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index 93eac07..ff6bfff 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -44,7 +44,9 @@ options:
type: list
alises: ["zone_name"]
name_from_ip:
- description: Derive zone name from reverse of IP (PTR).
+ description: |
+ Derive zone name from reverse of IP (PTR).
+ Can only be used with `state: present`.
required: false
type: str
forwarders:
--
2.26.2

View File

@ -0,0 +1,435 @@
From 78b635ae78346fdfb298dd0d0c82ae1ff34b754a Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Tue, 23 Jun 2020 17:53:47 -0300
Subject: [PATCH] Add suppport for changing password of symmetric vaults.
Allows changing passwords of symmetric waults, using a new variable
`new_password` (or the file-base version, `new_password_file`). The
old password must be passed using the `password` or `password_file`
variables that also received new aliases `old_password` and
`old_password_file`, respectively.
Tests were modyfied to reflect the changes.
---
README-vault.md | 23 +++-
.../vault/change-password-symmetric-vault.yml | 2 +-
plugins/modules/ipavault.py | 129 +++++++++++++++---
tests/vault/test_vault_symmetric.yml | 64 +++++++++
4 files changed, 194 insertions(+), 24 deletions(-)
diff --git a/README-vault.md b/README-vault.md
index c7ae6916..fa1d3e11 100644
--- a/README-vault.md
+++ b/README-vault.md
@@ -165,6 +165,22 @@ Example playbook to make sure vault data is absent in a symmetric vault:
state: absent
```
+Example playbook to change the password of a symmetric:
+
+```yaml
+---
+- name: Playbook to handle vaults
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ old_password: SomeVAULTpassword
+ new_password: SomeNEWpassword
+```
+
Example playbook to make sure vault is absent:
```yaml
@@ -197,8 +213,11 @@ Variable | Description | Required
`name` \| `cn` | The list of vault name strings. | yes
`description` | The vault description string. | no
`nomembers` | Suppress processing of membership attributes. (bool) | no
-`password ` \| `vault_password` \| `ipavaultpassword` | Vault password. | no
-`public_key ` \| `vault_public_key` \| `ipavaultpublickey` | Base64 encoded vault public key. | no
+`password` \| `vault_password` \| `ipavaultpassword` \| `old_password`| Vault password. | no
+`password_file` \| `vault_password_file` \| `old_password_file`| File containing Base64 encoded Vault password. | no
+`new_password` | Vault new password. | no
+`new_password_file` | File containing Base64 encoded new Vault password. | no
+`public_key ` \| `vault_public_key` \| `old_password_file` | Base64 encoded vault public key. | no
`public_key_file` \| `vault_public_key_file` | Path to file with public key. | no
`private_key `\| `vault_private_key` | Base64 encoded vault private key. Used only to retrieve data. | no
`private_key_file` \| `vault_private_key_file` | Path to file with private key. Used only to retrieve data. | no
diff --git a/playbooks/vault/change-password-symmetric-vault.yml b/playbooks/vault/change-password-symmetric-vault.yml
index 3871f45d..396a79f6 100644
--- a/playbooks/vault/change-password-symmetric-vault.yml
+++ b/playbooks/vault/change-password-symmetric-vault.yml
@@ -10,7 +10,7 @@
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeVAULTpassword
- - name: Change vault passord.
+ - name: Change vault password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index ad5dd413..46c6fcdb 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -69,12 +69,20 @@
description: password to be used on symmetric vault.
required: false
type: string
- aliases: ["ipavaultpassword", "vault_password"]
+ aliases: ["ipavaultpassword", "vault_password", "old_password"]
password_file:
description: file with password to be used on symmetric vault.
required: false
type: string
- aliases: ["vault_password_file"]
+ aliases: ["vault_password_file", "old_password_file"]
+ new_password:
+ description: new password to be used on symmetric vault.
+ required: false
+ type: string
+ new_password_file:
+ description: file with new password to be used on symmetric vault.
+ required: false
+ type: string
salt:
description: Vault salt.
required: false
@@ -235,7 +243,15 @@
state: retrieved
register: result
- debug:
- msg: "{{ result.data | b64decode }}"
+ msg: "{{ result.data }}"
+
+# Change password of a symmetric vault
+- ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ username: admin
+ old_password: SomeVAULTpassword
+ new_password: SomeNEWpassword
# Ensure vault symvault is absent
- ipavault:
@@ -416,18 +432,29 @@ def check_parameters(module, state, action, description, username, service,
shared, users, groups, services, owners, ownergroups,
ownerservices, vault_type, salt, password, password_file,
public_key, public_key_file, private_key,
- private_key_file, vault_data, datafile_in, datafile_out):
+ private_key_file, vault_data, datafile_in, datafile_out,
+ new_password, new_password_file):
invalid = []
if state == "present":
invalid = ['private_key', 'private_key_file', 'datafile_out']
+ if all([password, password_file]) \
+ or all([new_password, new_password_file]):
+ module.fail_json(msg="Password specified multiple times.")
+
+ if any([new_password, new_password_file]) \
+ and not any([password, password_file]):
+ module.fail_json(
+ msg="Either `password` or `password_file` must be provided to "
+ "change symmetric vault password.")
+
if action == "member":
invalid.extend(['description'])
elif state == "absent":
invalid = ['description', 'salt', 'vault_type', 'private_key',
'private_key_file', 'datafile_in', 'datafile_out',
- 'vault_data']
+ 'vault_data', 'new_password', 'new_password_file']
if action == "vault":
invalid.extend(['users', 'groups', 'services', 'owners',
@@ -437,7 +464,7 @@ def check_parameters(module, state, action, description, username, service,
elif state == "retrieved":
invalid = ['description', 'salt', 'datafile_in', 'users', 'groups',
'owners', 'ownergroups', 'public_key', 'public_key_file',
- 'vault_data']
+ 'vault_data', 'new_password', 'new_password_file']
if action == 'member':
module.fail_json(
msg="State `retrieved` do not support action `member`.")
@@ -458,11 +485,17 @@ def check_parameters(module, state, action, description, username, service,
def check_encryption_params(module, state, action, vault_type, salt,
password, password_file, public_key,
public_key_file, private_key, private_key_file,
- vault_data, datafile_in, datafile_out, res_find):
+ vault_data, datafile_in, datafile_out,
+ new_password, new_password_file, res_find):
vault_type_invalid = []
+
+ if res_find is not None:
+ vault_type = res_find['ipavaulttype']
+
if vault_type == "standard":
vault_type_invalid = ['public_key', 'public_key_file', 'password',
- 'password_file', 'salt']
+ 'password_file', 'salt', 'new_password',
+ 'new_password_file']
if vault_type is None or vault_type == "symmetric":
vault_type_invalid = ['public_key', 'public_key_file',
@@ -473,8 +506,14 @@ def check_encryption_params(module, state, action, vault_type, salt,
msg="Symmetric vault requires password or password_file "
"to store data or change `salt`.")
+ if any([new_password, new_password_file]) and res_find is None:
+ module.fail_json(
+ msg="Cannot modify password of inexistent vault.")
+
if vault_type == "asymmetric":
- vault_type_invalid = ['password', 'password_file']
+ vault_type_invalid = [
+ 'password', 'password_file', 'new_password', 'new_password_file'
+ ]
if not any([public_key, public_key_file]) and res_find is None:
module.fail_json(
msg="Assymmetric vault requires public_key "
@@ -487,6 +526,43 @@ def check_encryption_params(module, state, action, vault_type, salt,
(param, vault_type or 'symmetric'))
+def change_password(module, res_find, password, password_file, new_password,
+ new_password_file):
+ """
+ Change the password of a symmetric vault.
+
+ To change the password of a vault, it is needed to retrieve the stored
+ data with the current password, and store the data again, with the new
+ password, forcing it to override the old one.
+ """
+ # verify parameters.
+ if not any([new_password, new_password_file]):
+ return []
+ if res_find["ipavaulttype"][0] != "symmetric":
+ module.fail_json(msg="Cannot change password of `%s` vault."
+ % res_find["ipavaulttype"])
+
+ # prepare arguments to retrieve data.
+ name = res_find["cn"][0]
+ args = {}
+ if password:
+ args["password"] = password
+ if password_file:
+ args["password"] = password_file
+ # retrieve current stored data
+ result = api_command(module, 'vault_retrieve', name, args)
+ args['data'] = result['result']['data']
+
+ # modify arguments to store data with new password.
+ if password:
+ args["password"] = new_password
+ if password_file:
+ args["password"] = new_password_file
+ args["override_password"] = True
+ # return the command to store data with the new password.
+ return [(name, "vault_archive", args)]
+
+
def main():
ansible_module = AnsibleModule(
argument_spec=dict(
@@ -533,10 +609,18 @@ def main():
datafile_out=dict(type="str", required=False, default=None,
aliases=['out']),
vault_password=dict(type="str", required=False, default=None,
- aliases=['ipavaultpassword', 'password'],
- no_log=True),
+ no_log=True,
+ aliases=['ipavaultpassword', 'password',
+ "old_password"]),
vault_password_file=dict(type="str", required=False, default=None,
- no_log=False, aliases=['password_file']),
+ no_log=False,
+ aliases=[
+ 'password_file', "old_password_file"
+ ]),
+ new_password=dict(type="str", required=False, default=None,
+ no_log=True),
+ new_password_file=dict(type="str", required=False, default=None,
+ no_log=False),
# state
action=dict(type="str", default="vault",
choices=["vault", "data", "member"]),
@@ -546,6 +630,7 @@ def main():
supports_check_mode=True,
mutually_exclusive=[['username', 'service', 'shared'],
['datafile_in', 'vault_data'],
+ ['new_password', 'new_password_file'],
['vault_password', 'vault_password_file'],
['vault_public_key', 'vault_public_key_file']],
)
@@ -576,6 +661,8 @@ def main():
salt = module_params_get(ansible_module, "vault_salt")
password = module_params_get(ansible_module, "vault_password")
password_file = module_params_get(ansible_module, "vault_password_file")
+ new_password = module_params_get(ansible_module, "new_password")
+ new_password_file = module_params_get(ansible_module, "new_password_file")
public_key = module_params_get(ansible_module, "vault_public_key")
public_key_file = module_params_get(ansible_module,
"vault_public_key_file")
@@ -614,7 +701,8 @@ def main():
service, shared, users, groups, services, owners,
ownergroups, ownerservices, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
- private_key_file, vault_data, datafile_in, datafile_out)
+ private_key_file, vault_data, datafile_in, datafile_out,
+ new_password, new_password_file)
# Init
changed = False
@@ -660,7 +748,7 @@ def main():
ansible_module, state, action, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
- res_find)
+ new_password, new_password_file, res_find)
# Found the vault
if action == "vault":
@@ -721,7 +809,6 @@ def main():
owner_add_args = gen_member_args(
args, owner_add, ownergroups_add, ownerservice_add)
if owner_add_args is not None:
- # ansible_module.warn("OWNER ADD: %s" % owner_add_args)
commands.append(
[name, 'vault_add_owner', owner_add_args])
@@ -729,7 +816,6 @@ def main():
owner_del_args = gen_member_args(
args, owner_del, ownergroups_del, ownerservice_del)
if owner_del_args is not None:
- # ansible_module.warn("OWNER DEL: %s" % owner_del_args)
commands.append(
[name, 'vault_remove_owner', owner_del_args])
@@ -758,19 +844,22 @@ def main():
if any([vault_data, datafile_in]):
commands.append([name, "vault_archive", pwdargs])
+ cmds = change_password(
+ ansible_module, res_find, password, password_file,
+ new_password, new_password_file)
+ commands.extend(cmds)
+
elif state == "retrieved":
if res_find is None:
ansible_module.fail_json(
msg="Vault `%s` not found to retrieve data." % name)
- vault_type = res_find['cn']
-
# verify data encription args
check_encryption_params(
ansible_module, state, action, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
- res_find)
+ new_password, new_password_file, res_find)
pwdargs = data_storage_args(
args, vault_data, password, password_file, private_key,
@@ -813,7 +902,6 @@ def main():
errors = []
for name, command, args in commands:
try:
- # ansible_module.warn("RUN: %s %s %s" % (command, name, args))
result = api_command(ansible_module, command, name, args)
if command == 'vault_archive':
@@ -829,7 +917,6 @@ def main():
raise Exception("No data retrieved.")
changed = False
else:
- # ansible_module.warn("RESULT: %s" % (result))
if "completed" in result:
if result["completed"] > 0:
changed = True
diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml
index c9429f4f..a6072d88 100644
--- a/tests/vault/test_vault_symmetric.yml
+++ b/tests/vault/test_vault_symmetric.yml
@@ -178,6 +178,61 @@
register: result
failed_when: result.data != 'Hello World.' or result.changed
+ - name: Change vault password.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ password: SomeVAULTpassword
+ new_password: SomeNEWpassword
+ register: result
+ failed_when: not result.changed
+
+ - name: Retrieve data from symmetric vault, with wrong password.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ password: SomeVAULTpassword
+ state: retrieved
+ register: result
+ failed_when: not result.failed or "Invalid credentials" not in result.msg
+
+ - name: Change vault password, with wrong `old_password`.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ password: SomeVAULTpassword
+ new_password: SomeNEWpassword
+ register: result
+ failed_when: not result.failed or "Invalid credentials" not in result.msg
+
+ - name: Retrieve data from symmetric vault, with new password.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ password: SomeNEWpassword
+ state: retrieved
+ register: result
+ failed_when: result.data != 'Hello World.' or result.changed
+
+ - name: Try to add vault with multiple passwords.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: inexistentvault
+ password: SomeVAULTpassword
+ password_file: "{{ ansible_env.HOME }}/password.txt"
+ register: result
+ failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg
+
+ - name: Try to add vault with multiple new passwords.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: inexistentvault
+ password: SomeVAULTpassword
+ new_password: SomeVAULTpassword
+ new_password_file: "{{ ansible_env.HOME }}/password.txt"
+ register: result
+ failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg
+
- name: Ensure symmetric vault is absent
ipavault:
ipaadmin_password: SomeADMINpassword
@@ -194,5 +249,14 @@
register: result
failed_when: result.changed
+ - name: Try to change password of inexistent vault.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: inexistentvault
+ password: SomeVAULTpassword
+ new_password: SomeNEWpassword
+ register: result
+ failed_when: not result.failed or "Cannot modify password of inexistent vault" not in result.msg
+
- name: Cleanup testing environment.
import_tasks: env_cleanup.yml

View File

@ -0,0 +1,302 @@
From 75d16c2da4a5621943873a26343eb0f2acc2a925 Mon Sep 17 00:00:00 2001
From: Sergio Oliveira Campos <seocam@seocam.com>
Date: Mon, 3 Aug 2020 11:54:44 -0300
Subject: [PATCH] Allow multiple dns zones to be absent.
This PR allow ipadnszone module to ensure that multiple dns zones
are absent at once, to be consistent with other ansible-freeipa
modules.
To fix this issue, it was required that custom arguents must be
passed using keyword arguments so that `get_ipa_command_args()`
is kept generic.
---
README-dnszone.md | 2 +-
.../module_utils/ansible_freeipa_module.py | 4 +-
plugins/modules/ipadnszone.py | 126 ++++++++++--------
tests/dnszone/test_dnszone.yml | 37 +++++
4 files changed, 107 insertions(+), 62 deletions(-)
diff --git a/README-dnszone.md b/README-dnszone.md
index 766efe5..9c9b12c 100644
--- a/README-dnszone.md
+++ b/README-dnszone.md
@@ -163,7 +163,7 @@ Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
-`name` \| `zone_name` | The zone name string. | yes
+`name` \| `zone_name` | The zone name string or list of strings. | yes
`forwarders` | The list of forwarders dicts. Each `forwarders` dict entry has:| no
&nbsp; | `ip_address` - The IPv4 or IPv6 address of the DNS server. | yes
&nbsp; | `port` - The custom port that should be used on this server. | no
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 122ea2e..1e55693 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -506,7 +506,7 @@ class FreeIPABaseModule(AnsibleModule):
# when needed.
self.ipa_params = AnsibleFreeIPAParams(self)
- def get_ipa_command_args(self):
+ def get_ipa_command_args(self, **kwargs):
"""
Return a dict to be passed to an IPA command.
@@ -538,7 +538,7 @@ class FreeIPABaseModule(AnsibleModule):
elif hasattr(self, param_name):
method = getattr(self, param_name)
if callable(method):
- value = method()
+ value = method(**kwargs)
# We don't have a way to guess the value so fail.
else:
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index 717978e..c5e812a 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -41,7 +41,7 @@ options:
name:
description: The zone name string.
required: true
- type: str
+ type: list
alises: ["zone_name"]
forwarders:
description: The list of global DNS forwarders.
@@ -268,7 +268,7 @@ class DNSZoneModule(FreeIPABaseModule):
return True
- def get_ipa_nsec3paramrecord(self):
+ def get_ipa_nsec3paramrecord(self, **kwargs):
nsec3param_rec = self.ipa_params.nsec3param_rec
if nsec3param_rec is not None:
error_msg = (
@@ -280,7 +280,7 @@ class DNSZoneModule(FreeIPABaseModule):
self.fail_json(msg=error_msg)
return nsec3param_rec
- def get_ipa_idnsforwarders(self):
+ def get_ipa_idnsforwarders(self, **kwargs):
if self.ipa_params.forwarders is not None:
forwarders = []
for forwarder in self.ipa_params.forwarders:
@@ -304,14 +304,14 @@ class DNSZoneModule(FreeIPABaseModule):
return forwarders
- def get_ipa_idnsallowtransfer(self):
+ def get_ipa_idnsallowtransfer(self, **kwargs):
if self.ipa_params.allow_transfer is not None:
error_msg = "Invalid ip_address for DNS allow_transfer: %s"
self.validate_ips(self.ipa_params.allow_transfer, error_msg)
return (";".join(self.ipa_params.allow_transfer) or "none") + ";"
- def get_ipa_idnsallowquery(self):
+ def get_ipa_idnsallowquery(self, **kwargs):
if self.ipa_params.allow_query is not None:
error_msg = "Invalid ip_address for DNS allow_query: %s"
self.validate_ips(self.ipa_params.allow_query, error_msg)
@@ -334,81 +334,89 @@ class DNSZoneModule(FreeIPABaseModule):
return ".".join((name, domain))
- def get_ipa_idnssoarname(self):
+ def get_ipa_idnssoarname(self, **kwargs):
if self.ipa_params.admin_email is not None:
return DNSName(
self._replace_at_symbol_in_rname(self.ipa_params.admin_email)
)
- def get_ipa_idnssoamname(self):
+ def get_ipa_idnssoamname(self, **kwargs):
if self.ipa_params.name_server is not None:
return DNSName(self.ipa_params.name_server)
- def get_ipa_skip_overlap_check(self):
- if not self.zone and self.ipa_params.skip_overlap_check is not None:
+ def get_ipa_skip_overlap_check(self, **kwargs):
+ zone = kwargs.get('zone')
+ if not zone and self.ipa_params.skip_overlap_check is not None:
return self.ipa_params.skip_overlap_check
- def get_ipa_skip_nameserver_check(self):
- if not self.zone and self.ipa_params.skip_nameserver_check is not None:
+ def get_ipa_skip_nameserver_check(self, **kwargs):
+ zone = kwargs.get('zone')
+ if not zone and self.ipa_params.skip_nameserver_check is not None:
return self.ipa_params.skip_nameserver_check
def get_zone(self, zone_name):
get_zone_args = {"idnsname": zone_name, "all": True}
response = self.api_command("dnszone_find", args=get_zone_args)
+ zone = None
+ is_zone_active = False
+
if response["count"] == 1:
- self.zone = response["result"][0]
- self.is_zone_active = self.zone.get("idnszoneactive") == ["TRUE"]
- return self.zone
+ zone = response["result"][0]
+ is_zone_active = zone.get("idnszoneactive") == ["TRUE"]
- # Zone doesn't exist yet
- self.zone = None
- self.is_zone_active = False
+ return zone, is_zone_active
+
+ def get_zone_names(self):
+ if len(self.ipa_params.name) > 1 and self.ipa_params.state != "absent":
+ self.fail_json(
+ msg=("Please provide a single name. Multiple values for 'name'"
+ "can only be supplied for state 'absent'.")
+ )
- @property
- def zone_name(self):
return self.ipa_params.name
def define_ipa_commands(self):
- # Look for existing zone in IPA
- self.get_zone(self.zone_name)
- args = self.get_ipa_command_args()
- just_added = False
-
- if self.ipa_params.state in ["present", "enabled", "disabled"]:
- if not self.zone:
- # Since the zone doesn't exist we just create it
- # with given args
- self.add_ipa_command("dnszone_add", self.zone_name, args)
- self.is_zone_active = True
- just_added = True
-
- else:
- # Zone already exist so we need to verify if given args
- # matches the current config. If not we updated it.
- if self.require_ipa_attrs_change(args, self.zone):
- self.add_ipa_command("dnszone_mod", self.zone_name, args)
-
- if self.ipa_params.state == "enabled" and not self.is_zone_active:
- self.add_ipa_command("dnszone_enable", self.zone_name)
-
- if self.ipa_params.state == "disabled" and self.is_zone_active:
- self.add_ipa_command("dnszone_disable", self.zone_name)
-
- if self.ipa_params.state == "absent":
- if self.zone:
- self.add_ipa_command("dnszone_del", self.zone_name)
-
- # Due to a bug in FreeIPA dnszone-add won't set
- # SOA Serial. The good news is that dnszone-mod does the job.
- # See: https://pagure.io/freeipa/issue/8227
- # Because of that, if the zone was just added with a given serial
- # we run mod just after to workaround the bug
- if just_added and self.ipa_params.serial is not None:
- args = {
- "idnssoaserial": self.ipa_params.serial,
- }
- self.add_ipa_command("dnszone_mod", self.zone_name, args)
+ for zone_name in self.get_zone_names():
+ # Look for existing zone in IPA
+ zone, is_zone_active = self.get_zone(zone_name)
+ args = self.get_ipa_command_args(zone=zone)
+ just_added = False
+
+ if self.ipa_params.state in ["present", "enabled", "disabled"]:
+ if not zone:
+ # Since the zone doesn't exist we just create it
+ # with given args
+ self.add_ipa_command("dnszone_add", zone_name, args)
+ is_zone_active = True
+ just_added = True
+
+ else:
+ # Zone already exist so we need to verify if given args
+ # matches the current config. If not we updated it.
+ if self.require_ipa_attrs_change(args, zone):
+ self.add_ipa_command("dnszone_mod", zone_name, args)
+
+ if self.ipa_params.state == "enabled" and not is_zone_active:
+ self.add_ipa_command("dnszone_enable", zone_name)
+
+ if self.ipa_params.state == "disabled" and is_zone_active:
+ self.add_ipa_command("dnszone_disable", zone_name)
+
+ if self.ipa_params.state == "absent":
+ if zone:
+ self.add_ipa_command("dnszone_del", zone_name)
+
+ # Due to a bug in FreeIPA dnszone-add won't set
+ # SOA Serial. The good news is that dnszone-mod does the job.
+ # See: https://pagure.io/freeipa/issue/8227
+ # Because of that, if the zone was just added with a given serial
+ # we run mod just after to workaround the bug
+ if just_added and self.ipa_params.serial is not None:
+ args = {
+ "idnssoaserial": self.ipa_params.serial,
+ }
+ self.add_ipa_command("dnszone_mod", zone_name, args)
def get_argument_spec():
@@ -426,7 +434,7 @@ def get_argument_spec():
ipaadmin_principal=dict(type="str", default="admin"),
ipaadmin_password=dict(type="str", required=False, no_log=True),
name=dict(
- type="str", default=None, required=True, aliases=["zone_name"]
+ type="list", default=None, required=True, aliases=["zone_name"]
),
forwarders=dict(
type="list",
diff --git a/tests/dnszone/test_dnszone.yml b/tests/dnszone/test_dnszone.yml
index f7bd1f0..bd820df 100644
--- a/tests/dnszone/test_dnszone.yml
+++ b/tests/dnszone/test_dnszone.yml
@@ -149,3 +149,40 @@
forwarders: []
register: result
failed_when: not result.changed
+
+ - name: Create zones test1
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: test1.testzone.local
+
+ - name: Create zones test2
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: test2.testzone.local
+
+ - name: Create zones test3
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name: test3.testzone.local
+
+ - name: Ensure multiple zones are absent
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - test1.testzone.local
+ - test2.testzone.local
+ - test3.testzone.local
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure multiple zones are absent, again
+ ipadnszone:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - test1.testzone.local
+ - test2.testzone.local
+ - test3.testzone.local
+ state: absent
+ register: result
+ failed_when: result.changed
--
2.26.2

View File

@ -0,0 +1,628 @@
# Skipping 3ab575bcac310166e7d29c5a5349d90482f4e629 as it is reorganizing
# service module test test_service.yml and
# test_service_without_skip_host_check.yml
From b5e93c705fc56f6592121aa09bfb9f6dce5cee35 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Tue, 11 Aug 2020 16:23:15 -0300
Subject: [PATCH] Fix `allow_retrieve_keytab_host` in service module.
The attribute `allow_retrieve_keytab_host` was not working due to
wrong processing of the input and verification if the values should
be updated. Both the issues are fixed by this change.
Tests were added to better verify service keytab members.
---
plugins/modules/ipaservice.py | 4 +-
tests/service/env_cleanup.yml | 68 +++++
tests/service/env_setup.yml | 73 +++++
tests/service/env_vars.yml | 15 +
tests/service/test_service_keytab.yml | 397 ++++++++++++++++++++++++++
5 files changed, 555 insertions(+), 2 deletions(-)
create mode 100644 tests/service/env_cleanup.yml
create mode 100644 tests/service/env_setup.yml
create mode 100644 tests/service/env_vars.yml
create mode 100644 tests/service/test_service_keytab.yml
diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py
index b0d2535..8bc390d 100644
--- a/plugins/modules/ipaservice.py
+++ b/plugins/modules/ipaservice.py
@@ -460,7 +460,7 @@ def main():
allow_retrieve_keytab_group = module_params_get(
ansible_module, "allow_retrieve_keytab_group")
allow_retrieve_keytab_host = module_params_get(
- ansible_module, "allow_create_keytab_host")
+ ansible_module, "allow_retrieve_keytab_host")
allow_retrieve_keytab_hostgroup = module_params_get(
ansible_module, "allow_retrieve_keytab_hostgroup")
delete_continue = module_params_get(ansible_module, "delete_continue")
@@ -727,7 +727,7 @@ def main():
# Allow retrieve keytab
if len(allow_retrieve_keytab_user_add) > 0 or \
len(allow_retrieve_keytab_group_add) > 0 or \
- len(allow_retrieve_keytab_hostgroup_add) > 0 or \
+ len(allow_retrieve_keytab_host_add) > 0 or \
len(allow_retrieve_keytab_hostgroup_add) > 0:
commands.append(
[name, "service_allow_retrieve_keytab",
diff --git a/tests/service/env_cleanup.yml b/tests/service/env_cleanup.yml
new file mode 100644
index 0000000..f96a75b
--- /dev/null
+++ b/tests/service/env_cleanup.yml
@@ -0,0 +1,68 @@
+---
+# Cleanup tasks for the service module tests.
+- name: Ensure services are absent.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - "HTTP/{{ svc_fqdn }}"
+ - "HTTP/{{ nohost_fqdn }}"
+ - HTTP/svc.ihavenodns.info
+ - HTTP/no.idontexist.local
+ - "cifs/{{ host1_fqdn }}"
+ state: absent
+
+- name: Ensure host "{{ svc_fqdn }}" is absent
+ ipahost:
+ ipaadmin_password: SomeADMINpassword
+ name: "{{ svc_fqdn }}"
+ update_dns: yes
+ state: absent
+
+- name: Ensure host is absent
+ ipahost:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ - "{{ nohost_fqdn }}"
+ - svc.ihavenodns.info
+ update_dns: no
+ state: absent
+
+- name: Ensure testing users are absent.
+ ipauser:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - user01
+ - user02
+ state: absent
+
+- name: Ensure testing groups are absent.
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - group01
+ - group02
+ state: absent
+
+- name: Ensure testing hostgroup hostgroup01 is absent.
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - hostgroup01
+ state: absent
+
+- name: Ensure testing hostgroup hostgroup02 is absent.
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - hostgroup02
+ state: absent
+
+- name: Remove IP address for "nohost" host.
+ ipadnsrecord:
+ ipaadmin_password: SomeADMINpassword
+ zone_name: "{{ test_domain }}."
+ name: nohost
+ del_all: yes
+ state: absent
diff --git a/tests/service/env_setup.yml b/tests/service/env_setup.yml
new file mode 100644
index 0000000..309cfc0
--- /dev/null
+++ b/tests/service/env_setup.yml
@@ -0,0 +1,73 @@
+# Setup environment for service module tests.
+---
+- name: Setup variables and facts.
+ include_tasks: env_vars.yml
+
+# Cleanup before setup.
+- name: Cleanup test environment.
+ include_tasks: env_cleanup.yml
+
+- name: Add IP address for "nohost" host.
+ ipadnsrecord:
+ ipaadmin_password: SomeADMINpassword
+ zone_name: "{{ test_domain }}."
+ name: nohost
+ a_ip_address: "{{ ipv4_prefix + '.100' }}"
+
+- name: Add hosts for tests.
+ ipahost:
+ ipaadmin_password: SomeADMINpassword
+ hosts:
+ - name: "{{ host1_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.101' }}"
+ - name: "{{ host2_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.102' }}"
+ - name: "{{ svc_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.201' }}"
+ - name: svc.ihavenodns.info
+ force: yes
+ update_dns: yes
+
+- name: Ensure testing user user01 is present.
+ ipauser:
+ ipaadmin_password: SomeADMINpassword
+ name: user01
+ first: user01
+ last: last
+
+- name: Ensure testing user user02 is present.
+ ipauser:
+ ipaadmin_password: SomeADMINpassword
+ name: user02
+ first: user02
+ last: last
+
+- name: Ensure testing group group01 is present.
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name: group01
+
+- name: Ensure testing group group02 is present.
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name: group02
+
+- name: Ensure testing hostgroup hostgroup01 is present.
+ ipahostgroup:
+ ipaadmin_password: SomeADMINpassword
+ name: hostgroup01
+
+- name: Ensure testing hostgroup hostgroup02 is present.
+ ipahostgroup:
+ ipaadmin_password: SomeADMINpassword
+ name: hostgroup02
+
+- name: Ensure services are absent.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name:
+ - "HTTP/{{ svc_fqdn }}"
+ - "HTTP/{{ nohost_fqdn }}"
+ - HTTP/svc.ihavenodns.info
+ - HTTP/no.idontexist.info
+ state: absent
diff --git a/tests/service/env_vars.yml b/tests/service/env_vars.yml
new file mode 100644
index 0000000..eb53c7a
--- /dev/null
+++ b/tests/service/env_vars.yml
@@ -0,0 +1,15 @@
+---
+ - name: Get Domain from server name
+ set_fact:
+ test_domain: "{{ ansible_fqdn.split('.')[1:] | join('.') }}"
+
+ - name: Set host1, host2 and svc hosts fqdn
+ set_fact:
+ host1_fqdn: "{{ 'host1.' + test_domain }}"
+ host2_fqdn: "{{ 'host2.' + test_domain }}"
+ svc_fqdn: "{{ 'svc.' + test_domain }}"
+ nohost_fqdn: "{{ 'nohost.' + test_domain }}"
+
+ - name: Get IPv4 address prefix from server node
+ set_fact:
+ ipv4_prefix: "{{ ansible_default_ipv4.address.split('.')[:-1] | join('.') }}"
diff --git a/tests/service/test_service_keytab.yml b/tests/service/test_service_keytab.yml
new file mode 100644
index 0000000..0918802
--- /dev/null
+++ b/tests/service/test_service_keytab.yml
@@ -0,0 +1,397 @@
+---
+- name: Test service
+ hosts: ipaserver
+ become: yes
+
+ tasks:
+ # setup
+ - name: Setup test envirnoment.
+ include_tasks: env_setup.yml
+
+ # Add service to test keytab create/retrieve attributes.
+ - name: Ensure test service is present
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ pac_type:
+ - MS-PAC
+ - PAD
+ auth_ind: otp
+ force: yes
+ requires_pre_auth: yes
+ ok_as_delegate: no
+ ok_to_auth_as_delegate: no
+
+ # tests
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for users.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_user:
+ - user01
+ - user02
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for users, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_user:
+ - user01
+ - user02
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for users.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_user:
+ - user01
+ - user02
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for users, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_user:
+ - user01
+ - user02
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for group.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_group:
+ - group01
+ - group02
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for group, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_group:
+ - group01
+ - group02
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for group.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_group:
+ - group01
+ - group02
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for group, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_group:
+ - group01
+ - group02
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for host.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for host, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for host.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for host, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for hostgroup.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab present for hostgroup, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for hostgroup.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ state: absent
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_create_keytab absent for hostgroup, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_create_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for users.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_user:
+ - user01
+ - user02
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for users, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_user:
+ - user01
+ - user02
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for users.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_user:
+ - user01
+ - user02
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for users, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_user:
+ - user01
+ - user02
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for group.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_group:
+ - group01
+ - group02
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for group, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_group:
+ - group01
+ - group02
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for group.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_group:
+ - group01
+ - group02
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for group, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_group:
+ - group01
+ - group02
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for host.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for host, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for host.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for host, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_host:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for hostgroup.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab present for hostgroup, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for hostgroup.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Service "HTTP/{{ svc_fqdn }}" members allow_retrieve_keytab absent for hostgroup, again.
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "HTTP/{{ svc_fqdn }}"
+ allow_retrieve_keytab_hostgroup:
+ - hostgroup01
+ - hostgroup02
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ # cleanup
+ - name: Clean-up envirnoment.
+ include_tasks: env_cleanup.yml
--
2.26.2

View File

@ -0,0 +1,40 @@
From 563a03d94bfc29799ea964dab61a1ba35818b9bb Mon Sep 17 00:00:00 2001
From: Sergio Oliveira Campos <seocam@seocam.com>
Date: Thu, 30 Jul 2020 09:50:24 -0300
Subject: [PATCH] Fixed error msgs on FreeIPABaseModule subclasses
When a fail_json is called a SystemExit exeception is raised.
Since the FreeIPABaseModule has an internal context manager to deal
with exceptions this ContextManager captures the SystemExit. After
dealing destroying the kinit session the SystemExit must be raised again
to allow the fail_json to work properly.
---
plugins/module_utils/ansible_freeipa_module.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 122ea2e..a59e6e2 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -610,12 +610,15 @@ class FreeIPABaseModule(AnsibleModule):
exit the module with proper arguments.
"""
- if exc_val:
- self.fail_json(msg=str(exc_val))
-
# TODO: shouldn't we also disconnect from api backend?
temp_kdestroy(self.ccache_dir, self.ccache_name)
+ if exc_type == SystemExit:
+ raise
+
+ if exc_val:
+ self.fail_json(msg=str(exc_val))
+
self.exit_json(changed=self.changed, user=self.exit_args)
def get_command_errors(self, command, result):
--
2.26.2

View File

@ -0,0 +1,327 @@
From 3e5c54d4fdb10deda9b7e4deaf2c537b132711c9 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 31 Jul 2020 11:30:51 -0300
Subject: [PATCH] Fix identification of existing vault type.
In some scenarios, the value of the vault type is returned as a tuple,
rather than a string, this made some changes to existing vault to fail.
With this change, the vault type is correctly retrieved, if it was not
provided by the user.
---
plugins/modules/ipavault.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index 6a3c73e..8562ff7 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -494,8 +494,10 @@ def check_encryption_params(module, state, action, vault_type, salt,
new_password, new_password_file, res_find):
vault_type_invalid = []
- if res_find is not None:
+ if vault_type is None and res_find is not None:
vault_type = res_find['ipavaulttype']
+ if isinstance(vault_type, (tuple, list)):
+ vault_type = vault_type[0]
if vault_type == "standard":
vault_type_invalid = ['public_key', 'public_key_file', 'password',
--
2.26.2
From d52364bac923f2935b948882d5825e7488b0e9cf Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 31 Jul 2020 11:32:36 -0300
Subject: [PATCH] Fix random salt generation.
The generation of a random salt, when one was not provided, was in the
wrong place and being generated too late to be used properly. Also, the
generation of the value was duplicated.
---
plugins/modules/ipavault.py | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index 8562ff7..dffd972 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -768,7 +768,12 @@ def main():
commands.append([name, "vault_mod_internal", args])
else:
+ if vault_type == 'symmetric' \
+ and 'ipavaultsalt' not in args:
+ args['ipavaultsalt'] = os.urandom(32)
+
commands.append([name, "vault_add_internal", args])
+
if vault_type != 'standard' and vault_data is None:
vault_data = ''
@@ -826,14 +831,6 @@ def main():
commands.append(
[name, 'vault_remove_owner', owner_del_args])
- if vault_type == 'symmetric' \
- and 'ipavaultsalt' not in args:
- args['ipavaultsalt'] = os.urandom(32)
-
- if vault_type == 'symmetric' \
- and 'ipavaultsalt' not in args:
- args['ipavaultsalt'] = os.urandom(32)
-
elif action in "member":
# Add users and groups
if any([users, groups, services]):
--
2.26.2
From daee6a6c744a740329ca231a277229567619e10c Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 31 Jul 2020 11:33:47 -0300
Subject: [PATCH] Fix verification of parameters for modifying `salt`
attribute.
When modifying an existing vault to change the value of `salt`, the
password must also change. It is fine to "change" the password to the
same value, thus only changing the salt value.
---
plugins/modules/ipavault.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index dffd972..a608e64 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -517,6 +517,16 @@ def check_encryption_params(module, state, action, vault_type, salt,
module.fail_json(
msg="Cannot modify password of inexistent vault.")
+ if (
+ salt is not None
+ and not(
+ any([password, password_file])
+ and any([new_password, new_password_file])
+ )
+ ):
+ module.fail_json(
+ msg="Vault `salt` can only change when changing the password.")
+
if vault_type == "asymmetric":
vault_type_invalid = [
'password', 'password_file', 'new_password', 'new_password_file'
--
2.26.2
From 4ef4e706b79fdbb43e462b1a7130fc2cad5894b2 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 31 Jul 2020 11:42:13 -0300
Subject: [PATCH] Modify tests to verify password was changed correctly.
Modify and add tests to verify that a password change has the correct
effect on ipavault.
---
tests/vault/test_vault_symmetric.yml | 36 ++++++++++++++++++----------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml
index bedc221..9294331 100644
--- a/tests/vault/test_vault_symmetric.yml
+++ b/tests/vault/test_vault_symmetric.yml
@@ -178,6 +178,15 @@
register: result
failed_when: result.vault.data != 'Hello World.' or result.changed
+ - name: Retrieve data from symmetric vault, with wrong password.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ password: SomeWRONGpassword
+ state: retrieved
+ register: result
+ failed_when: not result.failed or "Invalid credentials" not in result.msg
+
- name: Change vault password.
ipavault:
ipaadmin_password: SomeADMINpassword
@@ -187,43 +196,44 @@
register: result
failed_when: not result.changed
- - name: Retrieve data from symmetric vault, with wrong password.
+ - name: Retrieve data from symmetric vault, with new password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
- password: SomeVAULTpassword
+ password: SomeNEWpassword
state: retrieved
register: result
- failed_when: not result.failed or "Invalid credentials" not in result.msg
+ failed_when: result.data != 'Hello World.' or result.changed
- - name: Change vault password, with wrong `old_password`.
+ - name: Retrieve data from symmetric vault, with old password.
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeVAULTpassword
- new_password: SomeNEWpassword
+ state: retrieved
register: result
failed_when: not result.failed or "Invalid credentials" not in result.msg
- - name: Retrieve data from symmetric vault, with new password.
+ - name: Change symmetric vault salt, changing password
ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
password: SomeNEWpassword
- state: retrieved
+ new_password: SomeVAULTpassword
+ salt: AAAAAAAAAAAAAAAAAAAAAAA=
register: result
- failed_when: result.vault.data != 'Hello World.' or result.changed
+ failed_when: not result.changed
- - name: Try to add vault with multiple passwords.
+ - name: Change symmetric vault salt, without changing password
ipavault:
ipaadmin_password: SomeADMINpassword
- name: inexistentvault
+ name: symvault
password: SomeVAULTpassword
- password_file: "{{ ansible_env.HOME }}/password.txt"
+ new_password: SomeVAULTpassword
+ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo=
register: result
- failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg
+ failed_when: not result.changed
- - name: Try to add vault with multiple new passwords.
ipavault:
ipaadmin_password: SomeADMINpassword
name: inexistentvault
--
2.26.2
From 8ca282e276477b52d0850d4c01feb3d8e7a5be6d Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 31 Jul 2020 11:44:33 -0300
Subject: [PATCH] Modified and added tests to verify correct `salt` update
behavior.
---
tests/vault/test_vault_symmetric.yml | 35 ++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)
diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml
index 9294331..1604a01 100644
--- a/tests/vault/test_vault_symmetric.yml
+++ b/tests/vault/test_vault_symmetric.yml
@@ -234,14 +234,41 @@
register: result
failed_when: not result.changed
+ - name: Try to change symmetric vault salt, without providing any password
ipavault:
ipaadmin_password: SomeADMINpassword
- name: inexistentvault
- password: SomeVAULTpassword
+ name: symvault
+ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo=
+ register: result
+ failed_when: not result.failed and "Vault `salt` can only change when changing the password." not in result.msg
+
+ - name: Try to change symmetric vault salt, without providing `password`
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo=
new_password: SomeVAULTpassword
- new_password_file: "{{ ansible_env.HOME }}/password.txt"
register: result
- failed_when: not result.failed or "parameters are mutually exclusive" not in result.msg
+ failed_when: not result.failed and "Vault `salt` can only change when changing the password." not in result.msg
+
+ - name: Try to change symmetric vault salt, without providing `new_password`
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ salt: MTIzNDU2Nzg5MDEyMzQ1Ngo=
+ password: SomeVAULTpassword
+ register: result
+ failed_when: not result.failed and "Vault `salt` can only change when changing the password." not in result.msg
+
+ - name: Try to change symmetric vault salt, using wrong password.
+ ipavault:
+ ipaadmin_password: SomeADMINpassword
+ name: symvault
+ password: SomeWRONGpassword
+ new_password: SomeWRONGpassword
+ salt: MDEyMzQ1Njc4OTAxMjM0NQo=
+ register: result
+ failed_when: not result.failed
- name: Ensure symmetric vault is absent
ipavault:
--
2.26.2
From 3c2700f68beade3513e0e44415d8eb4fb23026e8 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 14 Aug 2020 10:43:30 -0300
Subject: [PATCH] Fixed Vault return value usage from `data` to `vault.data`.
A test was failing due to use of old ipavault module return structure
and some places on the documentation were alse referring to it. All
ocurrences were fixed.
---
README-vault.md | 2 +-
plugins/modules/ipavault.py | 2 +-
tests/vault/test_vault_symmetric.yml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/README-vault.md b/README-vault.md
index 91d311d..e7a31a2 100644
--- a/README-vault.md
+++ b/README-vault.md
@@ -197,7 +197,7 @@ Example playbook to make sure vault is absent:
state: absent
register: result
- debug:
- msg: "{{ result.data }}"
+ msg: "{{ result.vault.data }}"
```
Variables
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index a608e64..8060976 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -243,7 +243,7 @@ EXAMPLES = """
state: retrieved
register: result
- debug:
- msg: "{{ result.data }}"
+ msg: "{{ result.vault.data }}"
# Change password of a symmetric vault
- ipavault:
diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml
index 1604a01..5394c71 100644
--- a/tests/vault/test_vault_symmetric.yml
+++ b/tests/vault/test_vault_symmetric.yml
@@ -203,7 +203,7 @@
password: SomeNEWpassword
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Retrieve data from symmetric vault, with old password.
ipavault:
--
2.26.2

View File

@ -0,0 +1,112 @@
From e57e4908f936c524085fb5853fe4493c7711ab3f Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Thu, 25 Jun 2020 16:26:30 -0300
Subject: [PATCH] Fixes service disable when service has no certificates
attached.
Services without certificates, but with keytabs were not being
disabled. This change allows execution of service_disable if
there is a certificate or if has_keytab is true.
A new test was added to verify the issue:
tests/service/test_service_disable.yml
---
plugins/modules/ipaservice.py | 8 +--
tests/service/test_service_disable.yml | 68 ++++++++++++++++++++++++++
2 files changed, 73 insertions(+), 3 deletions(-)
create mode 100644 tests/service/test_service_disable.yml
diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py
index 23a0d6b3..b0d25355 100644
--- a/plugins/modules/ipaservice.py
+++ b/plugins/modules/ipaservice.py
@@ -812,9 +812,11 @@ def main():
elif state == "disabled":
if action == "service":
- if res_find is not None and \
- len(res_find.get('usercertificate', [])) > 0:
- commands.append([name, 'service_disable', {}])
+ if res_find is not None:
+ has_cert = bool(res_find.get('usercertificate'))
+ has_keytab = res_find.get('has_keytab', False)
+ if has_cert or has_keytab:
+ commands.append([name, 'service_disable', {}])
else:
ansible_module.fail_json(
msg="Invalid action '%s' for state '%s'" %
diff --git a/tests/service/test_service_disable.yml b/tests/service/test_service_disable.yml
new file mode 100644
index 00000000..3b4a88fb
--- /dev/null
+++ b/tests/service/test_service_disable.yml
@@ -0,0 +1,68 @@
+---
+- name: Playbook to manage IPA service.
+ hosts: ipaserver
+ become: yes
+ gather_facts: yes
+
+ tasks:
+ - name: Ensure service is absent
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "mysvc1/{{ ansible_fqdn }}"
+
+ - name: Ensure service is present
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "mysvc1/{{ ansible_fqdn }}"
+ certificate:
+ - MIIC/zCCAeegAwIBAgIUMNHIbn+hhrOVew/2WbkteisV29QwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAyMDQxNDQxMDhaFw0zMDAyMDExNDQxMDhaMA8xDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+XVVGFYpHVkcDfVnNInE1Y/pFciegdzqTjMwUWlRL4Zt3u96GhaMLRbtk+OfEkzLUAhWBOwEraELJzMLJOMvjYF3C+TiGO7dStFLikZmccuSsSIXjnzIPwBXa8KvgRVRyGLoVvGbLJvmjfMXp0nIToTx/i74KF9S++WEes9H5ErJ99CDhLKFgq0amnvsgparYXhypHaRLnikn0vQINt55YoEd1s4KrvEcD2VdZkIMPbLRu2zFvMprF3cjQQG4LT9ggfEXNIPZ1nQWAnAsu7OJEkNF+E4Mkmpcxj9aGUVt5bsq1D+Tzj3GsidSX0nSNcZ2JltXRnL/5v63g5cZyE+nAgMBAAGjUzBRMB0GA1UdDgQWBBRV0j7JYukuH/r/t9+QeNlRLXDlEDAfBgNVHSMEGDAWgBRV0j7JYukuH/r/t9+QeNlRLXDlEDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCgVy1+1kNwHs5y1Zp0WjMWGCJC6/zw7FDG4OW5r2GJiCXZYdJ0UonY9ZtoVLJPrp2/DAv1m5DtnDhBYqicuPgLzEkOS1KdTi20Otm/J4yxLLrZC5W4x0XOeSVPXOJuQWfwQ5pPvKkn6WxYUYkGwIt1OH2nSMngkbami3CbSmKZOCpgQIiSlQeDJ8oGjWFMLDymYSHoVOIXHwNoooyEiaio3693l6noobyGv49zyCVLVR1DC7i6RJ186ql0av+D4vPoiF5mX7+sKC2E8xEj9uKQ5GTWRh59VnRBVC/SiMJ/H78tJnBAvoBwXxSEvj8Z3Kjm/BQqZfv4IBsA5yqV7MVq
+ force: no
+ register: result
+ failed_when: not result.changed
+
+ - name: Obtain keytab
+ shell: ipa-getkeytab -s "{{ ansible_fqdn }}" -p "mysvc1/{{ ansible_fqdn }}" -k mysvc1.keytab
+
+ - name: Verify keytab
+ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
+ register: result
+ failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
+
+ - name: Ensure service is disabled
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "mysvc1/{{ ansible_fqdn }}"
+ state: disabled
+ register: result
+ failed_when: not result.changed
+
+ - name: Verify keytab
+ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
+ register: result
+ failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
+
+ - name: Obtain keytab
+ shell: ipa-getkeytab -s "{{ ansible_fqdn }}" -p "mysvc1/{{ ansible_fqdn }}" -k mysvc1.keytab
+
+ - name: Verify keytab
+ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
+ register: result
+ failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
+
+ - name: Ensure service is disabled
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "mysvc1/{{ ansible_fqdn }}"
+ state: disabled
+ register: result
+ failed_when: not result.changed
+
+ - name: Verify keytab
+ shell: ipa service-find "mysvc1/{{ ansible_fqdn }}"
+ register: result
+ failed_when: result.failed or result.stdout | regex_search(" Keytab. true")
+
+ - name: Ensure service is absent
+ ipaservice:
+ ipaadmin_password: SomeADMINpassword
+ name: "mysvc1/{{ ansible_fqdn }}"

View File

@ -0,0 +1,300 @@
From e96ef4e98e523f20c25777308c093ebbff272b2d Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Wed, 5 Aug 2020 15:24:15 -0300
Subject: [PATCH] Updated documentation for ipavault module in the source code.
This change fixes a wrong parameter name in the documentation of
RESULT_VALUES, and also provide a correct YAML snippet to ensure
presence of an asymmetric vault with a formatted private key.
---
plugins/modules/ipavault.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index 46c6fcd..84645c7 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -267,7 +267,7 @@ EXAMPLES = """
username: user01
description: An asymmetric vault
vault_type: asymmetric
- public_key:
+ public_key: |
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTR
HTkFEQ0JpUUtCZ1FDdGFudjRkK3ptSTZ0T3ova1RXdGowY3AxRAowUENoYy8vR0pJMTUzTi
9CN3UrN0h3SXlRVlZoNUlXZG1UcCtkWXYzd09yeVpPbzYvbHN5eFJaZ2pZRDRwQ3VGCjlxM
@@ -303,7 +303,7 @@ EXAMPLES = """
"""
RETURN = """
-user:
+data:
description: The vault data.
returned: If state is retrieved.
type: string
--
2.26.2
From 7dd0b547c47b4fd617960490b8553a5036e3b30c Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Mon, 10 Aug 2020 16:02:09 -0300
Subject: [PATCH] Modified return value for ipavault module.
The ipavault module was returning a single string value when retrieving
data. To keep consistency with other modules, it should return a dict
with the `data` variable in it.
This change modifies the result of ipavault to be a dict and also fixes
relevant tests, examples and documentation.
---
README-vault.md | 5 +++++
.../vault/retrive-data-asymmetric-vault.yml | 2 +-
.../vault/retrive-data-symmetric-vault.yml | 2 +-
plugins/modules/ipavault.py | 19 +++++++++++++------
tests/vault/test_vault_asymmetric.yml | 12 ++++++------
tests/vault/test_vault_standard.yml | 8 ++++----
tests/vault/test_vault_symmetric.yml | 14 +++++++-------
7 files changed, 37 insertions(+), 25 deletions(-)
diff --git a/README-vault.md b/README-vault.md
index fa1d3e1..91d311d 100644
--- a/README-vault.md
+++ b/README-vault.md
@@ -248,6 +248,11 @@ Variable | Description | Returned When
-------- | ----------- | -------------
`data` | The data stored in the vault. | If `state` is `retrieved`.
+Variable | Description | Returned When
+-------- | ----------- | -------------
+`vault` | Vault dict with archived data. (dict) <br>Options: | If `state` is `retrieved`.
+&nbsp; | `data` - The vault data. | Always
+
Notes
=====
diff --git a/playbooks/vault/retrive-data-asymmetric-vault.yml b/playbooks/vault/retrive-data-asymmetric-vault.yml
index 5f67c59..f71f826 100644
--- a/playbooks/vault/retrive-data-asymmetric-vault.yml
+++ b/playbooks/vault/retrive-data-asymmetric-vault.yml
@@ -14,4 +14,4 @@
state: retrieved
register: result
- debug:
- msg: "Data: {{ result.data }}"
+ msg: "Data: {{ result.vault.data }}"
diff --git a/playbooks/vault/retrive-data-symmetric-vault.yml b/playbooks/vault/retrive-data-symmetric-vault.yml
index 163f8b9..24692a8 100644
--- a/playbooks/vault/retrive-data-symmetric-vault.yml
+++ b/playbooks/vault/retrive-data-symmetric-vault.yml
@@ -14,4 +14,4 @@
state: retrieved
register: result
- debug:
- msg: "{{ result.data | b64decode }}"
+ msg: "{{ result.vault.data }}"
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index 84645c7..6a3c73e 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -303,10 +303,15 @@ EXAMPLES = """
"""
RETURN = """
-data:
- description: The vault data.
- returned: If state is retrieved.
- type: string
+vault:
+ description: Vault dict with archived data.
+ returned: If state is `retrieved`.
+ type: dict
+ options:
+ data:
+ description: The vault data.
+ returned: always
+ type: string
"""
import os
@@ -910,9 +915,11 @@ def main():
if 'result' not in result:
raise Exception("No result obtained.")
if 'data' in result['result']:
- exit_args['data'] = result['result']['data']
+ data_return = exit_args.setdefault('vault', {})
+ data_return['data'] = result['result']['data']
elif 'vault_data' in result['result']:
- exit_args['data'] = result['result']['vault_data']
+ data_return = exit_args.setdefault('vault', {})
+ data_return['data'] = result['result']['vault_data']
else:
raise Exception("No data retrieved.")
changed = False
diff --git a/tests/vault/test_vault_asymmetric.yml b/tests/vault/test_vault_asymmetric.yml
index 1a1d3dc..268922c 100644
--- a/tests/vault/test_vault_asymmetric.yml
+++ b/tests/vault/test_vault_asymmetric.yml
@@ -42,7 +42,7 @@
private_key: "{{ lookup('file', 'private.pem') | b64encode }}"
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Retrieve data from asymmetric vault into file {{ ansible_env.HOME }}/data.txt.
ipavault:
@@ -75,7 +75,7 @@
private_key: "{{ lookup('file', 'private.pem') | b64encode }}"
state: retrieved
register: result
- failed_when: result.data != 'The world of π is half rounded.' or result.changed
+ failed_when: result.vault.data != 'The world of π is half rounded.' or result.changed
- name: Archive data in asymmetric vault, from file.
ipavault:
@@ -93,7 +93,7 @@
private_key: "{{ lookup('file', 'private.pem') | b64encode }}"
state: retrieved
register: result
- failed_when: result.data != 'Another World.' or result.changed
+ failed_when: result.vault.data != 'Another World.' or result.changed
- name: Archive data with single character to asymmetric vault
ipavault:
@@ -110,7 +110,7 @@
private_key: "{{ lookup('file', 'private.pem') | b64encode }}"
state: retrieved
register: result
- failed_when: result.data != 'c' or result.changed
+ failed_when: result.vault.data != 'c' or result.changed
- name: Ensure asymmetric vault is absent
ipavault:
@@ -161,7 +161,7 @@
private_key: "{{ lookup('file', 'private.pem') | b64encode }}"
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Retrieve data from asymmetric vault, with password file.
ipavault:
@@ -170,7 +170,7 @@
private_key_file: "{{ ansible_env.HOME }}/private.pem"
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Ensure asymmetric vault is absent
ipavault:
diff --git a/tests/vault/test_vault_standard.yml b/tests/vault/test_vault_standard.yml
index 5e0da98..6ccb0d5 100644
--- a/tests/vault/test_vault_standard.yml
+++ b/tests/vault/test_vault_standard.yml
@@ -39,7 +39,7 @@
name: stdvault
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Retrieve data from standard vault into file {{ ansible_env.HOME }}/data.txt.
ipavault:
@@ -70,7 +70,7 @@
name: stdvault
state: retrieved
register: result
- failed_when: result.data != 'The world of π is half rounded.' or result.changed
+ failed_when: result.vault.data != 'The world of π is half rounded.' or result.changed
- name: Archive data in standard vault, from file.
ipavault:
@@ -87,7 +87,7 @@
name: stdvault
state: retrieved
register: result
- failed_when: result.data != 'Another World.' or result.changed
+ failed_when: result.vault.data != 'Another World.' or result.changed
- name: Archive data with single character to standard vault
ipavault:
@@ -103,7 +103,7 @@
name: stdvault
state: retrieved
register: result
- failed_when: result.data != 'c' or result.changed
+ failed_when: result.vault.data != 'c' or result.changed
- name: Ensure standard vault is absent
ipavault:
diff --git a/tests/vault/test_vault_symmetric.yml b/tests/vault/test_vault_symmetric.yml
index a6072d8..bedc221 100644
--- a/tests/vault/test_vault_symmetric.yml
+++ b/tests/vault/test_vault_symmetric.yml
@@ -43,7 +43,7 @@
password: SomeVAULTpassword
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Retrieve data from symmetric vault into file {{ ansible_env.HOME }}/data.txt.
ipavault:
@@ -77,7 +77,7 @@
password: SomeVAULTpassword
state: retrieved
register: result
- failed_when: result.data != 'The world of π is half rounded.' or result.changed
+ failed_when: result.vault.data != 'The world of π is half rounded.' or result.changed
- name: Archive data in symmetric vault, from file.
ipavault:
@@ -95,7 +95,7 @@
password: SomeVAULTpassword
state: retrieved
register: result
- failed_when: result.data != 'Another World.' or result.changed
+ failed_when: result.vault.data != 'Another World.' or result.changed
- name: Archive data with single character to symmetric vault
ipavault:
@@ -113,7 +113,7 @@
password: SomeVAULTpassword
state: retrieved
register: result
- failed_when: result.data != 'c' or result.changed
+ failed_when: result.vault.data != 'c' or result.changed
- name: Ensure symmetric vault is absent
ipavault:
@@ -167,7 +167,7 @@
password: SomeVAULTpassword
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Retrieve data from symmetric vault, with password file.
ipavault:
@@ -176,7 +176,7 @@
password_file: "{{ ansible_env.HOME }}/password.txt"
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Change vault password.
ipavault:
@@ -212,7 +212,7 @@
password: SomeNEWpassword
state: retrieved
register: result
- failed_when: result.data != 'Hello World.' or result.changed
+ failed_when: result.vault.data != 'Hello World.' or result.changed
- name: Try to add vault with multiple passwords.
ipavault:
--
2.26.2

View File

@ -0,0 +1,49 @@
From 80aac15de9026055ae2b9972859939cf7925b813 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Tue, 30 Jun 2020 17:32:19 +0200
Subject: [PATCH] action_plugins/ipaclient_get_otp: Discovered python needed in
task_vars
Ansible is now also supporting discovered_python_interpreter for
action_plugins. task_vars needs to be non Null and contain a setting for
discovered_python_interpreter. The ipaclient_get_otp action_plugin
therefore needed to be adapted.
---
roles/ipaclient/action_plugins/ipaclient_get_otp.py | 4 ++--
roles/ipaclient/tasks/install.yml | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/roles/ipaclient/action_plugins/ipaclient_get_otp.py b/roles/ipaclient/action_plugins/ipaclient_get_otp.py
index dcddc0a..8e04ad9 100644
--- a/roles/ipaclient/action_plugins/ipaclient_get_otp.py
+++ b/roles/ipaclient/action_plugins/ipaclient_get_otp.py
@@ -164,7 +164,8 @@ class ActionModule(ActionBase):
return result
data = self._execute_module(module_name='ipaclient_get_facts',
- module_args=dict(), task_vars=None)
+ module_args=dict(), task_vars=task_vars)
+
try:
domain = data['ansible_facts']['ipa']['domain']
realm = data['ansible_facts']['ipa']['realm']
@@ -245,4 +246,3 @@ class ActionModule(ActionBase):
finally:
# delete the local temp directory
shutil.rmtree(local_temp_dir, ignore_errors=True)
- run_cmd(['/usr/bin/kdestroy', '-c', tmp_ccache])
diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml
index 0de3dea..4421f0c 100644
--- a/roles/ipaclient/tasks/install.yml
+++ b/roles/ipaclient/tasks/install.yml
@@ -134,7 +134,6 @@
"Password cannot be set on enrolled host" not
in result_ipaclient_get_otp.msg
delegate_to: "{{ result_ipaclient_test.servers[0] }}"
- delegate_facts: yes
ignore_errors: yes
- name: Install - Report error for OTP generation
--
2.26.2

View File

@ -0,0 +1,132 @@
From 6132a947e65fb9c3a1ec5c059aed34afb06a67df Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Mon, 29 Jun 2020 13:12:12 +0200
Subject: [PATCH] ipa[host]group: Fix membermanager unknow user issue
If a unknown membermanager user presence will be ensured, the unknown user
error was ignored. This has been fixed in ipagroup. The code for the error
handling in ipagroup and ipahostgroup has been adapted because of this.
New tests for tests/[host]group/test_[host]group_membermnager.yml have been
added.
---
plugins/modules/ipagroup.py | 19 +++++++++----------
plugins/modules/ipahostgroup.py | 13 +++++++------
tests/group/test_group_membermanager.yml | 11 ++++++++++-
.../test_hostgroup_membermanager.yml | 11 ++++++++++-
4 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/plugins/modules/ipagroup.py b/plugins/modules/ipagroup.py
index 915bc499..903c256d 100644
--- a/plugins/modules/ipagroup.py
+++ b/plugins/modules/ipagroup.py
@@ -507,16 +507,15 @@ def main():
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
errors = []
- if "failed" in result and len(result["failed"]) > 0:
- for item in result["failed"]:
- failed_item = result["failed"][item]
- for member_type in failed_item:
- for member, failure in failed_item[member_type]:
- if "already a member" in failure \
- or "not a member" in failure:
- continue
- errors.append("%s: %s %s: %s" % (
- command, member_type, member, failure))
+ for failed_item in result.get("failed", []):
+ failed = result["failed"][failed_item]
+ for member_type in failed:
+ for member, failure in failed[member_type]:
+ if "already a member" in failure \
+ or "not a member" in failure:
+ continue
+ errors.append("%s: %s %s: %s" % (
+ command, member_type, member, failure))
if len(errors) > 0:
ansible_module.fail_json(msg=", ".join(errors))
diff --git a/plugins/modules/ipahostgroup.py b/plugins/modules/ipahostgroup.py
index 4c18e940..5f615160 100644
--- a/plugins/modules/ipahostgroup.py
+++ b/plugins/modules/ipahostgroup.py
@@ -423,14 +423,15 @@ def main():
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
errors = []
- if "failed" in result and "member" in result["failed"]:
- failed = result["failed"]["member"]
+ for failed_item in result.get("failed", []):
+ failed = result["failed"][failed_item]
for member_type in failed:
for member, failure in failed[member_type]:
- if "already a member" not in failure \
- and "not a member" not in failure:
- errors.append("%s: %s %s: %s" % (
- command, member_type, member, failure))
+ if "already a member" in failure \
+ or "not a member" in failure:
+ continue
+ errors.append("%s: %s %s: %s" % (
+ command, member_type, member, failure))
if len(errors) > 0:
ansible_module.fail_json(msg=", ".join(errors))
diff --git a/tests/group/test_group_membermanager.yml b/tests/group/test_group_membermanager.yml
index 1d38654f..661f26d6 100644
--- a/tests/group/test_group_membermanager.yml
+++ b/tests/group/test_group_membermanager.yml
@@ -8,7 +8,7 @@
- name: Ensure user manangeruser1 and manageruser2 is absent
ipauser:
ipaadmin_password: SomeADMINpassword
- name: manageruser1,manageruser2
+ name: manageruser1,manageruser2,unknown_user
state: absent
- name: Ensure group testgroup, managergroup1 and managergroup2 are absent
@@ -185,6 +185,15 @@
register: result
failed_when: not result.changed
+ - name: Ensure unknown membermanager_user member failure
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name: testgroup
+ membermanager_user: unknown_user
+ action: member
+ register: result
+ failed_when: result.changed or "no such entry" not in result.msg
+
- name: Ensure group testgroup, managergroup1 and managergroup2 are absent
ipagroup:
ipaadmin_password: SomeADMINpassword
diff --git a/tests/hostgroup/test_hostgroup_membermanager.yml b/tests/hostgroup/test_hostgroup_membermanager.yml
index c32d1088..c0f65460 100644
--- a/tests/hostgroup/test_hostgroup_membermanager.yml
+++ b/tests/hostgroup/test_hostgroup_membermanager.yml
@@ -15,7 +15,7 @@
- name: Ensure user manangeruser1 and manageruser2 is absent
ipauser:
ipaadmin_password: SomeADMINpassword
- name: manageruser1,manageruser2
+ name: manageruser1,manageruser2,unknown_user
state: absent
- name: Ensure group managergroup1 and managergroup2 are absent
@@ -200,6 +200,15 @@
register: result
failed_when: not result.changed
+ - name: Ensure unknown membermanager_user member failure
+ ipahostgroup:
+ ipaadmin_password: SomeADMINpassword
+ name: testhostgroup
+ membermanager_user: unknown_user
+ action: member
+ register: result
+ failed_when: result.changed or "no such entry" not in result.msg
+
- name: Ensure host-group testhostgroup is absent
ipahostgroup:
ipaadmin_password: SomeADMINpassword

View File

@ -0,0 +1,150 @@
From 8ce5fd147aafc34e43dbe4246565c48eace2e115 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 2 Jul 2020 12:02:33 +0200
Subject: [PATCH] ipa[server,replica]: Fix pkcs12 info regressions introduced
with CA-less
With the CA-less patches the types for the pkcs12 infos have been changed
to lists in the modules. This is resulting in a bad conversion from None
to [''] for the parameters. Because of this a normal replica deployment is
failing as [''] is not a valid value.
The install.yml files for ipareplica and also ipaserver have been changed
in the way that the pkcs12 values are checked if they are None. The
parameter will simply be omitted in this case and the parameter in the
module will become None by default.
---
roles/ipareplica/tasks/install.yml | 18 +++++++++---------
roles/ipaserver/tasks/install.yml | 10 +++++-----
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml
index fc7f83e..c2a6222 100644
--- a/roles/ipareplica/tasks/install.yml
+++ b/roles/ipareplica/tasks/install.yml
@@ -281,7 +281,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
- _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
+ _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
subject_base: "{{ result_ipareplica_prepare.subject_base }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
@@ -345,7 +345,7 @@
config_master_host_name:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
ccache: "{{ result_ipareplica_prepare.ccache }}"
- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
+ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
# We need to point to the master in ipa default conf when certmonger
@@ -407,8 +407,8 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
- _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info }}"
- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
+ _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
+ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
@@ -429,7 +429,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
- _http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info }}"
+ _http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info if result_ipareplica_prepare._http_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
@@ -507,7 +507,7 @@
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
+ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
@@ -529,7 +529,7 @@
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
+ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
@@ -554,7 +554,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
+ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
@@ -574,7 +574,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
- _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info }}"
+ _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
diff --git a/roles/ipaserver/tasks/install.yml b/roles/ipaserver/tasks/install.yml
index 30f9da2..687f72d 100644
--- a/roles/ipaserver/tasks/install.yml
+++ b/roles/ipaserver/tasks/install.yml
@@ -203,7 +203,7 @@
# no_host_dns: "{{ result_ipaserver_test.no_host_dns }}"
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default(omit) }}"
- _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
+ _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
external_cert_files:
"{{ ipaserver_external_cert_files | default(omit) }}"
subject_base: "{{ result_ipaserver_prepare.subject_base }}"
@@ -240,7 +240,7 @@
no_hbac_allow: "{{ ipaserver_no_hbac_allow }}"
idstart: "{{ result_ipaserver_test.idstart }}"
idmax: "{{ result_ipaserver_test.idmax }}"
- _pkinit_pkcs12_info: "{{ result_ipaserver_test._pkinit_pkcs12_info }}"
+ _pkinit_pkcs12_info: "{{ result_ipaserver_test._pkinit_pkcs12_info if result_ipaserver_test._pkinit_pkcs12_info != None else omit }}"
- name: Install - Setup custodia
ipaserver_setup_custodia:
@@ -270,7 +270,7 @@
no_pkinit: "{{ result_ipaserver_test.no_pkinit }}"
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
dirsrv_cert_files: "{{ ipaserver_dirsrv_cert_files | default([]) }}"
- _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
+ _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
external_ca: "{{ ipaserver_external_ca }}"
external_ca_type: "{{ ipaserver_external_ca_type | default(omit) }}"
external_ca_profile:
@@ -334,7 +334,7 @@
idmax: "{{ result_ipaserver_test.idmax }}"
http_cert_files: "{{ ipaserver_http_cert_files | default([]) }}"
no_ui_redirect: "{{ ipaserver_no_ui_redirect }}"
- _http_pkcs12_info: "{{ result_ipaserver_test._http_pkcs12_info }}"
+ _http_pkcs12_info: "{{ result_ipaserver_test._http_pkcs12_info if result_ipaserver_test._http_pkcs12_info != None else omit }}"
- name: Install - Setup KRA
ipaserver_setup_kra:
@@ -394,7 +394,7 @@
idstart: "{{ result_ipaserver_test.idstart }}"
idmax: "{{ result_ipaserver_test.idmax }}"
dirsrv_config_file: "{{ ipaserver_dirsrv_config_file | default(omit) }}"
- _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info }}"
+ _dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
- name: Install - Setup client
include_role:
--
2.26.2

View File

@ -0,0 +1,132 @@
From 1d7fb31b8bfa00babd7c753b354d7344b531cd77 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Mon, 29 Jun 2020 14:50:56 +0200
Subject: [PATCH] ipa[user,host]: Fail on duplucate names in the users and
hosts lists
It was possible to have several entries for names with the hosts and users
lists. This resulted sometimes in errors but also unexpected changes. A new
check has been added to make sure that the names in the users and hosts
lists are unique.
New tests have been added to verify this in the existing files:
- tests/host/test_hosts.yml
- tests/user/test_users.yml
---
plugins/modules/ipahost.py | 7 +++++++
plugins/modules/ipauser.py | 7 +++++++
tests/host/test_hosts.yml | 15 +++++++++++++++
tests/user/test_users.yml | 19 +++++++++++++++++++
4 files changed, 48 insertions(+)
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index 7a981f16..1fe11dc5 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -799,10 +799,15 @@ def main():
server_realm = api_get_realm()
commands = []
+ host_set = set()
for host in names:
if isinstance(host, dict):
name = host.get("name")
+ if name in host_set:
+ ansible_module.fail_json(
+ msg="host '%s' is used more than once" % name)
+ host_set.add(name)
description = host.get("description")
locality = host.get("locality")
location = host.get("location")
@@ -1337,6 +1342,8 @@ def main():
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
+ del host_set
+
# Execute commands
errors = []
diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py
index b8152ee4..03713a41 100644
--- a/plugins/modules/ipauser.py
+++ b/plugins/modules/ipauser.py
@@ -958,10 +958,15 @@ def main():
# commands
commands = []
+ user_set = set()
for user in names:
if isinstance(user, dict):
name = user.get("name")
+ if name in user_set:
+ ansible_module.fail_json(
+ msg="user '%s' is used more than once" % name)
+ user_set.add(name)
# present
first = user.get("first")
last = user.get("last")
@@ -1370,6 +1375,8 @@ def main():
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
+ del user_set
+
# Execute commands
errors = []
diff --git a/tests/host/test_hosts.yml b/tests/host/test_hosts.yml
index 30fd6538..f82cc612 100644
--- a/tests/host/test_hosts.yml
+++ b/tests/host/test_hosts.yml
@@ -96,3 +96,18 @@
state: absent
register: result
failed_when: result.changed
+
+ - name: Duplicate names in hosts failure test
+ ipahost:
+ ipaadmin_password: SomeADMINpassword
+ hosts:
+ - name: "{{ host1_fqdn }}"
+ force: yes
+ - name: "{{ host2_fqdn }}"
+ force: yes
+ - name: "{{ host3_fqdn }}"
+ force: yes
+ - name: "{{ host3_fqdn }}"
+ force: yes
+ register: result
+ failed_when: result.changed or "is used more than once" not in result.msg
diff --git a/tests/user/test_users.yml b/tests/user/test_users.yml
index 5b5d4538..81c7b608 100644
--- a/tests/user/test_users.yml
+++ b/tests/user/test_users.yml
@@ -85,6 +85,25 @@
register: result
failed_when: result.changed
+ - name: Duplicate names in users failure test
+ ipauser:
+ ipaadmin_password: SomeADMINpassword
+ users:
+ - name: user1
+ givenname: user1
+ last: Last
+ - name: user2
+ first: user2
+ last: Last
+ - name: user3
+ first: user3
+ last: Last
+ - name: user3
+ first: user3
+ last: Last
+ register: result
+ failed_when: result.changed or "is used more than once" not in result.msg
+
- name: Remove test users
ipauser:
ipaadmin_password: SomeADMINpassword

View File

@ -0,0 +1,271 @@
From 7a2eaa6f535b1353d46bcfa8b0b2484b15ff3863 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Tue, 7 Jul 2020 17:13:09 +0200
Subject: [PATCH] ipareplica: Fix missing parameters for several modules
The parameters master_host_name, config_setup_ca, dirman_password have not
been set for some modules. Also there was no ldap2 connection within
ipareplica_setup_kra. All this resulted in improper configuration where
for example KRA deployment failed in the end.
A conversion warning in ipareplica_setup_adtrust has also been fixed for
the setup_ca parameter.
Fixes #314 (IPA replica installation failure - DS enabled SSL - second part)
---
.../library/ipareplica_create_ipa_conf.py | 1 +
.../library/ipareplica_ds_apply_updates.py | 1 +
.../library/ipareplica_ds_enable_ssl.py | 1 +
.../library/ipareplica_setup_adtrust.py | 2 +-
.../library/ipareplica_setup_custodia.py | 1 +
.../library/ipareplica_setup_http.py | 2 +-
.../ipareplica/library/ipareplica_setup_kra.py | 18 ++++++++++++++++++
.../ipareplica/library/ipareplica_setup_krb.py | 7 +++++++
roles/ipareplica/tasks/install.yml | 8 ++++++++
9 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/roles/ipareplica/library/ipareplica_create_ipa_conf.py b/roles/ipareplica/library/ipareplica_create_ipa_conf.py
index 3a85a6f..c475469 100644
--- a/roles/ipareplica/library/ipareplica_create_ipa_conf.py
+++ b/roles/ipareplica/library/ipareplica_create_ipa_conf.py
@@ -262,6 +262,7 @@ def main():
config.subject_base = options.subject_base
config.dirman_password = dirman_password
config.ca_host_name = ca_host_name
+ config.setup_ca = options.setup_ca
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
installer._remote_api = remote_api
diff --git a/roles/ipareplica/library/ipareplica_ds_apply_updates.py b/roles/ipareplica/library/ipareplica_ds_apply_updates.py
index 3796874..71008b3 100644
--- a/roles/ipareplica/library/ipareplica_ds_apply_updates.py
+++ b/roles/ipareplica/library/ipareplica_ds_apply_updates.py
@@ -177,6 +177,7 @@ def main():
config = gen_ReplicaConfig()
config.dirman_password = dirman_password
config.subject_base = options.subject_base
+ config.master_host_name = master_host_name
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
diff --git a/roles/ipareplica/library/ipareplica_ds_enable_ssl.py b/roles/ipareplica/library/ipareplica_ds_enable_ssl.py
index a1b638e..3e4090d 100644
--- a/roles/ipareplica/library/ipareplica_ds_enable_ssl.py
+++ b/roles/ipareplica/library/ipareplica_ds_enable_ssl.py
@@ -173,6 +173,7 @@ def main():
config = gen_ReplicaConfig()
config.dirman_password = dirman_password
config.subject_base = options.subject_base
+ config.master_host_name = master_host_name
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
# installer._remote_api = remote_api
diff --git a/roles/ipareplica/library/ipareplica_setup_adtrust.py b/roles/ipareplica/library/ipareplica_setup_adtrust.py
index c830ebf..734e56d 100644
--- a/roles/ipareplica/library/ipareplica_setup_adtrust.py
+++ b/roles/ipareplica/library/ipareplica_setup_adtrust.py
@@ -110,7 +110,7 @@ def main():
# additional
ccache=dict(required=True),
_top_dir=dict(required=True),
- setup_ca=dict(required=True),
+ setup_ca=dict(required=True, type='bool'),
config_master_host_name=dict(required=True),
),
supports_check_mode=True,
diff --git a/roles/ipareplica/library/ipareplica_setup_custodia.py b/roles/ipareplica/library/ipareplica_setup_custodia.py
index 5a74e87..2e95c26 100644
--- a/roles/ipareplica/library/ipareplica_setup_custodia.py
+++ b/roles/ipareplica/library/ipareplica_setup_custodia.py
@@ -169,6 +169,7 @@ def main():
config.promote = installer.promote
config.kra_enabled = kra_enabled
config.kra_host_name = kra_host_name
+ config.setup_ca = options.setup_ca
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
diff --git a/roles/ipareplica/library/ipareplica_setup_http.py b/roles/ipareplica/library/ipareplica_setup_http.py
index 987ea95..3fa4807 100644
--- a/roles/ipareplica/library/ipareplica_setup_http.py
+++ b/roles/ipareplica/library/ipareplica_setup_http.py
@@ -164,7 +164,7 @@ def main():
config.subject_base = options.subject_base
config.dirman_password = dirman_password
config.setup_ca = options.setup_ca
- # config.master_host_name = master_host_name
+ config.master_host_name = master_host_name
config.ca_host_name = ca_host_name
config.promote = installer.promote
diff --git a/roles/ipareplica/library/ipareplica_setup_kra.py b/roles/ipareplica/library/ipareplica_setup_kra.py
index 3149c10..0b2f681 100644
--- a/roles/ipareplica/library/ipareplica_setup_kra.py
+++ b/roles/ipareplica/library/ipareplica_setup_kra.py
@@ -120,6 +120,9 @@ options:
_subject_base:
description: The installer _subject_base setting
required: no
+ dirman_password:
+ description: Directory Manager (master) password
+ required: no
author:
- Thomas Woerner
'''
@@ -173,10 +176,12 @@ def main():
_ca_enabled=dict(required=False, type='bool'),
_kra_enabled=dict(required=False, type='bool'),
_kra_host_name=dict(required=False),
+ _ca_host_name=dict(required=False),
_top_dir=dict(required=True),
_add_to_ipaservers=dict(required=True, type='bool'),
_ca_subject=dict(required=True),
_subject_base=dict(required=True),
+ dirman_password=dict(required=True, no_log=True),
),
supports_check_mode=True,
)
@@ -233,6 +238,7 @@ def main():
ca_enabled = ansible_module.params.get('_ca_enabled')
kra_enabled = ansible_module.params.get('_kra_enabled')
kra_host_name = ansible_module.params.get('_kra_host_name')
+ ca_host_name = ansible_module.params.get('_ca_host_name')
options.subject_base = ansible_module.params.get('subject_base')
if options.subject_base is not None:
@@ -243,6 +249,7 @@ def main():
options._ca_subject = ansible_module.params.get('_ca_subject')
options._subject_base = ansible_module.params.get('_subject_base')
+ dirman_password = ansible_module.params.get('dirman_password')
# init #
@@ -254,14 +261,25 @@ def main():
constants.DEFAULT_CONFIG)
api_bootstrap_finalize(env)
config = gen_ReplicaConfig()
+ config.dirman_password = dirman_password
config.subject_base = options.subject_base
config.promote = installer.promote
config.kra_enabled = kra_enabled
config.kra_host_name = kra_host_name
+ config.ca_host_name = ca_host_name
+ config.master_host_name = master_host_name
remote_api = gen_remote_api(master_host_name, paths.ETC_IPA)
installer._remote_api = remote_api
+ conn = remote_api.Backend.ldap2
+ ccache = os.environ['KRB5CCNAME']
+
+ # There is a api.Backend.ldap2.connect call somewhere in ca, ds, dns or
+ # ntpinstance
+ api.Backend.ldap2.connect()
+ conn.connect(ccache=ccache)
+
with redirect_stdout(ansible_log):
ansible_log.debug("-- INSTALL KRA --")
diff --git a/roles/ipareplica/library/ipareplica_setup_krb.py b/roles/ipareplica/library/ipareplica_setup_krb.py
index c8d09f7..4500a6f 100644
--- a/roles/ipareplica/library/ipareplica_setup_krb.py
+++ b/roles/ipareplica/library/ipareplica_setup_krb.py
@@ -63,6 +63,9 @@ options:
_top_dir:
description: The installer _top_dir setting
required: no
+ dirman_password:
+ description: Directory Manager (master) password
+ required: no
author:
- Thomas Woerner
'''
@@ -98,6 +101,7 @@ def main():
ccache=dict(required=True),
_pkinit_pkcs12_info=dict(required=False, type='list'),
_top_dir=dict(required=True),
+ dirman_password=dict(required=True, no_log=True),
),
supports_check_mode=True,
)
@@ -126,6 +130,7 @@ def main():
'_pkinit_pkcs12_info')
options._top_dir = ansible_module.params.get('_top_dir')
+ dirman_password = ansible_module.params.get('dirman_password')
# init #
@@ -141,8 +146,10 @@ def main():
constants.DEFAULT_CONFIG)
api_bootstrap_finalize(env)
config = gen_ReplicaConfig()
+ config.dirman_password = dirman_password
config.master_host_name = config_master_host_name
config.subject_base = options.subject_base
+ config.setup_ca = options.setup_ca
ccache = os.environ['KRB5CCNAME']
diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml
index c2a6222..ddb3f85 100644
--- a/roles/ipareplica/tasks/install.yml
+++ b/roles/ipareplica/tasks/install.yml
@@ -226,6 +226,8 @@
setup_adtrust: "{{ result_ipareplica_test.setup_adtrust }}"
setup_kra: "{{ result_ipareplica_test.setup_kra }}"
setup_dns: "{{ ipareplica_setup_dns }}"
+ ### server ###
+ setup_ca: "{{ ipareplica_setup_ca }}"
### ssl certificate ###
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
@@ -332,6 +334,7 @@
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
+ setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
- name: Install - Setup KRB
ipareplica_setup_krb:
@@ -347,6 +350,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
+ dirman_password: "{{ ipareplica_dirman_password }}"
# We need to point to the master in ipa default conf when certmonger
# asks for HTTP certificate in newer ipa versions. In these versions
@@ -388,6 +392,7 @@
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
+ setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
master:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
when: result_ipareplica_test.change_master_for_certmonger
@@ -471,6 +476,7 @@
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
+ setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
when: result_ipareplica_test.change_master_for_certmonger
- name: Install - Setup otpd
@@ -611,10 +617,12 @@
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
+ _ca_host_name: "{{ result_ipareplica_prepare.config_ca_host_name }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
+ dirman_password: "{{ ipareplica_dirman_password }}"
when: result_ipareplica_test.setup_kra
- name: Install - Restart KDC
--
2.26.2

View File

@ -1,177 +0,0 @@
From 3780a9a00e77ae0fd2944b36adad446d094fc90f Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Tue, 11 Feb 2020 10:34:39 +0100
Subject: [PATCH] ansible_freeipa_module: Fix comparison of bool parameters in
compare_args_ipa
Bool types are not iterable. Therefore the comparison using sets was failing
with a TypeError. This prevented to change the bool parameters for hosts.
A test for the host module has been added to verify that the bool parameters
can be modified.
New test:
tests/host/test_host_bool_params.yml
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1784514
---
.../module_utils/ansible_freeipa_module.py | 18 ++-
tests/host/test_host_bool_params.yml | 119 ++++++++++++++++++
2 files changed, 133 insertions(+), 4 deletions(-)
create mode 100644 tests/host/test_host_bool_params.yml
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 8154a12..9e97b88 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -222,10 +222,20 @@ def compare_args_ipa(module, args, ipa):
arg = [to_text(_arg) for _arg in arg]
if isinstance(ipa_arg[0], unicode) and isinstance(arg[0], int):
arg = [to_text(_arg) for _arg in arg]
- # module.warn("%s <=> %s" % (arg, ipa_arg))
- if set(arg) != set(ipa_arg):
- # module.warn("DIFFERENT")
- return False
+ # module.warn("%s <=> %s" % (repr(arg), repr(ipa_arg)))
+ try:
+ arg_set = set(arg)
+ ipa_arg_set = set(ipa_arg)
+ except TypeError:
+ if arg != ipa_arg:
+ # module.warn("%s != %s" % (repr(arg), repr(ipa_arg)))
+ return False
+ else:
+ if arg_set != ipa_arg_set:
+ # module.warn("%s != %s" % (repr(arg), repr(ipa_arg)))
+ return False
+
+ # module.warn("%s == %s" % (repr(arg), repr(ipa_arg)))
return True
diff --git a/tests/host/test_host_bool_params.yml b/tests/host/test_host_bool_params.yml
new file mode 100644
index 0000000..824ea99
--- /dev/null
+++ b/tests/host/test_host_bool_params.yml
@@ -0,0 +1,119 @@
+---
+- name: Test host bool parameters
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Get Domain from server name
+ set_fact:
+ ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}"
+ when: ipaserver_domain is not defined
+
+ - name: Set host1_fqdn .. host6_fqdn
+ set_fact:
+ host1_fqdn: "{{ 'host1.' + ipaserver_domain }}"
+
+ - name: Host absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name:
+ - "{{ host1_fqdn }}"
+ update_dns: yes
+ state: absent
+
+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ force: yes
+ requires_pre_auth: yes
+ ok_as_delegate: yes
+ ok_to_auth_as_delegate: yes
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ requires_pre_auth: yes
+ ok_as_delegate: yes
+ ok_to_auth_as_delegate: yes
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate set to no
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ requires_pre_auth: no
+ ok_as_delegate: no
+ ok_to_auth_as_delegate: no
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth, ok_as_delegate and ok_to_auth_as_delegate set to no again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ requires_pre_auth: no
+ ok_as_delegate: no
+ ok_to_auth_as_delegate: no
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ requires_pre_auth: yes
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with requires_pre_auth again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ requires_pre_auth: yes
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with ok_as_delegate
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ok_as_delegate: yes
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with ok_as_delegate again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ok_as_delegate: yes
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with ok_to_auth_as_delegate
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ok_to_auth_as_delegate: yes
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present with ok_to_auth_as_delegate again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ok_to_auth_as_delegate: yes
+ register: result
+ failed_when: result.changed
+
+ - name: Host absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name:
+ - "{{ host1_fqdn }}"
+ update_dns: yes
+ state: absent

View File

@ -1,838 +0,0 @@
From 3865ce657e3ea1b621aa054c792201aedfde2d11 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Fri, 7 Feb 2020 10:11:38 +0100
Subject: [PATCH] ipahbacrule: Fix handing of members with action hbacrule
Changing members (host, hostgroup, hbacsvc, hbacsvcgroup, user, group) with
action hbacrule was not working due to the use of the wrong parameter
prefix. This has been fixed and the old members are removed correctly now.
The test script has been reworked completely to verify the fix.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1787996
---
plugins/modules/ipahbacrule.py | 24 +-
tests/hbacrule/test_hbacrule.yml | 549 +++++++++++++++++++++++--------
2 files changed, 432 insertions(+), 141 deletions(-)
diff --git a/plugins/modules/ipahbacrule.py b/plugins/modules/ipahbacrule.py
index 385876b..82340c2 100644
--- a/plugins/modules/ipahbacrule.py
+++ b/plugins/modules/ipahbacrule.py
@@ -344,41 +344,41 @@ def main():
# Generate addition and removal lists
host_add = list(
set(host or []) -
- set(res_find.get("member_host", [])))
+ set(res_find.get("memberhost_host", [])))
host_del = list(
- set(res_find.get("member_host", [])) -
+ set(res_find.get("memberhost_host", [])) -
set(host or []))
hostgroup_add = list(
set(hostgroup or []) -
- set(res_find.get("member_hostgroup", [])))
+ set(res_find.get("memberhost_hostgroup", [])))
hostgroup_del = list(
- set(res_find.get("member_hostgroup", [])) -
+ set(res_find.get("memberhost_hostgroup", [])) -
set(hostgroup or []))
hbacsvc_add = list(
set(hbacsvc or []) -
- set(res_find.get("member_hbacsvc", [])))
+ set(res_find.get("memberservice_hbacsvc", [])))
hbacsvc_del = list(
- set(res_find.get("member_hbacsvc", [])) -
+ set(res_find.get("memberservice_hbacsvc", [])) -
set(hbacsvc or []))
hbacsvcgroup_add = list(
set(hbacsvcgroup or []) -
- set(res_find.get("member_hbacsvcgroup", [])))
+ set(res_find.get("memberservice_hbacsvcgroup", [])))
hbacsvcgroup_del = list(
- set(res_find.get("member_hbacsvcgroup", [])) -
+ set(res_find.get("memberservice_hbacsvcgroup", [])) -
set(hbacsvcgroup or []))
user_add = list(
set(user or []) -
- set(res_find.get("member_user", [])))
+ set(res_find.get("memberuser_user", [])))
user_del = list(
- set(res_find.get("member_user", [])) -
+ set(res_find.get("memberuser_user", [])) -
set(user or []))
group_add = list(
set(group or []) -
- set(res_find.get("member_group", [])))
+ set(res_find.get("memberuser_group", [])))
group_del = list(
- set(res_find.get("member_group", [])) -
+ set(res_find.get("memberuser_group", [])) -
set(group or []))
# Add hosts and hostgroups
diff --git a/tests/hbacrule/test_hbacrule.yml b/tests/hbacrule/test_hbacrule.yml
index a5615cc..38858d3 100644
--- a/tests/hbacrule/test_hbacrule.yml
+++ b/tests/hbacrule/test_hbacrule.yml
@@ -1,338 +1,629 @@
---
-- name: Tests
+- name: Playbook to handle hbacrules
hosts: ipaserver
become: true
- gather_facts: false
tasks:
- - name: Ensure HBAC Rule allhosts is absent
- ipahbacrule:
+ - name: Get Domain from server name
+ set_fact:
+ ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}"
+ when: ipaserver_domain is not defined
+
+ # CLEANUP TEST ITEMS
+
+ - name: Ensure test hosts are absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ - "{{ 'testhost03.' + ipaserver_domain }}"
+ - "{{ 'testhost04.' + ipaserver_domain }}"
+ state: absent
+
+ - name: Ensure test hostgroups are absent
+ ipahostgroup:
ipaadmin_password: MyPassword123
- name: allhosts,sshd-pinky,loginRule
+ name: testhostgroup01,testhostgroup02,testhostgroup03,testhostgroup04
state: absent
- - name: User pinky absent
+ - name: Ensure test users are absent
ipauser:
ipaadmin_password: MyPassword123
- name: pinky
+ name: testuser01,testuser02,testuser03,testuser04
state: absent
- - name: User group login absent
+ - name: Ensure test user groups are absent
ipagroup:
ipaadmin_password: MyPassword123
- name: login
+ name: testgroup01,testgroup02,testgroup03,testgroup04
+ state: absent
+
+ - name: Ensure test HBAC Services are absent
+ ipahbacsvc:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvc01,testhbacsvc02,testhbacsvc03,testhbacsvc04
+ state: absent
+
+ - name: Ensure test HBAC Service Groups are absent
+ ipahbacsvcgroup:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvcgroup01,testhbacsvcgroup02,testhbacsvcgroup03,testhbacsvcgroup04
state: absent
- - name: User pinky present
+ # CREATE TEST ITEMS
+
+ - name: Ensure hosts "{{ 'host[1..4].' + ipaserver_domain }}" are present
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ 'testhost01.' + ipaserver_domain }}"
+ force: yes
+ - name: "{{ 'testhost02.' + ipaserver_domain }}"
+ force: yes
+ - name: "{{ 'testhost03.' + ipaserver_domain }}"
+ force: yes
+ - name: "{{ 'testhost04.' + ipaserver_domain }}"
+ force: yes
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure host-group testhostgroup01 is present
+ ipahostgroup:
+ ipaadmin_password: MyPassword123
+ name: testhostgroup01
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure host-group testhostgroup02 is present
+ ipahostgroup:
+ ipaadmin_password: MyPassword123
+ name: testhostgroup02
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure host-group testhostgroup03 is present
+ ipahostgroup:
+ ipaadmin_password: MyPassword123
+ name: testhostgroup03
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure host-group testhostgroup04 is present
+ ipahostgroup:
+ ipaadmin_password: MyPassword123
+ name: testhostgroup04
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure testusers are present
ipauser:
ipaadmin_password: MyPassword123
- name: pinky
- uid: 10001
- gid: 100
- phone: "+555123457"
- email: pinky@acme.com
- principalexpiration: "20220119235959"
- #passwordexpiration: "2022-01-19 23:59:59"
- first: pinky
- last: Acme
+ users:
+ - name: testuser01
+ first: test
+ last: user01
+ - name: testuser02
+ first: test
+ last: user02
+ - name: testuser03
+ first: test
+ last: user03
+ - name: testuser04
+ first: test
+ last: user04
register: result
failed_when: not result.changed
- - name: User group login present
+ - name: Ensure user group testgroup01 is present
ipagroup:
ipaadmin_password: MyPassword123
- name: login
+ name: testgroup01
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule allhosts is present
- ipahbacrule:
+ - name: Ensure user group testgroup02 is present
+ ipagroup:
ipaadmin_password: MyPassword123
- name: allhosts
- usercategory: all
+ name: testgroup02
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule allhosts is present again
- ipahbacrule:
+ - name: Ensure user group testgroup03 is present
+ ipagroup:
ipaadmin_password: MyPassword123
- name: allhosts
- usercategory: all
+ name: testgroup03
register: result
- failed_when: result.changed
+ failed_when: not result.changed
- - name: Ensure host "{{ groups.ipaserver[0] }}" is present in HBAC Rule allhosts
+ - name: Ensure user group testgroup04 is present
+ ipagroup:
+ ipaadmin_password: MyPassword123
+ name: testgroup04
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service testhbacsvc01 is present
+ ipahbacsvc:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvc01
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service testhbacsvc02 is present
+ ipahbacsvc:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvc02
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service testhbacsvc03 is present
+ ipahbacsvc:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvc03
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service testhbacsvc04 is present
+ ipahbacsvc:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvc04
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service Group testhbacsvcgroup01 is present
+ ipahbacsvcgroup:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvcgroup01
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service Group testhbacsvcgroup02 is present
+ ipahbacsvcgroup:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvcgroup02
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service Group testhbacsvcgroup03 is present
+ ipahbacsvcgroup:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvcgroup03
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC Service Group testhbacsvcgroup04 is present
+ ipahbacsvcgroup:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvcgroup04
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure test HBAC rule hbacrule01 is absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: allhosts
- host: "{{ groups.ipaserver[0] }}"
- action: member
+ name: hbacrule01
+ state: absent
+
+ # ENSURE HBACRULE
+
+ - name: Ensure HBAC rule hbacrule01 is present
+ ipahbacrule:
+ ipaadmin_password: MyPassword123
+ name: hbacrule01
register: result
failed_when: not result.changed
- - name: Ensure host "{{ groups.ipaserver[0] }}" is present in HBAC Rule allhosts again
+ - name: Ensure HBAC rule hbacrule01 is present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: allhosts
- host: "{{ groups.ipaserver[0] }}"
- action: member
+ name: hbacrule01
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule sshd-pinky is present
+ # CHANGE HBACRULE WITH ALL MEMBERS
+
+ - name: Ensure HBAC rule hbacrule01 is present with hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- hostcategory: all
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ hostgroup: testhostgroup01,testhostgroup02
+ user: testuser01,testuser02
+ group: testgroup01,testgroup02
+ hbacsvc: testhbacsvc01,testhbacsvc02
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule sshd-pinky is present again
+ - name: Ensure HBAC rule hbacrule01 is present with hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- hostcategory: all
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ hostgroup: testhostgroup01,testhostgroup02
+ user: testuser01,testuser02
+ group: testgroup01,testgroup02
+ hbacsvc: testhbacsvc01,testhbacsvc02
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
register: result
failed_when: result.changed
- - name: Ensure user pinky is present in HBAC Rule sshd-pinky
+ # REMOVE MEMBERS ONE BY ONE
+
+ - name: Ensure test HBAC rule hbacrule01 host members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- user: pinky
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ state: absent
action: member
register: result
failed_when: not result.changed
- - name: Ensure user pinky is present in HBAC Rule sshd-pinky again
+ - name: Ensure test HBAC rule hbacrule01 host members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- user: pinky
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ state: absent
action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC service sshd is present in HBAC Rule sshd-pinky
+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- hbacsvc: sshd
+ name: hbacrule01
+ hostgroup: testhostgroup01,testhostgroup02
+ state: absent
action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC service sshd is present in HBAC Rule sshd-pinky again
+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- hbacsvc: sshd
+ name: hbacrule01
+ hostgroup: testhostgroup01,testhostgroup02
+ state: absent
action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule loginRule is present with HBAC service sshd
+ - name: Ensure test HBAC rule hbacrule01 user members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
- group: login
+ name: hbacrule01
+ user: testuser01,testuser02
+ state: absent
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule loginRule is present with HBAC service sshd again
+ - name: Ensure test HBAC rule hbacrule01 user members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
- group: login
+ name: hbacrule01
+ user: testuser01,testuser02
+ state: absent
+ action: member
register: result
failed_when: result.changed
- - name: Ensure user pinky is present in HBAC Rule loginRule
+ - name: Ensure test HBAC rule hbacrule01 user group members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
- user: pinky
+ name: hbacrule01
+ group: testgroup01,testgroup02
+ state: absent
action: member
register: result
failed_when: not result.changed
- - name: Ensure user pinky is present in HBAC Rule loginRule again
+ - name: Ensure test HBAC rule hbacrule01 user group members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
- user: pinky
+ name: hbacrule01
+ group: testgroup01,testgroup02
+ state: absent
action: member
register: result
failed_when: result.changed
- - name: Ensure user pinky is absent in HBAC Rule loginRule
+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
- user: pinky
- action: member
+ name: hbacrule01
+ hbacsvc: testhbacsvc01,testhbacsvc02
state: absent
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure user pinky is absent in HBAC Rule loginRule again
+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
- user: pinky
- action: member
+ name: hbacrule01
+ hbacsvc: testhbacsvc01,testhbacsvc02
state: absent
+ action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule loginRule is absent
+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
+ name: hbacrule01
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
state: absent
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule loginRule is absent again
+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: loginRule
+ name: hbacrule01
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
state: absent
+ action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC service sshd is absent in HBAC Rule sshd-pinky
+ # ADD MEMBERS BACK
+
+ - name: Ensure test HBAC rule hbacrule01 host members are present
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- hbacsvc: sshd
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
action: member
- state: absent
register: result
failed_when: not result.changed
- - name: Ensure HBAC service sshd is absent in HBAC Rule sshd-pinky again
+ - name: Ensure test HBAC rule hbacrule01 host members are present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- hbacsvc: sshd
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
action: member
- state: absent
register: result
failed_when: result.changed
- - name: Ensure user pinky is absent in HBAC Rule sshd-pinky
+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are present
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- user: pinky
+ name: hbacrule01
+ hostgroup: testhostgroup01,testhostgroup02
action: member
- state: absent
register: result
failed_when: not result.changed
- - name: Ensure user pinky is absent in HBAC Rule sshd-pinky again
+ - name: Ensure test HBAC rule hbacrule01 hostgroup members are present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- user: pinky
+ name: hbacrule01
+ hostgroup: testhostgroup01,testhostgroup02
action: member
- state: absent
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule sshd-pinky is disabled
+ - name: Ensure test HBAC rule hbacrule01 user members are present
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- state: disabled
+ name: hbacrule01
+ user: testuser01,testuser02
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule sshd-pinky is disabled again
+ - name: Ensure test HBAC rule hbacrule01 user members are present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- state: disabled
+ name: hbacrule01
+ user: testuser01,testuser02
+ action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule sshd-pinky is enabled
+ - name: Ensure test HBAC rule hbacrule01 user group members are present
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- state: enabled
+ name: hbacrule01
+ group: testgroup01,testgroup02
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule sshd-pinky is enabled again
+ - name: Ensure test HBAC rule hbacrule01 user group members are present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- state: enabled
+ name: hbacrule01
+ group: testgroup01,testgroup02
+ action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule sshd-pinky is absent
+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are present
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- state: absent
+ name: hbacrule01
+ hbacsvc: testhbacsvc01,testhbacsvc02
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule sshd-pinky is absent again
+ - name: Ensure test HBAC rule hbacrule01 hbacsvc members are present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: sshd-pinky
- state: absent
+ name: hbacrule01
+ hbacsvc: testhbacsvc01,testhbacsvc02
+ action: member
register: result
failed_when: result.changed
- - name: Ensure host "{{ groups.ipaserver[0] }}" is absent in HBAC Rule allhosts
+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are present
ipahbacrule:
ipaadmin_password: MyPassword123
- name: allhosts
- host: "{{ groups.ipaserver[0] }}"
+ name: hbacrule01
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
action: member
- state: absent
register: result
failed_when: not result.changed
- - name: Ensure host "{{ groups.ipaserver[0] }}" is absent in HBAC Rule allhosts again
+ - name: Ensure test HBAC rule hbacrule01 hbacsvcgroup members are present again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: allhosts
- host: "{{ groups.ipaserver[0] }}"
+ name: hbacrule01
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
action: member
+ register: result
+ failed_when: result.changed
+
+ # CHANGE TO DIFFERENT MEMBERS
+
+ - name: Ensure HBAC rule hbacrule01 is present with different hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups
+ ipahbacrule:
+ ipaadmin_password: MyPassword123
+ name: hbacrule01
+ host:
+ - "{{ 'testhost03.' + ipaserver_domain }}"
+ - "{{ 'testhost04.' + ipaserver_domain }}"
+ hostgroup: testhostgroup03,testhostgroup04
+ user: testuser03,testuser04
+ group: testgroup03,testgroup04
+ hbacsvc: testhbacsvc03,testhbacsvc04
+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure HBAC rule hbacrule01 is present with different hosts, hostgroups, users, groups, hbassvcs and hbacsvcgroups again
+ ipahbacrule:
+ ipaadmin_password: MyPassword123
+ name: hbacrule01
+ host:
+ - "{{ 'testhost03.' + ipaserver_domain }}"
+ - "{{ 'testhost04.' + ipaserver_domain }}"
+ hostgroup: testhostgroup03,testhostgroup04
+ user: testuser03,testuser04
+ group: testgroup03,testgroup04
+ hbacsvc: testhbacsvc03,testhbacsvc04
+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04
+ register: result
+ failed_when: result.changed
+
+ # ENSURE OLD TEST MEMBERS ARE ABSENT
+
+ - name: Ensure HBAC rule hbacrule01 members (same) are present
+ ipahbacrule:
+ ipaadmin_password: MyPassword123
+ name: hbacrule01
+ host:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ hostgroup: testhostgroup01,testhostgroup02
+ user: testuser01,testuser02
+ group: testgroup01,testgroup02
+ hbacsvc: testhbacsvc01,testhbacsvc02
+ hbacsvcgroup: testhbacsvcgroup01,testhbacsvcgroup02
state: absent
+ action: member
register: result
failed_when: result.changed
- - name: Ensure HBAC Rule allhosts is absent
+ # ENSURE NEW TEST MEMBERS ARE ABSENT
+
+ - name: Ensure HBAC rule hbacrule01 members are absent
ipahbacrule:
ipaadmin_password: MyPassword123
- name: allhosts
+ name: hbacrule01
+ host:
+ - "{{ 'testhost03.' + ipaserver_domain }}"
+ - "{{ 'testhost04.' + ipaserver_domain }}"
+ hostgroup: testhostgroup03,testhostgroup04
+ user: testuser03,testuser04
+ group: testgroup03,testgroup04
+ hbacsvc: testhbacsvc03,testhbacsvc04
+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04
state: absent
+ action: member
register: result
failed_when: not result.changed
- - name: Ensure HBAC Rule allhosts is absent again
+ - name: Ensure HBAC rule hbacrule01 members are absent again
ipahbacrule:
ipaadmin_password: MyPassword123
- name: allhosts
+ name: hbacrule01
+ host:
+ - "{{ 'testhost03.' + ipaserver_domain }}"
+ - "{{ 'testhost04.' + ipaserver_domain }}"
+ hostgroup: testhostgroup03,testhostgroup04
+ user: testuser03,testuser04
+ group: testgroup03,testgroup04
+ hbacsvc: testhbacsvc03,testhbacsvc04
+ hbacsvcgroup: testhbacsvcgroup03,testhbacsvcgroup04
state: absent
+ action: member
register: result
failed_when: result.changed
- - name: User pinky absent
+ # CLEANUP TEST ITEMS
+
+ - name: Ensure test HBAC rule hbacrule01 is absent
+ ipahbacrule:
+ ipaadmin_password: MyPassword123
+ name: hbacrule01
+ state: absent
+
+ - name: Ensure test hosts are absent
+ ipahostgroup:
+ ipaadmin_password: MyPassword123
+ name:
+ - "{{ 'testhost01.' + ipaserver_domain }}"
+ - "{{ 'testhost02.' + ipaserver_domain }}"
+ - "{{ 'testhost03.' + ipaserver_domain }}"
+ - "{{ 'testhost04.' + ipaserver_domain }}"
+ state: absent
+
+ - name: Ensure test hostgroups are absent
+ ipahostgroup:
+ ipaadmin_password: MyPassword123
+ name: testhostgroup01,testhostgroup02,testhostgroup03,testhostgroup04
+ state: absent
+
+ - name: Ensure test users are absent
ipauser:
ipaadmin_password: MyPassword123
- name: pinky
+ name: testuser01,testuser02,testuser03,testuser04
state: absent
- - name: User group login absent
+ - name: Ensure test user groups are absent
ipagroup:
ipaadmin_password: MyPassword123
- name: login
+ name: testgroup01,testgroup02,testgroup03,testgroup04
+ state: absent
+
+ - name: Ensure test HBAC Services are absent
+ ipahbacsvc:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvc01,testhbacsvc02,testhbacsvc03,testhbacsvc04
+ state: absent
+
+ - name: Ensure test HBAC Service Groups are absent
+ ipahbacsvcgroup:
+ ipaadmin_password: MyPassword123
+ name: testhbacsvcgroup01,testhbacsvcgroup02,testhbacsvcgroup03,testhbacsvcgroup04
state: absent

View File

@ -1,106 +0,0 @@
From 22d8784da29dcfede0744ef6b691b4506eae5deb Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 20 Feb 2020 12:58:11 +0100
Subject: [PATCH] ipahost: Do not fail on missing DNS or zone when no IP
address given
If no IP address is given and either DNS is not configured or if the zone is
not found then ipahost may not fail in dnsrecord_find.
The error happened for example by ensuring the absence of a host that is not
part of the domain or for a host that has been added with force and is using
a domain that is not served by the DNS server in the domain. It also
happened if there was no DNS server in the domain at all.
A new test case has been added to test_host_ipaddresses.yml
The fix requires ipalib_errors provided by ansible_freeipa_module.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1804838
---
plugins/modules/ipahost.py | 17 +++++++++++++++--
tests/host/test_host_ipaddresses.yml | 9 +++++++++
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index 558560e..062f768 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -409,7 +409,7 @@
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \
- is_ipv4_addr, is_ipv6_addr
+ is_ipv4_addr, is_ipv6_addr, ipalib_errors
import six
@@ -871,7 +871,20 @@ def main():
# Make sure host exists
res_find = find_host(ansible_module, name)
- res_find_dnsrecord = find_dnsrecord(ansible_module, name)
+ try:
+ res_find_dnsrecord = find_dnsrecord(ansible_module, name)
+ except ipalib_errors.NotFound as e:
+ msg = str(e)
+ if ip_address is None and \
+ ("DNS is not configured" in msg or \
+ "DNS zone not found" in msg):
+ # IP address(es) not given and no DNS support in IPA
+ # -> Ignore failure
+ # IP address(es) not given and DNS zone is not found
+ # -> Ignore failure
+ res_find_dnsrecord = None
+ else:
+ ansible_module.fail_json(msg="%s: %s" % (host, msg))
# Create command
if state == "present":
diff --git a/tests/host/test_host_ipaddresses.yml b/tests/host/test_host_ipaddresses.yml
index 0a97dd5..136a610 100644
--- a/tests/host/test_host_ipaddresses.yml
+++ b/tests/host/test_host_ipaddresses.yml
@@ -301,6 +301,15 @@
register: result
failed_when: result.changed
+ - name: Absent host01.ihavenodns.info test
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: host01.ihavenodns.info
+ state: absent
+ register: result
+ failed_when: result.changed
+
- name: Host absent
ipahost:
ipaadmin_password: MyPassword123
From 4d94cb09a9fb09dd2576223b9be7f77d515202fb Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 20 Feb 2020 12:54:32 +0100
Subject: [PATCH] ansible_freeipa_module: Import ipalib.errors as ipalib_errors
For beeing able to catch ipalib.errors.NotFound errors in ipahost it is
needed to import ipalib.errors. ipalib.errors is now imported as
ipalib_errors to not have name conflicts with the errors list used in some
of the modules.
Related: https://bugzilla.redhat.com/show_bug.cgi?id=1804838
---
plugins/module_utils/ansible_freeipa_module.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 6acdbef..5066de3 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -28,6 +28,7 @@
import gssapi
from datetime import datetime
from ipalib import api
+from ipalib import errors as ipalib_errors
from ipalib.config import Env
from ipalib.constants import DEFAULT_CONFIG, LDAP_GENERALIZED_TIME_FORMAT
try:

View File

@ -1,51 +0,0 @@
From 24515e40ad289552d45bddd33c7a0dda93117a7f Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Wed, 18 Dec 2019 12:28:03 +0100
Subject: [PATCH] ipahost: Enhanced failure msg for member params used without
member action
The failure message if member parameters like certificate, managedby_host,
principal, allow_create_keytab_* and allow_retrieve_keytab_* are used
without member action for state absent has been enhanced to propose the
member action.
---
plugins/modules/ipahost.py | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index ec5e196..8ee9532 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -511,19 +511,25 @@ def check_parameters(
"userclass", "auth_ind", "requires_pre_auth",
"ok_as_delegate", "ok_to_auth_as_delegate", "force",
"reverse", "ip_address", "update_password"]
+ for x in invalid:
+ if vars()[x] is not None:
+ module.fail_json(
+ msg="Argument '%s' can not be used with state '%s'" %
+ (x, state))
if action == "host":
- invalid.extend([
+ invalid = [
"certificate", "managedby_host", "principal",
"allow_create_keytab_user", "allow_create_keytab_group",
"allow_create_keytab_host", "allow_create_keytab_hostgroup",
"allow_retrieve_keytab_user", "allow_retrieve_keytab_group",
"allow_retrieve_keytab_host",
- "allow_retrieve_keytab_hostgroup"])
- for x in invalid:
- if vars()[x] is not None:
- module.fail_json(
- msg="Argument '%s' can not be used with state '%s'" %
- (x, state))
+ "allow_retrieve_keytab_hostgroup"
+ ]
+ for x in invalid:
+ if vars()[x] is not None:
+ module.fail_json(
+ msg="Argument '%s' can only be used with action "
+ "'member' for state '%s'" % (x, state))
def main():

View File

@ -1,57 +0,0 @@
From 0816b0773b1535780c7c3e5f05bda39434ab6bac Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Fri, 14 Feb 2020 13:21:54 +0100
Subject: [PATCH] ipahost: Fail on action member for new hosts, fix
dnsrecord_add reverse flag
The check to make sure that member can not be used on non existing hosts
has bee missing. Also the reverse flag for the dnsrecord_add call was None
if the varaible was not set.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1803026
---
plugins/modules/ipahost.py | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index a5fd482..558560e 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -1005,6 +1005,11 @@ def main():
dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord"))
+ else:
+ if res_find is None:
+ ansible_module.fail_json(
+ msg="No host '%s'" % name)
+
if action != "host" or (action == "host" and res_find is None):
certificate_add = certificate or []
certificate_del = []
@@ -1178,15 +1183,17 @@ def main():
domain_name = name[name.find(".")+1:]
host_name = name[:name.find(".")]
+ _args = {"idnsname": host_name}
+ if reverse is not None:
+ _args["a_extra_create_reverse"] = reverse
+ _args["aaaa_extra_create_reverse"] = reverse
+ if len(dnsrecord_a_add) > 0:
+ _args["arecord"] = dnsrecord_a_add
+ if len(dnsrecord_aaaa_add) > 0:
+ _args["aaaarecord"] = dnsrecord_aaaa_add
+
commands.append([domain_name,
- "dnsrecord_add",
- {
- "idnsname": host_name,
- "arecord": dnsrecord_a_add,
- "a_extra_create_reverse": reverse,
- "aaaarecord": dnsrecord_aaaa_add,
- "aaaa_extra_create_reverse": reverse
- }])
+ "dnsrecord_add", _args])
if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0:
domain_name = name[name.find(".")+1:]

View File

@ -1,78 +0,0 @@
From b6100f0c19e2caf73ab70bbc572d3e47e6066b48 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Tue, 17 Dec 2019 14:04:43 +0100
Subject: [PATCH] ipahost: Fix choices of auth_ind parameter, allow to reset
parameter
The choices for the auth_ind parameter have been wrong. The choices are now
['radius', 'otp', 'pkinit', 'hardened', '']. The empty string has been added
to be able to rest auth_ind for the host entry.
---
README-host.md | 2 +-
plugins/modules/ipahost.py | 15 ++++++++++++---
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/README-host.md b/README-host.md
index edec8d9..be5ad79 100644
--- a/README-host.md
+++ b/README-host.md
@@ -280,7 +280,7 @@ Variable | Description | Required
`mac_address` \| `macaddress` | List of hardware MAC addresses. | no
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys | no
`userclass` \| `class` | Host category (semantics placed on this attribute are for local interpretation) | no
-`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened"] | no
+`auth_ind` \| `krbprincipalauthind` | Defines a whitelist for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened", ""] | no
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service (bool) | no
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service (bool) | no
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index ec5e196..b130395 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -147,9 +147,10 @@
Defines a whitelist for Authentication Indicators. Use 'otp' to allow
OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA
authentications. Other values may be used for custom configurations.
+ Use empty string to reset auth_ind to the initial value.
type: list
aliases: ["krbprincipalauthind"]
- choices: ["radius", "otp", "pkinit", "hardened"]
+ choices: ["radius", "otp", "pkinit", "hardened", ""]
required: false
requires_pre_auth:
description: Pre-authentication is required for the service
@@ -277,9 +278,10 @@
Defines a whitelist for Authentication Indicators. Use 'otp' to allow
OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA
authentications. Other values may be used for custom configurations.
+ Use empty string to reset auth_ind to the initial value.
type: list
aliases: ["krbprincipalauthind"]
- choices: ["radius", "otp", "pkinit", "hardened"]
+ choices: ["radius", "otp", "pkinit", "hardened", ""]
required: false
requires_pre_auth:
description: Pre-authentication is required for the service
@@ -590,7 +592,7 @@ def main():
default=None),
auth_ind=dict(type='list', aliases=["krbprincipalauthind"],
default=None,
- choices=['password', 'radius', 'otp']),
+ choices=['radius', 'otp', 'pkinit', 'hardened', '']),
requires_pre_auth=dict(type="bool", aliases=["ipakrbrequirespreauth"],
default=None),
ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"],
@@ -835,6 +837,13 @@ def main():
if x in args:
del args[x]
+ # Ignore auth_ind if it is empty (for resetting)
+ # and not set in for the host
+ if "krbprincipalauthind" not in res_find and \
+ "krbprincipalauthind" in args and \
+ args["krbprincipalauthind"] == ['']:
+ del args["krbprincipalauthind"]
+
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify

View File

@ -1,179 +0,0 @@
From 4dd1d25eacd1481be0a881a017144ff4d3396ccd Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 6 Feb 2020 15:38:00 +0100
Subject: [PATCH] ipapwpolicy: Use global_policy if name is not set
If the name is not set, the policy global_policy is now used. It was needed
before to explicitly name the global_policy. Also a check has been added
to fail early if global_policy is used with state absent.
The README for pwpolicy has been extended with an example for global_policy
and also the description of the name variable.
The test has also been extended to check a change of maxlife for
global_policy and that global_policy can not be used with state: absent
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1797532
---
README-pwpolicy.md | 19 +++++++++++--
plugins/modules/ipapwpolicy.py | 9 ++++--
tests/pwpolicy/test_pwpolicy.yml | 49 ++++++++++++++++++++++++++++++++
3 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/README-pwpolicy.md b/README-pwpolicy.md
index 16306b7..847b32d 100644
--- a/README-pwpolicy.md
+++ b/README-pwpolicy.md
@@ -56,7 +56,7 @@ Example playbook to ensure presence of pwpolicies for exisiting group ops:
maxfail: 3
```
-Example playbook to ensure absence of pwpolicies for group ops
+Example playbook to ensure absence of pwpolicies for group ops:
```yaml
---
@@ -72,6 +72,21 @@ Example playbook to ensure absence of pwpolicies for group ops
state: absent
```
+Example playbook to ensure maxlife is set to 49 in global policy:
+
+```yaml
+---
+- name: Playbook to handle pwpolicies
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ # Ensure absence of pwpolicies for group ops
+ - ipapwpolicy:
+ ipaadmin_password: MyPassword123
+ maxlife: 49
+```
+
Variables
=========
@@ -83,7 +98,7 @@ Variable | Description | Required
-------- | ----------- | --------
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
-`name` \| `cn` | The list of pwpolicy name strings. | no
+`name` \| `cn` | The list of pwpolicy name strings. If name is not given, `global_policy` will be used automatically. | no
`maxlife` \| `krbmaxpwdlife` | Maximum password lifetime in days. (int) | no
`minlife` \| `krbminpwdlife` | Minimum password lifetime in hours. (int) | no
`history` \| `krbpwdhistorylength` | Password history size. (int) | no
diff --git a/plugins/modules/ipapwpolicy.py b/plugins/modules/ipapwpolicy.py
index 9437b59..f168703 100644
--- a/plugins/modules/ipapwpolicy.py
+++ b/plugins/modules/ipapwpolicy.py
@@ -167,7 +167,7 @@ def main():
ipaadmin_password=dict(type="str", required=False, no_log=True),
name=dict(type="list", aliases=["cn"], default=None,
- required=True),
+ required=False),
# present
maxlife=dict(type="int", aliases=["krbmaxpwdlife"], default=None),
@@ -218,6 +218,9 @@ def main():
# Check parameters
+ if names is None:
+ names = ["global_policy"]
+
if state == "present":
if len(names) != 1:
ansible_module.fail_json(
@@ -225,8 +228,10 @@ def main():
if state == "absent":
if len(names) < 1:
+ ansible_module.fail_json(msg="No name given.")
+ if "global_policy" in names:
ansible_module.fail_json(
- msg="No name given.")
+ msg="'global_policy' can not be made absent.")
invalid = ["maxlife", "minlife", "history", "minclasses",
"minlength", "priority", "maxfail", "failinterval",
"lockouttime"]
diff --git a/tests/pwpolicy/test_pwpolicy.yml b/tests/pwpolicy/test_pwpolicy.yml
index 5c69345..f93f275 100644
--- a/tests/pwpolicy/test_pwpolicy.yml
+++ b/tests/pwpolicy/test_pwpolicy.yml
@@ -5,10 +5,30 @@
gather_facts: false
tasks:
+ - name: Ensure maxlife of 90 for global_policy
+ ipapwpolicy:
+ ipaadmin_password: SomeADMINpassword
+ maxlife: 90
+
+ - name: Ensure absence of group ops
+ ipagroup:
+ ipaadmin_password: SomeADMINpassword
+ name: ops
+ state: absent
+
+ - name: Ensure absence of pwpolicies for group ops
+ ipapwpolicy:
+ ipaadmin_password: SomeADMINpassword
+ name: ops
+ state: absent
+
- name: Ensure presence of group ops
ipagroup:
ipaadmin_password: SomeADMINpassword
name: ops
+ state: present
+ register: result
+ failed_when: not result.changed
- name: Ensure presence of pwpolicies for group ops
ipapwpolicy:
@@ -42,6 +62,28 @@
register: result
failed_when: result.changed
+ - name: Ensure maxlife of 49 for global_policy
+ ipapwpolicy:
+ ipaadmin_password: SomeADMINpassword
+ maxlife: 49
+ register: result
+ failed_when: not result.changed
+
+ - name: Ensure maxlife of 49 for global_policy again
+ ipapwpolicy:
+ ipaadmin_password: SomeADMINpassword
+ maxlife: 49
+ register: result
+ failed_when: result.changed
+
+ - name: Ensure absence of pwpoliciy global_policy will fail
+ ipapwpolicy:
+ ipaadmin_password: SomeADMINpassword
+ state: absent
+ register: result
+ ignore_errors: True
+ failed_when: result is defined and result
+
- name: Ensure absence of pwpolicies for group ops
ipapwpolicy:
ipaadmin_password: SomeADMINpassword
@@ -50,6 +92,13 @@
register: result
failed_when: not result.changed
+ - name: Ensure maxlife of 90 for global_policy
+ ipapwpolicy:
+ ipaadmin_password: MyPassword123
+ maxlife: 90
+ register: result
+ failed_when: not result.changed
+
- name: Ensure absence of pwpolicies for group ops
ipapwpolicy:
ipaadmin_password: SomeADMINpassword

View File

@ -1,116 +0,0 @@
From 36c1c837086c42049f09cf689a1ebd61627abae0 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Tue, 17 Dec 2019 15:30:45 +0100
Subject: [PATCH] ipauser: Allow reset of userauthtype, do not depend on
first,last for mod
It was not possible to reset the userauthtype. The empty string has been
added to userauthtype for this.
Also ipauser will only depend on given first and last name if the user
does not exist yet. For the update operation these parameters are not
needed anymore.
---
README-user.md | 2 +-
plugins/modules/ipauser.py | 38 ++++++++++++++++++++++++++------------
2 files changed, 27 insertions(+), 13 deletions(-)
diff --git a/README-user.md b/README-user.md
index 56772a7..991121c 100644
--- a/README-user.md
+++ b/README-user.md
@@ -408,7 +408,7 @@ Variable | Description | Required
`manager` | List of manager user names. | no
`carlicense` | List of car licenses. | no
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no
-`userauthtype` | List of supported user authentication types. Choices: `password`, `radius` and `otp` | no
+`userauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp` and ``. Use empty string to reset userauthtype to the initial value. | no
`userclass` | User category. (semantics placed on this attribute are for local interpretation). | no
`radius` | RADIUS proxy configuration | no
`radiususer` | RADIUS proxy username | no
diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py
index ac45295..36e8bae 100644
--- a/plugins/modules/ipauser.py
+++ b/plugins/modules/ipauser.py
@@ -153,9 +153,12 @@
required: false
aliases: ["ipasshpubkey"]
userauthtype:
- description: List of supported user authentication types
- choices=['password', 'radius', 'otp']
+ description:
+ List of supported user authentication types
+ Use empty string to reset userauthtype to the initial value.
+ choices=['password', 'radius', 'otp', '']
required: false
+ aliases: ["ipauserauthtype"]
userclass:
description:
- User category
@@ -310,9 +313,12 @@
required: false
aliases: ["ipasshpubkey"]
userauthtype:
- description: List of supported user authentication types
- choices=['password', 'radius', 'otp']
+ description:
+ List of supported user authentication types
+ Use empty string to reset userauthtype to the initial value.
+ choices=['password', 'radius', 'otp', '']
required: false
+ aliases: ["ipauserauthtype"]
userclass:
description:
- User category
@@ -701,7 +707,7 @@ def main():
default=None),
userauthtype=dict(type='list', aliases=["ipauserauthtype"],
default=None,
- choices=['password', 'radius', 'otp']),
+ choices=['password', 'radius', 'otp', '']),
userclass=dict(type="list", aliases=["class"],
default=None),
radius=dict(type="str", aliases=["ipatokenradiusconfiglink"],
@@ -845,13 +851,6 @@ def main():
if names is not None and len(names) != 1:
ansible_module.fail_json(
msg="Only one user can be added at a time using name.")
- if action != "member":
- # Only check first and last here if names is set
- if names is not None:
- if first is None:
- ansible_module.fail_json(msg="First name is needed")
- if last is None:
- ansible_module.fail_json(msg="Last name is needed")
check_parameters(
ansible_module, state, action,
@@ -1011,6 +1010,13 @@ def main():
if "noprivate" in args:
del args["noprivate"]
+ # Ignore userauthtype if it is empty (for resetting)
+ # and not set in for the user
+ if "ipauserauthtype" not in res_find and \
+ "ipauserauthtype" in args and \
+ args["ipauserauthtype"] == ['']:
+ del args["ipauserauthtype"]
+
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
@@ -1019,6 +1025,14 @@ def main():
commands.append([name, "user_mod", args])
else:
+ # Make sure we have a first and last name
+ if first is None:
+ ansible_module.fail_json(
+ msg="First name is needed")
+ if last is None:
+ ansible_module.fail_json(
+ msg="Last name is needed")
+
commands.append([name, "user_add", args])
# Handle members: principal, manager, certificate and

View File

@ -1,915 +0,0 @@
From 167c76311da72c2bfabf4b2bce9e128c11d519d0 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Wed, 12 Feb 2020 16:54:13 +0100
Subject: [PATCH] ipahost: Add support for several IP addresses and also to
change them
ipahost was so far ignoring IP addresses when the host already existed.
This happened because host_mod is not providing functionality to do this.
Now ipaddress is a list and it is possible to ensure a host with several
IP addresses (these can be IPv4 and IPv6). Also it is possible to ensure
presence and absence of IP addresses for an exising host using action
member.
There are no IP address conclict checks as this would lead into issues with
updating an existing host that already is using a duplicate IP address for
example for round-robin (RR). Also this might lead into issues with ensuring
a new host with several IP addresses in this case. Also to ensure a list of
hosts with changing the IP address of one host to another in the list would
result in issues here.
New example playbooks have been added:
playbooks/host/host-present-with-several-ip-addresses.yml
playbooks/host/host-member-ipaddresses-absent.yml
playbooks/host/host-member-ipaddresses-present.yml
A new test has been added for verification:
tests/host/test_host_ipaddresses.yml
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1783976
https://bugzilla.redhat.com/show_bug.cgi?id=1783979
---
README-host.md | 79 ++++-
.../host/host-member-ipaddresses-absent.yml | 17 +
.../host/host-member-ipaddresses-present.yml | 16 +
...host-present-with-several-ip-addresses.yml | 24 ++
.../module_utils/ansible_freeipa_module.py | 23 ++
plugins/modules/ipahost.py | 179 +++++++---
tests/host/test_host_ipaddresses.yml | 312 ++++++++++++++++++
7 files changed, 600 insertions(+), 50 deletions(-)
create mode 100644 playbooks/host/host-member-ipaddresses-absent.yml
create mode 100644 playbooks/host/host-member-ipaddresses-present.yml
create mode 100644 playbooks/host/host-present-with-several-ip-addresses.yml
create mode 100644 tests/host/test_host_ipaddresses.yml
diff --git a/README-host.md b/README-host.md
index be5ad79..ecc59a9 100644
--- a/README-host.md
+++ b/README-host.md
@@ -65,6 +65,79 @@ Example playbook to ensure host presence:
- "52:54:00:BD:97:1E"
state: present
```
+Compared to `ipa host-add` command no IP address conflict check is done as the ipahost module supports to have several IPv4 and IPv6 addresses for a host.
+
+
+Example playbook to ensure host presence with several IP addresses:
+
+```yaml
+---
+- name: Playbook to handle hosts
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ # Ensure host is present
+ - ipahost:
+ ipaadmin_password: MyPassword123
+ name: host01.example.com
+ description: Example host
+ ip_address:
+ - 192.168.0.123
+ - 192.168.0.124
+ - fe80::20c:29ff:fe02:a1b3
+ - fe80::20c:29ff:fe02:a1b4
+ locality: Lab
+ ns_host_location: Lab
+ ns_os_version: CentOS 7
+ ns_hardware_platform: Lenovo T61
+ mac_address:
+ - "08:00:27:E3:B1:2D"
+ - "52:54:00:BD:97:1E"
+ state: present
+```
+
+
+Example playbook to ensure IP addresses are present for a host:
+
+```yaml
+---
+- name: Playbook to handle hosts
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ # Ensure host is present
+ - ipahost:
+ ipaadmin_password: MyPassword123
+ name: host01.example.com
+ ip_address:
+ - 192.168.0.124
+ - fe80::20c:29ff:fe02:a1b4
+ action: member
+ state: present
+```
+
+
+Example playbook to ensure IP addresses are absent for a host:
+
+```yaml
+---
+- name: Playbook to handle hosts
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ # Ensure host is present
+ - ipahost:
+ ipaadmin_password: MyPassword123
+ name: host01.example.com
+ ip_address:
+ - 192.168.0.124
+ - fe80::20c:29ff:fe02:a1b4
+ action: member
+ state: absent
+```
Example playbook to ensure host presence without DNS:
@@ -215,7 +288,7 @@ Example playbook to disable a host:
update_dns: yes
state: disabled
```
-`update_dns` controls if the DNS entries will be updated.
+`update_dns` controls if the DNS entries will be updated in this case. For `state` present it is controlling the update of the DNS SSHFP records, but not the the other DNS records.
Example playbook to ensure a host is absent:
@@ -286,8 +359,8 @@ Variable | Description | Required
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no
`force` | Force host name even if not in DNS. | no
`reverse` | Reverse DNS detection. | no
-`ip_address` \| `ipaddress` | The host IP address. | no
-`update_dns` | Update DNS entries. | no
+`ip_address` \| `ipaddress` | The host IP address list. It can contain IPv4 and IPv6 addresses. No conflict check for IP addresses is done. | no
+`update_dns` | For existing hosts: DNS SSHFP records are updated with `state` present and all DNS entries for a host removed with `state` absent. | no
Return Values
diff --git a/playbooks/host/host-member-ipaddresses-absent.yml b/playbooks/host/host-member-ipaddresses-absent.yml
new file mode 100644
index 0000000..2466dbd
--- /dev/null
+++ b/playbooks/host/host-member-ipaddresses-absent.yml
@@ -0,0 +1,17 @@
+---
+- name: Host member IP addresses absent
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Ensure host01.example.com IP addresses absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: host01.example.com
+ ip_address:
+ - 192.168.0.123
+ - fe80::20c:29ff:fe02:a1b3
+ - 192.168.0.124
+ - fe80::20c:29ff:fe02:a1b4
+ action: member
+ state: absent
diff --git a/playbooks/host/host-member-ipaddresses-present.yml b/playbooks/host/host-member-ipaddresses-present.yml
new file mode 100644
index 0000000..f473993
--- /dev/null
+++ b/playbooks/host/host-member-ipaddresses-present.yml
@@ -0,0 +1,16 @@
+---
+- name: Host member IP addresses present
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Ensure host01.example.com IP addresses present
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: host01.example.com
+ ip_address:
+ - 192.168.0.123
+ - fe80::20c:29ff:fe02:a1b3
+ - 192.168.0.124
+ - fe80::20c:29ff:fe02:a1b4
+ action: member
diff --git a/playbooks/host/host-present-with-several-ip-addresses.yml b/playbooks/host/host-present-with-several-ip-addresses.yml
new file mode 100644
index 0000000..4956562
--- /dev/null
+++ b/playbooks/host/host-present-with-several-ip-addresses.yml
@@ -0,0 +1,24 @@
+---
+- name: Host present with several IP addresses
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Ensure host is present
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: host01.example.com
+ description: Example host
+ ip_address:
+ - 192.168.0.123
+ - fe80::20c:29ff:fe02:a1b3
+ - 192.168.0.124
+ - fe80::20c:29ff:fe02:a1b4
+ locality: Lab
+ ns_host_location: Lab
+ ns_os_version: CentOS 7
+ ns_hardware_platform: Lenovo T61
+ mac_address:
+ - "08:00:27:E3:B1:2D"
+ - "52:54:00:BD:97:1E"
+ state: present
diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index 9e97b88..6acdbef 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -42,6 +42,7 @@
from ipalib.x509 import Encoding
except ImportError:
from cryptography.hazmat.primitives.serialization import Encoding
+import socket
import base64
import six
@@ -285,3 +286,25 @@ def encode_certificate(cert):
if not six.PY2:
encoded = encoded.decode('ascii')
return encoded
+
+
+def is_ipv4_addr(ipaddr):
+ """
+ Test if figen IP address is a valid IPv4 address
+ """
+ try:
+ socket.inet_pton(socket.AF_INET, ipaddr)
+ except socket.error:
+ return False
+ return True
+
+
+def is_ipv6_addr(ipaddr):
+ """
+ Test if figen IP address is a valid IPv6 address
+ """
+ try:
+ socket.inet_pton(socket.AF_INET6, ipaddr)
+ except socket.error:
+ return False
+ return True
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index dba4181..a5fd482 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -176,11 +176,16 @@
default: true
required: false
ip_address:
- description: The host IP address
+ description:
+ The host IP address list (IPv4 and IPv6). No IP address conflict
+ check will be done.
aliases: ["ipaddress"]
required: false
update_dns:
- description: Update DNS entries
+ description:
+ Controls the update of the DNS SSHFP records for existing hosts and
+ the removal of all DNS entries if a host gets removed with state
+ absent.
required: false
description:
description: The host description
@@ -306,11 +311,16 @@
default: true
required: false
ip_address:
- description: The host IP address
+ description:
+ The host IP address list (IPv4 and IPv6). No IP address conflict
+ check will be done.
aliases: ["ipaddress"]
required: false
update_dns:
- description: Update DNS entries
+ description:
+ Controls the update of the DNS SSHFP records for existing hosts and
+ the removal of all DNS entries if a host gets removed with state
+ absent.
required: false
update_password:
description:
@@ -398,7 +408,8 @@
from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
- module_params_get, gen_add_del_lists, encode_certificate, api_get_realm
+ module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \
+ is_ipv4_addr, is_ipv6_addr
import six
@@ -428,6 +439,32 @@ def find_host(module, name):
return None
+def find_dnsrecord(module, name):
+ domain_name = name[name.find(".")+1:]
+ host_name = name[:name.find(".")]
+
+ _args = {
+ "all": True,
+ "idnsname": to_text(host_name),
+ }
+
+ _result = api_command(module, "dnsrecord_find", to_text(domain_name),
+ _args)
+
+ if len(_result["result"]) > 1:
+ module.fail_json(
+ msg="There is more than one host '%s'" % (name))
+ elif len(_result["result"]) == 1:
+ _res = _result["result"][0]
+ certs = _res.get("usercertificate")
+ if certs is not None:
+ _res["usercertificate"] = [encode_certificate(cert) for
+ cert in certs]
+ return _res
+ else:
+ return None
+
+
def show_host(module, name):
_result = api_command(module, "host_show", to_text(name), {})
return _result["result"]
@@ -470,16 +507,34 @@ def gen_args(description, locality, location, platform, os, password, random,
_args["ipakrboktoauthasdelegate"] = ok_to_auth_as_delegate
if force is not None:
_args["force"] = force
- if reverse is not None:
- _args["no_reverse"] = not reverse
if ip_address is not None:
- _args["ip_address"] = ip_address
+ # IP addresses are handed extra, therefore it is needed to set
+ # the force option here to make sure that host-add is able to
+ # add a host without IP address.
+ _args["force"] = True
if update_dns is not None:
_args["updatedns"] = update_dns
return _args
+def gen_dnsrecord_args(module, ip_address, reverse):
+ _args = {}
+ if reverse is not None:
+ _args["a_extra_create_reverse"] = reverse
+ _args["aaaa_extra_create_reverse"] = reverse
+ if ip_address is not None:
+ for ip in ip_address:
+ if is_ipv4_addr(ip):
+ _args.setdefault("arecord", []).append(ip)
+ elif is_ipv6_addr(ip):
+ _args.setdefault("aaaarecord", []).append(ip)
+ else:
+ module.fail_json(msg="'%s' is not a valid IP address." % ip)
+
+ return _args
+
+
def check_parameters(
module, state, action,
description, locality, location, platform, os, password, random,
@@ -499,8 +554,7 @@ def check_parameters(
"os", "password", "random", "mac_address", "sshpubkey",
"userclass", "auth_ind", "requires_pre_auth",
"ok_as_delegate", "ok_to_auth_as_delegate", "force",
- "reverse", "ip_address", "update_dns",
- "update_password"]
+ "reverse", "update_dns", "update_password"]
for x in invalid:
if vars()[x] is not None:
module.fail_json(
@@ -512,7 +566,7 @@ def check_parameters(
"password", "random", "mac_address", "sshpubkey",
"userclass", "auth_ind", "requires_pre_auth",
"ok_as_delegate", "ok_to_auth_as_delegate", "force",
- "reverse", "ip_address", "update_password"]
+ "reverse", "update_password"]
for x in invalid:
if vars()[x] is not None:
module.fail_json(
@@ -549,9 +603,6 @@ def main():
default=None, no_log=True),
random=dict(type="bool", aliases=["random_password"],
default=None),
-
-
-
certificate=dict(type="list", aliases=["usercertificate"],
default=None),
managedby_host=dict(type="list",
@@ -608,7 +659,7 @@ def main():
default=None),
force=dict(type='bool', default=None),
reverse=dict(type='bool', default=None),
- ip_address=dict(type="str", aliases=["ipaddress"],
+ ip_address=dict(type="list", aliases=["ipaddress"],
default=None),
update_dns=dict(type="bool", aliases=["updatedns"],
default=None),
@@ -820,6 +871,7 @@ def main():
# Make sure host exists
res_find = find_host(ansible_module, name)
+ res_find_dnsrecord = find_dnsrecord(ansible_module, name)
# Create command
if state == "present":
@@ -829,6 +881,8 @@ def main():
random, mac_address, sshpubkey, userclass, auth_ind,
requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate,
force, reverse, ip_address, update_dns)
+ dnsrecord_args = gen_dnsrecord_args(
+ ansible_module, ip_address, reverse)
if action == "host":
# Found the host
@@ -938,39 +992,20 @@ def main():
res_find.get(
"ipaallowedtoperform_read_keys_hostgroup"))
- else:
- certificate_add = certificate or []
- certificate_del = []
- managedby_host_add = managedby_host or []
- managedby_host_del = []
- principal_add = principal or []
- principal_del = []
- allow_create_keytab_user_add = \
- allow_create_keytab_user or []
- allow_create_keytab_user_del = []
- allow_create_keytab_group_add = \
- allow_create_keytab_group or []
- allow_create_keytab_group_del = []
- allow_create_keytab_host_add = \
- allow_create_keytab_host or []
- allow_create_keytab_host_del = []
- allow_create_keytab_hostgroup_add = \
- allow_create_keytab_hostgroup or []
- allow_create_keytab_hostgroup_del = []
- allow_retrieve_keytab_user_add = \
- allow_retrieve_keytab_user or []
- allow_retrieve_keytab_user_del = []
- allow_retrieve_keytab_group_add = \
- allow_retrieve_keytab_group or []
- allow_retrieve_keytab_group_del = []
- allow_retrieve_keytab_host_add = \
- allow_retrieve_keytab_host or []
- allow_retrieve_keytab_host_del = []
- allow_retrieve_keytab_hostgroup_add = \
- allow_retrieve_keytab_hostgroup or []
- allow_retrieve_keytab_hostgroup_del = []
+ # IP addresses are not really a member of hosts, but
+ # we will simply treat it as this to enable the
+ # addition and removal of IPv4 and IPv6 addresses in
+ # a simple way.
+ _dnsrec = res_find_dnsrecord or {}
+ dnsrecord_a_add, dnsrecord_a_del = gen_add_del_lists(
+ dnsrecord_args.get("arecord"),
+ _dnsrec.get("arecord"))
+ dnsrecord_aaaa_add, dnsrecord_aaaa_del = \
+ gen_add_del_lists(
+ dnsrecord_args.get("aaaarecord"),
+ _dnsrec.get("aaaarecord"))
- else:
+ if action != "host" or (action == "host" and res_find is None):
certificate_add = certificate or []
certificate_del = []
managedby_host_add = managedby_host or []
@@ -1001,6 +1036,10 @@ def main():
allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = []
+ dnsrecord_a_add = dnsrecord_args.get("arecord") or []
+ dnsrecord_a_del = []
+ dnsrecord_aaaa_add = dnsrecord_args.get("aaaarecord") or []
+ dnsrecord_aaaa_del = []
# Remove canonical principal from principal_del
canonical_principal = "host/" + name + "@" + server_realm
@@ -1135,6 +1174,36 @@ def main():
"hostgroup": allow_retrieve_keytab_hostgroup_del,
}])
+ if len(dnsrecord_a_add) > 0 or len(dnsrecord_aaaa_add) > 0:
+ domain_name = name[name.find(".")+1:]
+ host_name = name[:name.find(".")]
+
+ commands.append([domain_name,
+ "dnsrecord_add",
+ {
+ "idnsname": host_name,
+ "arecord": dnsrecord_a_add,
+ "a_extra_create_reverse": reverse,
+ "aaaarecord": dnsrecord_aaaa_add,
+ "aaaa_extra_create_reverse": reverse
+ }])
+
+ if len(dnsrecord_a_del) > 0 or len(dnsrecord_aaaa_del) > 0:
+ domain_name = name[name.find(".")+1:]
+ host_name = name[:name.find(".")]
+
+ # There seems to be an issue with dnsrecord_del (not
+ # for dnsrecord_add) if aaaarecord is an empty list.
+ # Therefore this is done differently here:
+ _args = {"idnsname": host_name}
+ if len(dnsrecord_a_del) > 0:
+ _args["arecord"] = dnsrecord_a_del
+ if len(dnsrecord_aaaa_del) > 0:
+ _args["aaaarecord"] = dnsrecord_aaaa_del
+
+ commands.append([domain_name,
+ "dnsrecord_del", _args])
+
elif state == "absent":
if action == "host":
@@ -1215,6 +1284,17 @@ def main():
"hostgroup": allow_retrieve_keytab_hostgroup,
}])
+ dnsrecord_args = gen_dnsrecord_args(ansible_module,
+ ip_address, reverse)
+ if "arecord" in dnsrecord_args or \
+ "aaaarecord" in dnsrecord_args:
+ domain_name = name[name.find(".")+1:]
+ host_name = name[:name.find(".")]
+ dnsrecord_args["idnsname"] = host_name
+
+ commands.append([domain_name, "dnsrecord_del",
+ dnsrecord_args])
+
elif state == "disabled":
if res_find is not None:
commands.append([name, "host_disable", {}])
@@ -1259,6 +1339,11 @@ def main():
# Host is already disabled, ignore error
if "This entry is already disabled" in msg:
continue
+
+ # Ignore no modification error.
+ if "no modifications to be performed" in msg:
+ continue
+
ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
msg))
diff --git a/tests/host/test_host_ipaddresses.yml b/tests/host/test_host_ipaddresses.yml
new file mode 100644
index 0000000..0a97dd5
--- /dev/null
+++ b/tests/host/test_host_ipaddresses.yml
@@ -0,0 +1,312 @@
+---
+- name: Test host IP addresses
+ hosts: ipaserver
+ become: true
+
+ tasks:
+ - name: Get Domain from server name
+ set_fact:
+ ipaserver_domain: "{{ groups.ipaserver[0].split('.')[1:] | join ('.') }}"
+ when: ipaserver_domain is not defined
+
+ - name: Set host1_fqdn .. host6_fqdn
+ set_fact:
+ host1_fqdn: "{{ 'host1.' + ipaserver_domain }}"
+ host2_fqdn: "{{ 'host2.' + ipaserver_domain }}"
+ host3_fqdn: "{{ 'host3.' + ipaserver_domain }}"
+
+ - name: Get IPv4 address prefix from server node
+ set_fact:
+ ipv4_prefix: "{{ ansible_default_ipv4.address.split('.')[:-1] |
+ join('.') }}"
+
+ - name: Host absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ - "{{ host3_fqdn }}"
+ update_dns: yes
+ state: absent
+
+ - name: Host "{{ host1_fqdn }}" present
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.201' }}"
+ - fe80::20c:29ff:fe02:a1b2
+ update_dns: yes
+ reverse: no
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.201' }}"
+ - fe80::20c:29ff:fe02:a1b2
+ update_dns: yes
+ reverse: no
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" present again with new IP address
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ update_dns: yes
+ reverse: no
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" present again with new IP address again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ update_dns: yes
+ reverse: no
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv4 address present
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.201' }}"
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv4 address present again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.201' }}"
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv4 address absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.201' }}"
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv4 address absent again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: "{{ ipv4_prefix + '.201' }}"
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv6 address present
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: fe80::20c:29ff:fe02:a1b2
+ action: member
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv6 address present again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: fe80::20c:29ff:fe02:a1b2
+ action: member
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv6 address absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: fe80::20c:29ff:fe02:a1b2
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" member IPv6 address absent again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address: fe80::20c:29ff:fe02:a1b2
+ action: member
+ state: absent
+ register: result
+
+ - name: Host "{{ host1_fqdn }}" member all ip-addresses absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ action: member
+ state: absent
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host1_fqdn }}" all member ip-addresses absent again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ action: member
+ state: absent
+ register: result
+ failed_when: result.changed
+
+ - name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" present with same IP addresses
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ - name: "{{ host2_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ register: result
+ failed_when: not result.changed
+
+ - name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" present with same IP addresses again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host1_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ - name: "{{ host2_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ register: result
+ failed_when: result.changed
+
+ - name: Hosts "{{ host3_fqdn }}" present with same IP addresses
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host3_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ register: result
+ failed_when: not result.changed
+
+ - name: Hosts "{{ host3_fqdn }}" present with same IP addresses again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host3_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host3_fqdn }}" present with differnt IP addresses
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host3_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.111' }}"
+ - fe80::20c:29ff:fe02:a1b1
+ - "{{ ipv4_prefix + '.121' }}"
+ - fe80::20c:29ff:fe02:a1b2
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host3_fqdn }}" present with different IP addresses again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host3_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.111' }}"
+ - fe80::20c:29ff:fe02:a1b1
+ - "{{ ipv4_prefix + '.121' }}"
+ - fe80::20c:29ff:fe02:a1b2
+ register: result
+ failed_when: result.changed
+
+ - name: Host "{{ host3_fqdn }}" present with old IP addresses
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host3_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ register: result
+ failed_when: not result.changed
+
+ - name: Host "{{ host3_fqdn }}" present with old IP addresses again
+ ipahost:
+ ipaadmin_password: MyPassword123
+ hosts:
+ - name: "{{ host3_fqdn }}"
+ ip_address:
+ - "{{ ipv4_prefix + '.211' }}"
+ - fe80::20c:29ff:fe02:a1b3
+ - "{{ ipv4_prefix + '.221' }}"
+ - fe80::20c:29ff:fe02:a1b4
+ register: result
+ failed_when: result.changed
+
+ - name: Host absent
+ ipahost:
+ ipaadmin_password: MyPassword123
+ name:
+ - "{{ host1_fqdn }}"
+ - "{{ host2_fqdn }}"
+ - "{{ host3_fqdn }}"
+ update_dns: yes
+ state: absent
From 8f32cb04c1e161e1e3217f10413685a2cc9bf492 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 13 Feb 2020 14:10:38 +0100
Subject: [PATCH] tests/host/test_host: Fix use of wrong host in the host5 test
host1 was used instead of host5 in the repeated host5 test. This lead to an
error with the new IP address handling in ipahost. It was correctly
reporting a change for host1 which resulted in a failed test.
---
tests/host/test_host.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/host/test_host.yml b/tests/host/test_host.yml
index 1a555a1..f3ec11d 100644
--- a/tests/host/test_host.yml
+++ b/tests/host/test_host.yml
@@ -129,7 +129,7 @@
- name: Host "{{ host5_fqdn }}" present again
ipahost:
ipaadmin_password: MyPassword123
- name: "{{ host1_fqdn }}"
+ name: "{{ host5_fqdn }}"
ip_address: "{{ ipv4_prefix + '.205' }}"
update_dns: yes
reverse: no

View File

@ -5,28 +5,33 @@
Summary: Roles and playbooks to deploy FreeIPA servers, replicas and clients Summary: Roles and playbooks to deploy FreeIPA servers, replicas and clients
Name: ansible-freeipa Name: ansible-freeipa
Version: 0.1.8 Version: 0.1.12
Release: 3%{?dist} Release: 6%{?dist}
URL: https://github.com/freeipa/ansible-freeipa URL: https://github.com/freeipa/ansible-freeipa
License: GPLv3+ License: GPLv3+
Source: https://github.com/freeipa/ansible-freeipa/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz Source: https://github.com/freeipa/ansible-freeipa/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
Patch1: ansible-freeipa-0.1.8-ipahost-Fix-choices-of-auth_ind-parameter-allow-to-reset-parameter_rhbz#1783992.patch Patch1: ansible-freeipa-0.1.12-Fixes-service-disable-when-service-has-no-certificates-attached_rhbz#1836294.patch
Patch2: ansible-freeipa-0.1.8-ipauser-Allow-reset-of-userauthtype-do-not-depend-on-first-last-for-mod_rhbz#1784474.patch Patch2: ansible-freeipa-0.1.12-Add-suppport-for-changing-password-of-symmetric-vaults_rhbz#1839197.patch
Patch3: ansible-freeipa-0.1.8-ipahost-Enhanced-failure-msg-for-member-params-used-without-member-action_rhbz#1783948.patch Patch3: ansible-freeipa-0.1.12-Fix-forwardzone-issues_rhbz#1843826,1843828,1843829,1843830,1843831.patch
Patch4: ansible-freeipa-0.1.8-Add-missing-attributes-to-ipasudorule_rhbz#1788168,1788035,1788024.patch Patch4: ansible-freeipa-0.1.12-ipa-host-group-Fix-membermanager-unknow-user-issue_rhbz#1848426.patch
Patch5: ansible-freeipa-0.1.8-ipapwpolicy-Use-global_policy-if-name-is-not-set_rhbz#1797532.patch Patch5: ansible-freeipa-0.1.12-ipa-user,host-Fail-on-duplucate-names-in-the-users-and-hosts-lists_rhbz#1822683.patch
Patch6: ansible-freeipa-0.1.8-ipahbacrule-Fix-handing-of-members-with-action-hbacrule_rhbz#1787996.patch Patch6: ansible-freeipa-0.1.12-action_plugins-ipaclient_get_otp-Discovered-python-n_rhbz#1852714.patch
Patch7: ansible-freeipa-0.1.8-ansible_freeipa_module-Fix-comparison-of-bool-parameters-in-compare_args_ipa_rhbz#1784514.patch Patch7: ansible-freeipa-0.1.12-ipa-server-replica-Fix-pkcs12-info-regressions-intro_rhbz#1853284.patch
Patch8: ansible-freeipa-ipahost-Add-support-for-several-IP-addresses-and-also-to-change-them_rhbz#1783979,1783976.patch Patch8: ansible-freeipa-0.1.12-ipareplica-Fix-missing-parameters-for-several-module_hbz#1855299.patch
Patch9: ansible-freeipa-0.1.8-ipahost-Fail-on-action-member-for-new-hosts-fix-dnsrecord_add-reverse-flag_rhbz#1803026.patch Patch9: ansible-freeipa-0.1.12-Allow-multiple-dns-zones-to-be-absent_rhbz#1845058.patch
Patch10: ansible-freeipa-0.1.8-ipahost-Do-not-fail-on-missing-DNS-or-zone-when-no-IP-address-given_rhbz#1804838.patch Patch10: ansible-freeipa-0.1.12-Fixed-error-msgs-on-FreeIPABaseModule-subclasses_rhbz#1845051.patch
Patch11: ansible-freeipa-0.1.12-Fix-allow_retrieve_keytab_host-in-service-module_rhbz#1868020.patch
Patch12: ansible-freeipa-0.1.12-Modified-return-value-for-ipavault-module_rhbz#1867909.patch
Patch13: ansible-freeipa-0.1.12-Add-support-for-option-name_from_ip-in-ipadnszone-mo_rhbz#1845056.patch
Patch14: ansible-freeipa-0.1.12-Fixes-password-behavior-on-Vault-module_rhbz#1839200.patch
BuildArch: noarch BuildArch: noarch
#Requires: ansible #Requires: ansible
%description %description
ansible-freeipa provides Ansible roles and playbooks to install and uninstall ansible-freeipa provides Ansible roles and playbooks to install and uninstall
FreeIPA servers, replicas and clients also modules for management. FreeIPA servers, replicas and clients. Also modules for group, host, topology
and user management.
Note: The ansible playbooks and roles require a configured ansible environment Note: The ansible playbooks and roles require a configured ansible environment
where the ansible nodes are reachable and are properly set up to have an IP where the ansible nodes are reachable and are properly set up to have an IP
@ -38,6 +43,9 @@ Features
- Cluster deployments: Server, replicas and clients in one playbook - Cluster deployments: Server, replicas and clients in one playbook
- One-time-password (OTP) support for client installation - One-time-password (OTP) support for client installation
- Repair mode for clients - Repair mode for clients
- Modules for dns forwarder management
- Modules for dns record management
- Modules for dns zone management
- Modules for group management - Modules for group management
- Modules for hbacrule management - Modules for hbacrule management
- Modules for hbacsvc management - Modules for hbacsvc management
@ -45,11 +53,13 @@ Features
- Modules for host management - Modules for host management
- Modules for hostgroup management - Modules for hostgroup management
- Modules for pwpolicy management - Modules for pwpolicy management
- Modules for service management
- Modules for sudocmd management - Modules for sudocmd management
- Modules for sudocmdgroup management - Modules for sudocmdgroup management
- Modules for sudorule management - Modules for sudorule management
- Modules for topology management - Modules for topology management
- Modules for user management - Modules for user management
- Modules for vault management
Supported FreeIPA Versions Supported FreeIPA Versions
@ -69,23 +79,25 @@ Supported Distributions
Requirements Requirements
Controller Controller
- Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection) - Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection)
- /usr/bin/kinit is required on the controller if a one time password (OTP) /usr/bin/kinit is required on the controller if a one time password (OTP)
is used is used
- python3-gssapi is required on the controller if a one time password (OTP) - python3-gssapi is required on the controller if a one time password (OTP)
is used with keytab to install the client. is used with keytab to install the client.
Node Node
- Supported FreeIPA version (see above) - Supported FreeIPA version (see above)
- Supported distribution (needed for package installation only, see above) - Supported distribution (needed for package installation only, see above)
Limitations Limitations
External CA support is not supported or working. The currently needed two step External signed CA is now supported. But the currently needed two step process
process is an issue for the processing in the role. The configuration of the is an issue for the processing in a simple playbook.
server is partly done already and needs to be continued after the CSR has been Work is planned to have a new method to handle CSR for external signed CAs in
handled. This is for example breaking the deployment of a server with replicas a separate step before starting the server installation.
or clients in one playbook.
%prep %prep
%setup -q %setup -q
@ -100,6 +112,10 @@ or clients in one playbook.
%patch8 -p1 %patch8 -p1
%patch9 -p1 %patch9 -p1
%patch10 -p1 %patch10 -p1
%patch11 -p1
%patch12 -p1
%patch13 -p1
%patch14 -p1
# Fix python modules and module utils: # Fix python modules and module utils:
# - Remove shebang # - Remove shebang
# - Remove execute flag # - Remove execute flag
@ -130,11 +146,84 @@ cp -rp plugins/* %{buildroot}%{_datadir}/ansible/plugins/
%{_datadir}/ansible/roles/ipaclient %{_datadir}/ansible/roles/ipaclient
%{_datadir}/ansible/plugins/module_utils %{_datadir}/ansible/plugins/module_utils
%{_datadir}/ansible/plugins/modules %{_datadir}/ansible/plugins/modules
%doc README.md %doc README*.md
%doc README-*.md
%doc playbooks %doc playbooks
%changelog %changelog
* Tue Aug 18 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-6
- Allow to manage multiple dnszone entries
Resolves: RHBZ#1845058
- Fixed error msgs on FreeIPABaseModule subclasses
Resolves: RHBZ#1845051
- Fix `allow_create_keytab_host` in service module
Resolves: RHBZ#1868020
- Modified return value for ipavault module
Resolves: RHBZ#1867909
- Add support for option `name_from_ip` in ipadnszone module
Resolves: RHBZ#1845056
- Fixe password behavior on Vault module
Resolves: RHBZ#1839200
* Tue Jul 14 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-5
- ipareplica: Fix failure while deploying KRA
Resolves: RHBZ#1855299
* Thu Jul 02 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-4
- ipa[server,replica]: Fix pkcs12 info regressions introduced with CA-less
Resolves: RHBZ#1853284
* Wed Jul 01 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-3
- action_plugins/ipaclient_get_otp: Discovered python needed in task_vars
Resolves: RHBZ#1852714
* Mon Jun 29 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-2
- Fixes service disable when service has no certificates attached
Resolves: RHBZ#1836294
- Add suppport for changing password of symmetric vaults
Resolves: RHBZ#1839197
- Fix forwardzone issues
Resolves: RHBZ#1843826
Resolves: RHBZ#1843828
Resolves: RHBZ#1843829
Resolves: RHBZ#1843830
Resolves: RHBZ#1843831
- ipa[host]group: Fix membermanager unknow user issue
Resolves: RHBZ#1848426
- ipa[user,host]: Fail on duplucate names in the users and hosts lists
Resolves: RHBZ#1822683
* Mon Jun 15 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.12-1
- Update to version 0.1.12 bug fix only release
Related: RHBZ#1818768
* Thu Jun 11 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.11-1
- Update to version 0.1.11
Related: RHBZ#1818768
* Mon Apr 27 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.10-1
- Update to version 0.1.10:
- ipaclient: Not delete keytab when ipaclient_on_master is true
- New module to manage dns forwarder zones in ipa
- Enhancements of sudorule module tests
- Gracefully handle RuntimeError raised during parameter validation in
fail_jso
- ipareplica_prepare: Fix module DOCUMENTATION
- ipa[server,replica,client]: setup_logging wrapper for
standard_logging_setup
- Created FreeIPABaseModule class to facilitate creation of new modules
- New IPADNSZone module
- Add admin password to the ipadnsconfig module tests
- Added alias module arguments in dnszone module
- Fixed a bug in AnsibleFreeIPAParams
- utils/build-galaxy-release: Do not add release tag to version for galaxy
- ipaserver docs: Calm down module linter
- galaxy.yml: Add system tag
- ipareplica_setup_kra: Remove unused ccache parameter
- ipareplica_setup_krb: krb is assigned to but never used
- utils/galaxy: Make galaxy scripts more generic
- galaxyfy-playbook.py: Fixed script name
Related: RHBZ#1818768
* Thu Feb 20 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.8-3 * Thu Feb 20 2020 Thomas Woerner <twoerner@redhat.com> - 0.1.8-3
- ipahost: Do not fail on missing DNS or zone when no IP address given - ipahost: Do not fail on missing DNS or zone when no IP address given
Resolves: RHBZ#1804838 Resolves: RHBZ#1804838