From b3d5b32e31d4411c2f951b26da113b4653675fd7 Mon Sep 17 00:00:00 2001 From: Thomas Woerner <twoerner@redhat.com> Date: Mon, 11 May 2020 12:38:11 +0200 Subject: [PATCH] ipahost: Honour update_password also for random If random is enabled and update_password is limited to "create_only", the random password may only be changed if the host does not exist yet. Additionally the generation of the random password will fail, if the host is already enrolled if update_password is "always" (default value). An error will be reported early in this case now. The command host_show is now used instead of host_find, as `has_password` and `has_keytab` are only returned by host_show, but not by host_find. The find_host function has been adapated for this change. Resolves: #253 (ipahost is not idempotent) --- README-host.md | 12 ++++---- plugins/modules/ipahost.py | 51 +++++++++++++++++++++------------ tests/host/test_host_random.yml | 16 +++++++++++ 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/README-host.md b/README-host.md index 5f540cac..4ee4cbe4 100644 --- a/README-host.md +++ b/README-host.md @@ -173,14 +173,14 @@ Example playbook to ensure host presence with a random password: name: host01.example.com random: yes force: yes + update_password: on_create register: ipahost - name: Print generated random password debug: var: ipahost.host.randompassword ``` -Please remember that the `force` tag will also force the generation of a new random password even if the host already exists and if `update_password` is limited to `on_create`. - +Please remember that a new random password will be generated for an existing but not enrolled host if `update_password` is not limited to `on_create`. For an already enrolled host the task will fail with `update_password` default setting `always`. Example playbook to ensure presence of several hosts with a random password: @@ -198,9 +198,11 @@ Example playbook to ensure presence of several hosts with a random password: - name: host01.example.com random: yes force: yes + update_password: on_create - name: host02.example.com random: yes force: yes + update_password: on_create register: ipahost - name: Print generated random password for host01.example.com @@ -211,7 +213,7 @@ Example playbook to ensure presence of several hosts with a random password: debug: var: ipahost.host["host02.example.com"].randompassword ``` -Please remember that the `force` tag will also force the generation of a new random password even if the host alreay exists and if `update_password` is limited to `on_create`. +Please remember that a new random password will be generated for an existing but not enrolled host if `update_password` is not limited to `on_create`. For an already enrolled host the task will fail with `update_password` default setting `always`. Example playbook to ensure presence of host member principal: @@ -337,8 +339,8 @@ Variable | Description | Required `location` \| `ns_host_location` | Host location (e.g. "Lab 2"). | no `platform` \| `ns_hardware_platform` | Host hardware platform (e.g. "Lenovo T61"). | no `os` \| `ns_os_version` | Host operating system and version (e.g. "Fedora 9"). | no -`password` \| `user_password` \| `userpassword` | Password used in bulk enrollment. | no -`random` \| `random_password` | Initiate the generation of a random password to be used in bulk enrollment. | no +`password` \| `user_password` \| `userpassword` | Password used in bulk enrollment for absent or not enrolled hosts. | no +`random` \| `random_password` | Initiate the generation of a random password to be used in bulk enrollment for absent or not enrolled hosts. | no `certificate` \| `usercertificate` | List of base-64 encoded host certificates | no `managedby` \| `principalname` \| `krbprincipalname` | List of hosts that can manage this host | no `principal` \| `principalname` \| `krbprincipalname` | List of principal aliases for this host | no diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py index 42bd7e1f..4778386c 100644 --- a/plugins/modules/ipahost.py +++ b/plugins/modules/ipahost.py @@ -420,23 +420,22 @@ if six.PY3: def find_host(module, name): _args = { "all": True, - "fqdn": to_text(name), } - _result = api_command(module, "host_find", to_text(name), _args) + try: + _result = api_command(module, "host_show", to_text(name), _args) + except ipalib_errors.NotFound as e: + msg = str(e) + if "host not found" in msg: + return None + module.fail_json(msg="host_show failed: %s" % msg) - 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 + _res = _result["result"] + certs = _res.get("usercertificate") + if certs is not None: + _res["usercertificate"] = [encode_certificate(cert) for + cert in certs] + return _res def find_dnsrecord(module, name): @@ -903,9 +902,25 @@ def main(): # Found the host if res_find is not None: # Ignore password with update_password == on_create - if update_password == "on_create" and \ - "userpassword" in args: - del args["userpassword"] + if update_password == "on_create": + # Ignore userpassword and random for existing + # host if update_password is "on_create" + if "userpassword" in args: + del args["userpassword"] + if "random" in args: + del args["random"] + elif "userpassword" in args or "random" in args: + # Allow an existing OTP to be reset but don't + # allow a OTP or to be added to an enrolled host. + # Also do not allow to change the password for an + # enrolled host. + + if not res_find["has_password"] and \ + res_find["has_keytab"]: + ansible_module.fail_json( + msg="%s: Password cannot be set on " + "enrolled host." % host + ) # Ignore force, ip_address and no_reverse for mod for x in ["force", "ip_address", "no_reverse"]: @@ -953,7 +968,7 @@ def main(): principal_add, principal_del = gen_add_del_lists( principal, res_find.get("principal")) # Principals are not returned as utf8 for IPA using - # python2 using host_find, therefore we need to + # python2 using host_show, therefore we need to # convert the principals that we should remove. principal_del = [to_text(x) for x in principal_del] diff --git a/tests/host/test_host_random.yml b/tests/host/test_host_random.yml index 4d1b2545..376740c7 100644 --- a/tests/host/test_host_random.yml +++ b/tests/host/test_host_random.yml @@ -77,6 +77,22 @@ debug: var: ipahost.host["{{host2_fqdn }}"].randompassword + - name: Enrolled host "{{ groups.ipaserver[0] }}" fails to set random password with update_password always + ipahost: + ipaadmin_password: SomeADMINpassword + hosts: + - name: "{{ groups.ipaserver[0] }}" + random: yes + update_password: always + register: ipahost + failed_when: ipahost.changed + + - assert: + that: + - ipahost.host["{{ groups.ipaserver[0] }}"].randompassword is + not defined + - "'Password cannot be set on enrolled host' in ipahost.msg" + - name: Hosts "{{ host1_fqdn }}" and "{{ host2_fqdn }}" absent ipahost: ipaadmin_password: SomeADMINpassword -- GitLab