From fca774d0c728f6acf8463ef1d4273ad41a286d78 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 09 2023 10:16:27 +0000 Subject: import ansible-freeipa-1.9.2-2.el9_2 --- diff --git a/SOURCES/ansible-freeipa-1.9.2-ipaclient-Defer-creating-the-final-krb5.conf-on-clients_RHBZ#2189232.patch b/SOURCES/ansible-freeipa-1.9.2-ipaclient-Defer-creating-the-final-krb5.conf-on-clients_RHBZ#2189232.patch new file mode 100644 index 0000000..08aa73d --- /dev/null +++ b/SOURCES/ansible-freeipa-1.9.2-ipaclient-Defer-creating-the-final-krb5.conf-on-clients_RHBZ#2189232.patch @@ -0,0 +1,727 @@ +From 6b5acd9b0c8de965d9b815f8033a2bace9dd737d Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Wed, 22 Feb 2023 13:35:18 +0100 +Subject: [PATCH] ipaclient: Defer creating the final krb5.conf on clients + +A temporary krb5 configuration was used to join the domain in +ipaclient_join. After that the final krkb5 configuration was created +with enabled DNS discovery and used for the remainaing tasks, where also +a connection to the IPA API was done. + +With several servers the DNS discovery could have picked up a different +server. If the client deployment was faster than the replication this +could have lead to an unknown host error. + +The issue was seen in performance testing where many simultaneous client +enrollments have been done.. + +The goal is to keep server affinity as long as possible within the +deployment process: + +The temporary krb5.conf that was used before in ipaclient_join was +pulled out into an own module. The generated temporary krb5.conf is now +used in ipaclient_join and also ipaclient_api. + +The generation of the final krb5.conf is moved to the end of the +deployment process. + +Same as: https://pagure.io/freeipa/issue/9228 + +The setup of certmonger has been pulled out of ipaclient_setup_nss and moved +to the end of the process after generating the final krb5.conf as it will +use t will only use /etc/krb5.conf. + +Certificate issuance may fail during deployment due to using the final +krb5.conf, but certmonger will re-try the request in this case. + +Same as: https://pagure.io/freeipa/issue/9246 +--- + roles/ipaclient/library/ipaclient_api.py | 8 + + roles/ipaclient/library/ipaclient_join.py | 55 ++---- + .../library/ipaclient_setup_certmonger.py | 123 +++++++++++++ + .../ipaclient/library/ipaclient_setup_nss.py | 4 +- + .../ipaclient/library/ipaclient_temp_krb5.py | 163 ++++++++++++++++++ + .../library/ipaclient_test_keytab.py | 6 + + roles/ipaclient/tasks/install.yml | 69 ++++++-- + 7 files changed, 365 insertions(+), 63 deletions(-) + create mode 100644 roles/ipaclient/library/ipaclient_setup_certmonger.py + create mode 100644 roles/ipaclient/library/ipaclient_temp_krb5.py + +diff --git a/roles/ipaclient/library/ipaclient_api.py b/roles/ipaclient/library/ipaclient_api.py +index 7d4b829..9193f60 100644 +--- a/roles/ipaclient/library/ipaclient_api.py ++++ b/roles/ipaclient/library/ipaclient_api.py +@@ -55,6 +55,10 @@ options: + type: bool + required: no + default: no ++ krb_name: ++ description: The krb5 config file name ++ type: str ++ required: yes + author: + - Thomas Woerner (@t-woerner) + ''' +@@ -65,6 +69,7 @@ EXAMPLES = ''' + servers: ["server1.example.com","server2.example.com"] + domain: example.com + hostname: client1.example.com ++ krb_name: /tmp/tmpkrb5.conf + register: result_ipaclient_api + ''' + +@@ -99,6 +104,7 @@ def main(): + realm=dict(required=True, type='str'), + hostname=dict(required=True, type='str'), + debug=dict(required=False, type='bool', default="false"), ++ krb_name=dict(required=True, type='str'), + ), + supports_check_mode=False, + ) +@@ -110,9 +116,11 @@ def main(): + realm = module.params.get('realm') + hostname = module.params.get('hostname') + debug = module.params.get('debug') ++ krb_name = module.params.get('krb_name') + + host_principal = 'host/%s@%s' % (hostname, realm) + os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE ++ os.environ['KRB5_CONFIG'] = krb_name + + ca_certs = x509.load_certificate_list_from_file(paths.IPA_CA_CRT) + if 40500 <= NUM_VERSION < 40590: +diff --git a/roles/ipaclient/library/ipaclient_join.py b/roles/ipaclient/library/ipaclient_join.py +index 5d41a54..68379ea 100644 +--- a/roles/ipaclient/library/ipaclient_join.py ++++ b/roles/ipaclient/library/ipaclient_join.py +@@ -46,10 +46,6 @@ options: + type: list + elements: str + required: yes +- domain: +- description: Primary DNS domain of the IPA deployment +- type: str +- required: yes + realm: + description: Kerberos realm name of the IPA deployment + type: str +@@ -58,10 +54,6 @@ options: + description: Fully qualified name of this host + type: str + required: yes +- kdc: +- description: The name or address of the host running the KDC +- type: str +- required: yes + basedn: + description: The basedn of the IPA server (of the form dc=example,dc=com) + type: str +@@ -102,6 +94,10 @@ options: + description: Turn on extra debugging + type: bool + required: no ++ krb_name: ++ description: The krb5 config file name ++ type: str ++ required: yes + author: + - Thomas Woerner (@t-woerner) + ''' +@@ -111,27 +107,25 @@ EXAMPLES = ''' + - name: Join IPA in force mode with maximum 5 kinit attempts + ipaclient_join: + servers: ["server1.example.com","server2.example.com"] +- domain: example.com + realm: EXAMPLE.COM +- kdc: server1.example.com + basedn: dc=example,dc=com + hostname: client1.example.com + principal: admin + password: MySecretPassword + force_join: yes + kinit_attempts: 5 ++ krb_name: /tmp/tmpkrb5.conf + + # Join IPA to get the keytab using ipadiscovery return values + - name: Join IPA + ipaclient_join: + servers: "{{ ipadiscovery.servers }}" +- domain: "{{ ipadiscovery.domain }}" + realm: "{{ ipadiscovery.realm }}" +- kdc: "{{ ipadiscovery.kdc }}" + basedn: "{{ ipadiscovery.basedn }}" + hostname: "{{ ipadiscovery.hostname }}" + principal: admin + password: MySecretPassword ++ krb_name: /tmp/tmpkrb5.conf + ''' + + RETURN = ''' +@@ -147,9 +141,9 @@ import tempfile + from ansible.module_utils.basic import AnsibleModule + from ansible.module_utils.ansible_ipa_client import ( + setup_logging, check_imports, +- SECURE_PATH, sysrestore, paths, options, configure_krb5_conf, +- realm_to_suffix, kinit_keytab, GSSError, kinit_password, NUM_VERSION, +- get_ca_cert, get_ca_certs, errors, run ++ SECURE_PATH, sysrestore, paths, options, realm_to_suffix, kinit_keytab, ++ GSSError, kinit_password, NUM_VERSION, get_ca_cert, get_ca_certs, errors, ++ run + ) + + +@@ -157,10 +151,8 @@ def main(): + module = AnsibleModule( + argument_spec=dict( + servers=dict(required=True, type='list', elements='str'), +- domain=dict(required=True, type='str'), + realm=dict(required=True, type='str'), + hostname=dict(required=True, type='str'), +- kdc=dict(required=True, type='str'), + basedn=dict(required=True, type='str'), + principal=dict(required=False, type='str'), + password=dict(required=False, type='str', no_log=True), +@@ -170,6 +162,7 @@ def main(): + force_join=dict(required=False, type='bool'), + kinit_attempts=dict(required=False, type='int', default=5), + debug=dict(required=False, type='bool'), ++ krb_name=dict(required=True, type='str'), + ), + supports_check_mode=False, + ) +@@ -179,11 +172,9 @@ def main(): + setup_logging() + + servers = module.params.get('servers') +- domain = module.params.get('domain') + realm = module.params.get('realm') + hostname = module.params.get('hostname') + basedn = module.params.get('basedn') +- kdc = module.params.get('kdc') + force_join = module.params.get('force_join') + principal = module.params.get('principal') + password = module.params.get('password') +@@ -192,6 +183,7 @@ def main(): + ca_cert_file = module.params.get('ca_cert_file') + kinit_attempts = module.params.get('kinit_attempts') + debug = module.params.get('debug') ++ krb_name = module.params.get('krb_name') + + if password is not None and keytab is not None: + module.fail_json(msg="Password and keytab cannot be used together") +@@ -199,12 +191,10 @@ def main(): + if password is None and admin_keytab is None: + module.fail_json(msg="Password or admin_keytab is needed") + +- client_domain = hostname[hostname.find(".") + 1:] + nolog = tuple() + env = {'PATH': SECURE_PATH} + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) + host_principal = 'host/%s@%s' % (hostname, realm) +- sssd = True + + options.ca_cert_file = ca_cert_file + options.principal = principal +@@ -215,19 +205,6 @@ def main(): + changed = False + already_joined = False + try: +- (krb_fd, krb_name) = tempfile.mkstemp() +- os.close(krb_fd) +- configure_krb5_conf( +- cli_realm=realm, +- cli_domain=domain, +- cli_server=servers, +- cli_kdc=kdc, +- dnsok=False, +- filename=krb_name, +- client_domain=client_domain, +- client_hostname=hostname, +- configure_sssd=sssd, +- force=False) + env['KRB5_CONFIG'] = krb_name + ccache_dir = tempfile.mkdtemp(prefix='krbcc') + ccache_name = os.path.join(ccache_dir, 'ccache') +@@ -336,27 +313,17 @@ def main(): + paths.IPA_DNS_CCACHE, + config=krb_name, + attempts=kinit_attempts) +- env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE + except GSSError as e: + # failure to get ticket makes it impossible to login and + # bind from sssd to LDAP, abort installation + module.fail_json(msg="Failed to obtain host TGT: %s" % e) + + finally: +- try: +- os.remove(krb_name) +- except OSError: +- module.fail_json(msg="Could not remove %s" % krb_name) + if ccache_dir is not None: + try: + os.rmdir(ccache_dir) + except OSError: + pass +- if os.path.exists(krb_name + ".ipabkp"): +- try: +- os.remove(krb_name + ".ipabkp") +- except OSError: +- module.fail_json(msg="Could not remove %s.ipabkp" % krb_name) + + module.exit_json(changed=changed, + already_joined=already_joined) +diff --git a/roles/ipaclient/library/ipaclient_setup_certmonger.py b/roles/ipaclient/library/ipaclient_setup_certmonger.py +new file mode 100644 +index 0000000..5c81b40 +--- /dev/null ++++ b/roles/ipaclient/library/ipaclient_setup_certmonger.py +@@ -0,0 +1,123 @@ ++# -*- coding: utf-8 -*- ++ ++# Authors: ++# Thomas Woerner ++# ++# Based on ipa-client-install code ++# ++# Copyright (C) 2017-2022 Red Hat ++# see file 'COPYING' for use and warranty information ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++from __future__ import (absolute_import, division, print_function) ++ ++__metaclass__ = type ++ ++ANSIBLE_METADATA = { ++ 'metadata_version': '1.0', ++ 'supported_by': 'community', ++ 'status': ['preview'], ++} ++ ++DOCUMENTATION = ''' ++--- ++module: ipaclient_setup_certmonger ++short_description: Setup certmonger for IPA client ++description: Setup certmonger for IPA client ++options: ++ realm: ++ description: Kerberos realm name of the IPA deployment ++ type: str ++ required: yes ++ hostname: ++ description: Fully qualified name of this host ++ type: str ++ required: yes ++ subject_base: ++ description: | ++ The certificate subject base (default O=). ++ RDNs are in LDAP order (most specific RDN first). ++ type: str ++ required: yes ++ ca_enabled: ++ description: Whether the Certificate Authority is enabled or not ++ type: bool ++ required: yes ++ request_cert: ++ description: Request certificate for the machine ++ type: bool ++ required: yes ++author: ++ - Thomas Woerner (@t-woerner) ++''' ++ ++EXAMPLES = ''' ++- name: Setup certmonger for IPA client ++ ipaclient_setup_certmonger: ++ realm: EXAMPLE.COM ++ hostname: client1.example.com ++ subject_base: O=EXAMPLE.COM ++ ca_enabled: true ++ request_cert: false ++''' ++ ++RETURN = ''' ++''' ++ ++from ansible.module_utils.basic import AnsibleModule ++from ansible.module_utils.ansible_ipa_client import ( ++ setup_logging, check_imports, ++ options, sysrestore, paths, ScriptError, configure_certmonger ++) ++ ++ ++def main(): ++ module = AnsibleModule( ++ argument_spec=dict( ++ realm=dict(required=True, type='str'), ++ hostname=dict(required=True, type='str'), ++ subject_base=dict(required=True, type='str'), ++ ca_enabled=dict(required=True, type='bool'), ++ request_cert=dict(required=True, type='bool'), ++ ), ++ supports_check_mode=False, ++ ) ++ ++ module._ansible_debug = True ++ check_imports(module) ++ setup_logging() ++ ++ cli_realm = module.params.get('realm') ++ hostname = module.params.get('hostname') ++ subject_base = module.params.get('subject_base') ++ ca_enabled = module.params.get('ca_enabled') ++ ++ fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) ++ ++ options.request_cert = module.params.get('request_cert') ++ options.hostname = hostname ++ ++ try: ++ configure_certmonger(fstore, subject_base, cli_realm, hostname, ++ options, ca_enabled) ++ ++ except ScriptError as e: ++ module.fail_json(msg=str(e)) ++ ++ module.exit_json(changed=True) ++ ++ ++if __name__ == '__main__': ++ main() +diff --git a/roles/ipaclient/library/ipaclient_setup_nss.py b/roles/ipaclient/library/ipaclient_setup_nss.py +index 3dc0dcc..240bc76 100644 +--- a/roles/ipaclient/library/ipaclient_setup_nss.py ++++ b/roles/ipaclient/library/ipaclient_setup_nss.py +@@ -177,7 +177,7 @@ from ansible.module_utils.ansible_ipa_client import ( + options, sysrestore, paths, ansible_module_get_parsed_ip_addresses, + api, errors, create_ipa_nssdb, ipautil, ScriptError, CLIENT_INSTALL_ERROR, + get_certs_from_ldap, DN, certstore, x509, logger, certdb, +- CalledProcessError, tasks, client_dns, configure_certmonger, services, ++ CalledProcessError, tasks, client_dns, services, + update_ssh_keys, save_state, configure_ldap_conf, configure_nslcd_conf, + configure_openldap_conf, hardcode_ldap_server, getargspec, NUM_VERSION, + serialization +@@ -350,8 +350,6 @@ def main(): + + if not options.on_master: + client_dns(cli_server[0], hostname, options) +- configure_certmonger(fstore, subject_base, cli_realm, hostname, +- options, ca_enabled) + + if hasattr(paths, "SSH_CONFIG_DIR"): + ssh_config_dir = paths.SSH_CONFIG_DIR +diff --git a/roles/ipaclient/library/ipaclient_temp_krb5.py b/roles/ipaclient/library/ipaclient_temp_krb5.py +new file mode 100644 +index 0000000..cbe652c +--- /dev/null ++++ b/roles/ipaclient/library/ipaclient_temp_krb5.py +@@ -0,0 +1,163 @@ ++# -*- coding: utf-8 -*- ++ ++# Authors: ++# Thomas Woerner ++# ++# Based on ipa-client-install code ++# ++# Copyright (C) 2017-2022 Red Hat ++# see file 'COPYING' for use and warranty information ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++from __future__ import (absolute_import, division, print_function) ++ ++__metaclass__ = type ++ ++ANSIBLE_METADATA = { ++ 'metadata_version': '1.0', ++ 'supported_by': 'community', ++ 'status': ['preview'], ++} ++ ++DOCUMENTATION = ''' ++--- ++module: ipaclient_temp_krb5 ++short_description: ++ Create temporary krb5 configuration. ++description: ++ Create temporary krb5 configuration for deferring the creation of the final ++ krb5.conf on clients ++options: ++ servers: ++ description: Fully qualified name of IPA servers to enroll to ++ type: list ++ elements: str ++ required: yes ++ domain: ++ description: Primary DNS domain of the IPA deployment ++ type: str ++ required: yes ++ realm: ++ description: Kerberos realm name of the IPA deployment ++ type: str ++ required: yes ++ hostname: ++ description: Fully qualified name of this host ++ type: str ++ required: yes ++ kdc: ++ description: The name or address of the host running the KDC ++ type: str ++ required: yes ++ on_master: ++ description: Whether the configuration is done on the master or not ++ type: bool ++ required: no ++ default: no ++author: ++ - Thomas Woerner (@t-woerner) ++''' ++ ++EXAMPLES = ''' ++# Test IPA with local keytab ++- name: Test IPA in force mode with maximum 5 kinit attempts ++ ipaclient_test_keytab: ++ servers: ["server1.example.com","server2.example.com"] ++ domain: example.com ++ realm: EXAMPLE.COM ++ kdc: server1.example.com ++ hostname: client1.example.com ++ ++# Test IPA with ipadiscovery return values ++- name: Join IPA ++ ipaclient_test_keytab: ++ servers: "{{ ipadiscovery.servers }}" ++ domain: "{{ ipadiscovery.domain }}" ++ realm: "{{ ipadiscovery.realm }}" ++ kdc: "{{ ipadiscovery.kdc }}" ++ hostname: "{{ ipadiscovery.hostname }}" ++''' ++ ++RETURN = ''' ++krb_name: ++ description: The krb5 config file name ++ returned: always ++ type: str ++''' ++ ++import os ++import tempfile ++ ++from ansible.module_utils.basic import AnsibleModule ++from ansible.module_utils.ansible_ipa_client import ( ++ setup_logging, check_imports, configure_krb5_conf ++) ++ ++ ++def main(): ++ module = AnsibleModule( ++ argument_spec=dict( ++ servers=dict(required=True, type='list', elements='str'), ++ domain=dict(required=True, type='str'), ++ realm=dict(required=True, type='str'), ++ hostname=dict(required=True, type='str'), ++ kdc=dict(required=True, type='str'), ++ on_master=dict(required=False, type='bool', default=False), ++ ), ++ supports_check_mode=False, ++ ) ++ ++ module._ansible_debug = True ++ check_imports(module) ++ setup_logging() ++ ++ servers = module.params.get('servers') ++ domain = module.params.get('domain') ++ realm = module.params.get('realm') ++ hostname = module.params.get('hostname') ++ kdc = module.params.get('kdc') ++ client_domain = hostname[hostname.find(".") + 1:] ++ ++ krb_name = None ++ # Create temporary krb5 configuration ++ try: ++ (krb_fd, krb_name) = tempfile.mkstemp() ++ os.close(krb_fd) ++ configure_krb5_conf( ++ cli_realm=realm, ++ cli_domain=domain, ++ cli_server=servers, ++ cli_kdc=kdc, ++ dnsok=False, ++ filename=krb_name, ++ client_domain=client_domain, ++ client_hostname=hostname, ++ configure_sssd=True, ++ force=False) ++ except Exception as ex: ++ if krb_name: ++ try: ++ os.remove(krb_name) ++ except OSError: ++ module.fail_json(msg="Could not remove %s" % krb_name) ++ module.fail_json( ++ msg="Failed to create temporary krb5 configuration: %s" % str(ex)) ++ ++ module.exit_json(changed=False, ++ krb_name=krb_name) ++ ++ ++if __name__ == '__main__': ++ main() +diff --git a/roles/ipaclient/library/ipaclient_test_keytab.py b/roles/ipaclient/library/ipaclient_test_keytab.py +index 3f1c69d..3bebeea 100644 +--- a/roles/ipaclient/library/ipaclient_test_keytab.py ++++ b/roles/ipaclient/library/ipaclient_test_keytab.py +@@ -244,6 +244,12 @@ def main(): + os.remove(krb_name) + except OSError: + module.fail_json(msg="Could not remove %s" % krb_name) ++ if os.path.exists(krb_name + ".ipabkp"): ++ try: ++ os.remove(krb_name + ".ipabkp") ++ except OSError: ++ module.fail_json( ++ msg="Could not remove %s.ipabkp" % krb_name) + + module.exit_json(changed=False, + krb5_keytab_ok=krb5_keytab_ok, +diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml +index fa33f89..1b889d0 100644 +--- a/roles/ipaclient/tasks/install.yml ++++ b/roles/ipaclient/tasks/install.yml +@@ -239,12 +239,19 @@ + hostname: "{{ result_ipaclient_test.hostname }}" + when: not ipaclient_on_master | bool + +- - name: Install - Join IPA +- ipaclient_join: ++ - name: Install - Create temporary krb5 configuration ++ ipaclient_temp_krb5: + servers: "{{ result_ipaclient_test.servers }}" + domain: "{{ result_ipaclient_test.domain }}" + realm: "{{ result_ipaclient_test.realm }}" ++ hostname: "{{ result_ipaclient_test.hostname }}" + kdc: "{{ result_ipaclient_test.kdc }}" ++ register: result_ipaclient_temp_krb5 ++ ++ - name: Install - Join IPA ++ ipaclient_join: ++ servers: "{{ result_ipaclient_test.servers }}" ++ realm: "{{ result_ipaclient_test.realm }}" + basedn: "{{ result_ipaclient_test.basedn }}" + hostname: "{{ result_ipaclient_test.hostname }}" + force_join: "{{ ipaclient_force_join | default(omit) }}" +@@ -255,6 +262,7 @@ + admin_keytab: "{{ ipaadmin_keytab if ipaadmin_keytab is defined and not ipaclient_use_otp | bool else omit }}" + # ca_cert_file: "{{ ipaclient_ca_cert_file | default(omit) }}" + kinit_attempts: "{{ ipaclient_kinit_attempts | default(omit) }}" ++ krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}" + register: result_ipaclient_join + when: not ipaclient_on_master | bool and + (not result_ipaclient_test_keytab.krb5_keytab_ok or +@@ -323,26 +331,13 @@ + "{{ ipassd_no_krb5_offline_passwords + | default(ipasssd_no_krb5_offline_passwords) }}" + +- - name: Install - Configure krb5 for IPA realm +- ipaclient_setup_krb5: +- realm: "{{ result_ipaclient_test.realm }}" +- domain: "{{ result_ipaclient_test.domain }}" +- servers: "{{ result_ipaclient_test.servers }}" +- kdc: "{{ result_ipaclient_test.kdc }}" +- dnsok: "{{ result_ipaclient_test.dnsok }}" +- client_domain: "{{ result_ipaclient_test.client_domain }}" +- hostname: "{{ result_ipaclient_test.hostname }}" +- sssd: "{{ result_ipaclient_test.sssd }}" +- force: "{{ ipaclient_force }}" +- # on_master: "{{ ipaclient_on_master }}" +- when: not ipaclient_on_master | bool +- + - name: Install - IPA API calls for remaining enrollment parts + ipaclient_api: + servers: "{{ result_ipaclient_test.servers }}" + realm: "{{ result_ipaclient_test.realm }}" + hostname: "{{ result_ipaclient_test.hostname }}" + # debug: yes ++ krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}" + register: result_ipaclient_api + + - name: Install - Fix IPA ca +@@ -412,6 +407,36 @@ + domain: "{{ result_ipaclient_test.domain }}" + nisdomain: "{{ ipaclient_nisdomain | default(omit) }}" + when: not ipaclient_no_nisdomain | bool ++ ++ - name: Remove temporary krb5.conf ++ ansible.builtin.file: ++ path: "{{ result_ipaclient_temp_krb5.krb_name }}" ++ state: absent ++ when: result_ipaclient_temp_krb5.krb_name is defined ++ ++ - name: Install - Configure krb5 for IPA realm ++ ipaclient_setup_krb5: ++ realm: "{{ result_ipaclient_test.realm }}" ++ domain: "{{ result_ipaclient_test.domain }}" ++ servers: "{{ result_ipaclient_test.servers }}" ++ kdc: "{{ result_ipaclient_test.kdc }}" ++ dnsok: "{{ result_ipaclient_test.dnsok }}" ++ client_domain: "{{ result_ipaclient_test.client_domain }}" ++ hostname: "{{ result_ipaclient_test.hostname }}" ++ sssd: "{{ result_ipaclient_test.sssd }}" ++ force: "{{ ipaclient_force }}" ++ # on_master: "{{ ipaclient_on_master }}" ++ when: not ipaclient_on_master | bool ++ ++ - name: Install - Configure certmonger ++ ipaclient_setup_certmonger: ++ realm: "{{ result_ipaclient_test.realm }}" ++ hostname: "{{ result_ipaclient_test.hostname }}" ++ subject_base: "{{ result_ipaclient_api.subject_base }}" ++ ca_enabled: "{{ result_ipaclient_api.ca_enabled }}" ++ request_cert: "{{ ipaclient_request_cert }}" ++ when: not ipaclient_on_master | bool ++ + always: + - name: Install - Restore original admin password if overwritten by OTP + no_log: yes +@@ -423,3 +448,15 @@ + ansible.builtin.file: + path: "/etc/ipa/.dns_ccache" + state: absent ++ ++ - name: Remove temporary krb5.conf ++ ansible.builtin.file: ++ path: "{{ result_ipaclient_temp_krb5.krb_name }}" ++ state: absent ++ when: result_ipaclient_temp_krb5.krb_name is defined ++ ++ - name: Remove temporary krb5.conf backup ++ ansible.builtin.file: ++ path: "{{ result_ipaclient_temp_krb5.krb_name }}.ipabkp" ++ state: absent ++ when: result_ipaclient_temp_krb5.krb_name is defined +-- +2.39.2 + diff --git a/SOURCES/ansible-freeipa-1.9.2-ipaclient-Defer-krb5-configuration-fix_RHBZ#2189232.patch b/SOURCES/ansible-freeipa-1.9.2-ipaclient-Defer-krb5-configuration-fix_RHBZ#2189232.patch new file mode 100644 index 0000000..dda52d1 --- /dev/null +++ b/SOURCES/ansible-freeipa-1.9.2-ipaclient-Defer-krb5-configuration-fix_RHBZ#2189232.patch @@ -0,0 +1,152 @@ +From 0ec89eb53cf8771b34528ec210b2614370d9b662 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Thu, 23 Mar 2023 18:13:08 +0100 +Subject: [PATCH] ipaclient: ipaclient_setup_nss also needs krb_name parameter + +With the fix to defer creating the final krb5.conf on clients a bug has +been introduced with ipaclient_setup_nss: The krb_name parameter that +points to the temporary krb5 configuration was not added to the module. + +With a properly configured DNS (like for example IPA DNS) the krb TXT +records have been present in the DNS configuration. These have been used +automatically as a fallback and broke server affinity for the client. +Without the TXT records creating the IPA NSS database failed with + "Cannot find KDC for realm ..". + +The krb_name parameter has been added to ipaclient_setup_nss and is also +properly set in tasks/install.yml. +--- + roles/ipaclient/library/ipaclient_setup_nss.py | 8 ++++++++ + roles/ipaclient/tasks/install.yml | 1 + + 2 files changed, 9 insertions(+) + +diff --git a/roles/ipaclient/library/ipaclient_setup_nss.py b/roles/ipaclient/library/ipaclient_setup_nss.py +index 74ca9d4..0e8c658 100644 +--- a/roles/ipaclient/library/ipaclient_setup_nss.py ++++ b/roles/ipaclient/library/ipaclient_setup_nss.py +@@ -152,6 +152,10 @@ options: + The dist of nss_ldap or nss-pam-ldapd files if sssd is disabled + required: yes + type: dict ++ krb_name: ++ description: The krb5 config file name ++ type: str ++ required: yes + author: + - Thomas Woerner (@t-woerner) + ''' +@@ -167,6 +171,7 @@ EXAMPLES = ''' + subject_base: O=EXAMPLE.COM + principal: admin + ca_enabled: yes ++ krb_name: /tmp/tmpkrb5.conf + ''' + + RETURN = ''' +@@ -218,6 +223,7 @@ def main(): + no_krb5_offline_passwords=dict(required=False, type='bool'), + no_dns_sshfp=dict(required=False, type='bool', default=False), + nosssd_files=dict(required=True, type='dict'), ++ krb_name=dict(required=True, type='str'), + ), + supports_check_mode=False, + ) +@@ -268,6 +274,8 @@ def main(): + options.sssd = not options.no_sssd + options.no_ac = False + nosssd_files = module.params.get('nosssd_files') ++ krb_name = module.params.get('krb_name') ++ os.environ['KRB5_CONFIG'] = krb_name + + # pylint: disable=invalid-name + CCACHE_FILE = paths.IPA_DNS_CCACHE +diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml +index 662f09a..1dc6fdf 100644 +--- a/roles/ipaclient/tasks/install.yml ++++ b/roles/ipaclient/tasks/install.yml +@@ -382,6 +382,7 @@ + | default(ipasssd_no_krb5_offline_passwords) }}" + no_dns_sshfp: "{{ ipaclient_no_dns_sshfp }}" + nosssd_files: "{{ result_ipaclient_test.nosssd_files }}" ++ krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}" + + - name: Install - Configure SSH and SSHD + ipaclient_setup_ssh: +-- +2.39.2 + +From 10d072a8c42e6aa91485661d02b31f79bcc89fc0 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Fri, 24 Mar 2023 12:40:32 +0100 +Subject: [PATCH] ipaclient: ipaclient_fix_ca also needs krb_name parameter + +With the fix to defer creating the final krb5.conf on clients a bug has +been introduced with ipaclient_fix_ca: The krb_name parameter that +points to the temporary krb5 configuration was not added to the module + +Without this the server affinity is broken for allow_repair and additionally +ipaclient_fix_ca could fail if krb5 configuration needs to be repraied +and also CA needs to be fixed. + +The krb_name parameter has been added to ipaclient_fix_ca and is also +properly set in tasks/install.yml. +--- + roles/ipaclient/library/ipaclient_fix_ca.py | 8 ++++++++ + roles/ipaclient/tasks/install.yml | 1 + + 2 files changed, 9 insertions(+) + +diff --git a/roles/ipaclient/library/ipaclient_fix_ca.py b/roles/ipaclient/library/ipaclient_fix_ca.py +index 238b316..ede8d56 100644 +--- a/roles/ipaclient/library/ipaclient_fix_ca.py ++++ b/roles/ipaclient/library/ipaclient_fix_ca.py +@@ -54,6 +54,10 @@ options: + the host entry will not be changed on the server + type: bool + required: yes ++ krb_name: ++ description: The krb5 config file name ++ type: str ++ required: yes + author: + - Thomas Woerner (@t-woerner) + ''' +@@ -65,6 +69,7 @@ EXAMPLES = ''' + realm: EXAMPLE.COM + basedn: dc=example,dc=com + allow_repair: yes ++ krb_name: /tmp/tmpkrb5.conf + ''' + + RETURN = ''' +@@ -87,6 +92,7 @@ def main(): + realm=dict(required=True, type='str'), + basedn=dict(required=True, type='str'), + allow_repair=dict(required=True, type='bool'), ++ krb_name=dict(required=True, type='str'), + ), + ) + +@@ -98,6 +104,8 @@ def main(): + realm = module.params.get('realm') + basedn = module.params.get('basedn') + allow_repair = module.params.get('allow_repair') ++ krb_name = module.params.get('krb_name') ++ os.environ['KRB5_CONFIG'] = krb_name + + env = {'PATH': SECURE_PATH} + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) +diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml +index 1dc6fdf..7ff2c39 100644 +--- a/roles/ipaclient/tasks/install.yml ++++ b/roles/ipaclient/tasks/install.yml +@@ -346,6 +346,7 @@ + realm: "{{ result_ipaclient_test.realm }}" + basedn: "{{ result_ipaclient_test.basedn }}" + allow_repair: "{{ ipaclient_allow_repair }}" ++ krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}" + when: not ipaclient_on_master | bool and + result_ipaclient_test_keytab.krb5_keytab_ok and + not result_ipaclient_test_keytab.ca_crt_exists +-- +2.39.2 + diff --git a/SOURCES/ansible-freeipa-1.9.2-paclient-Fix-allow_repair-with-removed-krb5.conf-an_RHBZ#2189229.patch b/SOURCES/ansible-freeipa-1.9.2-paclient-Fix-allow_repair-with-removed-krb5.conf-an_RHBZ#2189229.patch new file mode 100644 index 0000000..1d4c186 --- /dev/null +++ b/SOURCES/ansible-freeipa-1.9.2-paclient-Fix-allow_repair-with-removed-krb5.conf-an_RHBZ#2189229.patch @@ -0,0 +1,74 @@ +From bfeefaf454e3e705e509ed13b2e650ddfd487fa2 Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Wed, 8 Feb 2023 13:38:12 +0100 +Subject: [PATCH] ipaclient: Fix allow_repair with removed krb5.conf and DNS + lookup + +The test in ipaclient_test_keytab is at first trying to use an existing +krb5.conf to test if the host keytab can be used. With working DNS lookup +an absent krb5.conf is not reported as an error as DNS lookup is +silently used instead. + +A temporary krb5.conf is now used in this test that forces to deactivate +DNS lookups and also to load /etc/krb5.conf. A missing krb5.conf is now +detected properly as the kinit call fails now properly. Thanks to Julien +Rische for this proposal. + +ipaclient_test_keytab is now properly returning the state of usable or +not usable krb5.conf in krb5_conf_ok. This fixes the handling of this +case later on in the role. +--- + .../library/ipaclient_test_keytab.py | 27 +++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/roles/ipaclient/library/ipaclient_test_keytab.py b/roles/ipaclient/library/ipaclient_test_keytab.py +index a86b237..3f1c69d 100644 +--- a/roles/ipaclient/library/ipaclient_test_keytab.py ++++ b/roles/ipaclient/library/ipaclient_test_keytab.py +@@ -159,11 +159,29 @@ def main(): + ca_crt_exists = os.path.exists(paths.IPA_CA_CRT) + env = {'PATH': SECURE_PATH, 'KRB5CCNAME': paths.IPA_DNS_CCACHE} + +- # First try: Validate krb5 keytab with system krb5 configuraiton ++ # First try: Validate with temporary test krb5.conf that forces ++ # 1) no DNS lookups and ++ # 2) to load /etc/krb5.conf: ++ # ++ # [libdefaults] ++ # dns_lookup_realm = false ++ # dns_lookup_kdc = false ++ # include /etc/krb5.conf ++ # + try: ++ (krb_fd, krb_name) = tempfile.mkstemp() ++ os.close(krb_fd) ++ content = "\n".join([ ++ "[libdefaults]", ++ "dns_lookup_realm = false", ++ "dns_lookup_kdc = false", ++ "include /etc/krb5.conf" ++ ]) ++ with open(krb_name, "w") as outf: ++ outf.write(content) + kinit_keytab(host_principal, paths.KRB5_KEYTAB, + paths.IPA_DNS_CCACHE, +- config=paths.KRB5_CONF, ++ config=krb_name, + attempts=kinit_attempts) + krb5_keytab_ok = True + krb5_conf_ok = True +@@ -177,6 +195,11 @@ def main(): + pass + except GSSError: + pass ++ finally: ++ try: ++ os.remove(krb_name) ++ except OSError: ++ module.fail_json(msg="Could not remove %s" % krb_name) + + # Second try: Validate krb5 keytab with temporary krb5 + # configuration +-- +2.39.2 + diff --git a/SPECS/ansible-freeipa.spec b/SPECS/ansible-freeipa.spec index 2fe9b9d..3685070 100644 --- a/SPECS/ansible-freeipa.spec +++ b/SPECS/ansible-freeipa.spec @@ -8,10 +8,13 @@ Summary: Roles and playbooks to deploy FreeIPA servers, replicas and clients Name: ansible-freeipa Version: 1.9.2 -Release: 1%{?dist} +Release: 2%{?dist} URL: https://github.com/freeipa/ansible-freeipa License: GPLv3+ Source: https://github.com/freeipa/ansible-freeipa/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz +Patch1: ansible-freeipa-1.9.2-paclient-Fix-allow_repair-with-removed-krb5.conf-an_RHBZ#2189229.patch +Patch2: ansible-freeipa-1.9.2-ipaclient-Defer-creating-the-final-krb5.conf-on-clients_RHBZ#2189232.patch +Patch3: ansible-freeipa-1.9.2-ipaclient-Defer-krb5-configuration-fix_RHBZ#2189232.patch BuildArch: noarch %if 0%{?fedora} >= 35 || 0%{?rhel} >= 9 Requires: ansible-core @@ -116,6 +119,9 @@ to get the needed requrements to run the tests. %prep %setup -q # Do not create backup files with patches +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 # Fix python modules and module utils: # - Remove shebang @@ -179,6 +185,12 @@ cp -rp tests %{buildroot}%{_datadir}/ansible-freeipa/ %{_datadir}/ansible-freeipa/requirements-tests.txt %changelog +* Mon Apr 24 2023 Thomas Woerner - 1.9.2-2 +- ipaclient: Fix allow_repair with removed krb5.conf and DNS lookup + Resolves: RHBZ#2189229 +- ipaclient: Defer creating the final krb5.conf on clients + Resolves: RHBZ#2189232 + * Tue Jan 31 2023 Thomas Woerner - 1.9.2-1 - Update to version 1.9.2 https://github.com/freeipa/ansible-freeipa/releases/tag/v1.9.2