diff --git a/roles/ipareplica/README.md b/roles/ipareplica/README.md index 21371330..93f69b73 100644 --- a/roles/ipareplica/README.md +++ b/roles/ipareplica/README.md @@ -270,6 +270,11 @@ Variable | Description | Required `ipareplica_auto_forwarders` | Add DNS forwarders configured in /etc/resolv.conf to the list of forwarders used by IPA DNS. (bool, default: false) | no `ipareplica_forward_policy` | DNS forwarding policy for global forwarders specified using other options. (choice: first,only) | no `ipareplica_no_dnssec_validation` | Disable DNSSEC validation on this server. (bool, default: false) | no +`ipareplica_dot_forwarders` | List of DNS over TLS forwarders. Required if `ipareplica_dns_over_tls` is enabled. (list of strings) | no +`ipareplica_dns_over_tls` \| `ipaclient_dns_over_tls` | Configure DNS over TLS. Requires FreeIPA version 4.12.5 or later. (bool, default: false) | no +`ipareplica_dns_over_tls_cert` | Certificate to use for DNS over TLS. If empty, a new certificate will be requested from IPA CA. (string) | no +`ipareplica_dns_over_tls_key` | Key for certificate specified in `ipareplica_dns_over_tls_cert`. (string) | no +`ipareplica_dns_policy` | Encrypted DNS policy. Only usable if `ipareplica_dns_over_tls` is enabled. (choice: relaxed, enforced, default: relaxed) | no AD trust Variables ------------------ diff --git a/roles/ipareplica/library/ipareplica_prepare.py b/roles/ipareplica/library/ipareplica_prepare.py index 74965946..5edbba5d 100644 --- a/roles/ipareplica/library/ipareplica_prepare.py +++ b/roles/ipareplica/library/ipareplica_prepare.py @@ -224,6 +224,32 @@ options: type: bool default: no required: no + dot_forwarders: + description: List of DNS over TLS forwarders + type: list + elements: str + default: [] + required: no + dns_over_tls: + description: Configure DNS over TLS + type: bool + default: no + required: no + dns_over_tls_cert: + description: + Certificate to use for DNS over TLS. If empty, a new + certificate will be requested from IPA CA + type: str + required: no + dns_over_tls_key: + description: Key for certificate specified in dns_over_tls_cert + type: str + required: no + dns_policy: + description: Encrypted DNS policy + type: str + choices: ['relaxed', 'enforced'] + default: 'relaxed' enable_compat: description: Enable support for trusted domains for old clients type: bool @@ -354,6 +380,15 @@ def main(): choices=['first', 'only'], default=None), no_dnssec_validation=dict(required=False, type='bool', default=False), + dot_forwarders=dict(required=False, type='list', elements='str', + default=[]), + dns_over_tls=dict(required=False, type='bool', + default=False), + dns_over_tls_cert=dict(required=False, type='str'), + dns_over_tls_key=dict(required=False, type='str'), + dns_policy=dict(required=False, type='str', + choices=['relaxed', 'enforced'], + default='relaxed'), # ad trust enable_compat=dict(required=False, type='bool', default=False), netbios_name=dict(required=False, type='str'), @@ -430,6 +465,11 @@ def main(): options.forward_policy = ansible_module.params.get('forward_policy') options.no_dnssec_validation = ansible_module.params.get( 'no_dnssec_validation') + options.dot_forwarders = ansible_module.params.get('dot_forwarders') + options.dns_over_tls = ansible_module.params.get('dns_over_tls') + options.dns_over_tls_cert = ansible_module.params.get('dns_over_tls_cert') + options.dns_over_tls_key = ansible_module.params.get('dns_over_tls_key') + options.dns_policy = ansible_module.params.get('dns_policy') # ad trust options.enable_compat = ansible_module.params.get('enable_compat') options.netbios_name = ansible_module.params.get('netbios_name') diff --git a/roles/ipareplica/library/ipareplica_setup_dns.py b/roles/ipareplica/library/ipareplica_setup_dns.py index d8c643e5..ef8e93e9 100644 --- a/roles/ipareplica/library/ipareplica_setup_dns.py +++ b/roles/ipareplica/library/ipareplica_setup_dns.py @@ -72,6 +72,32 @@ options: type: bool default: no required: no + dot_forwarders: + description: List of DNS over TLS forwarders + type: list + elements: str + default: [] + required: no + dns_over_tls: + description: Configure DNS over TLS + type: bool + default: no + required: no + dns_over_tls_cert: + description: + Certificate to use for DNS over TLS. If empty, a new + certificate will be requested from IPA CA + type: str + required: no + dns_over_tls_key: + description: Key for certificate specified in dns_over_tls_cert + type: str + required: no + dns_policy: + description: Encrypted DNS policy + type: str + choices: ['relaxed', 'enforced'] + default: 'relaxed' dns_ip_addresses: description: The dns ip_addresses setting type: list @@ -117,6 +143,9 @@ from ansible.module_utils.ansible_ipa_replica import ( gen_ReplicaConfig, gen_remote_api, api, redirect_stdout, dns, ansible_module_get_parsed_ip_addresses ) +# pylint: disable=unused-import +from ansible.module_utils.ansible_ipa_replica import bindinstance # noqa: F401 +# pylint: enable=unused-import def main(): @@ -135,6 +164,14 @@ def main(): choices=['first', 'only'], default=None), no_dnssec_validation=dict(required=False, type='bool', default=False), + dot_forwarders=dict(required=False, type='list', elements='str', + default=[]), + dns_over_tls=dict(required=False, type='bool', default=False), + dns_over_tls_cert=dict(required=False, type='str'), + dns_over_tls_key=dict(required=False, type='str'), + dns_policy=dict(required=False, type='str', + choices=['relaxed', 'enforced'], + default='relaxed'), # additional dns_ip_addresses=dict(required=True, type='list', elements='str'), dns_reverse_zones=dict(required=True, type='list', elements='str'), @@ -167,6 +204,11 @@ def main(): options.forward_policy = ansible_module.params.get('forward_policy') options.no_dnssec_validation = ansible_module.params.get( 'no_dnssec_validation') + options.dot_forwarders = ansible_module.params.get('dot_forwarders') + options.dns_over_tls = ansible_module.params.get('dns_over_tls') + options.dns_over_tls_cert = ansible_module.params.get('dns_over_tls_cert') + options.dns_over_tls_key = ansible_module.params.get('dns_over_tls_key') + options.dns_policy = ansible_module.params.get('dns_policy') # additional dns.ip_addresses = ansible_module_get_parsed_ip_addresses( ansible_module, 'dns_ip_addresses') diff --git a/roles/ipareplica/library/ipareplica_test.py b/roles/ipareplica/library/ipareplica_test.py index 06ecad4e..94b41e58 100644 --- a/roles/ipareplica/library/ipareplica_test.py +++ b/roles/ipareplica/library/ipareplica_test.py @@ -181,6 +181,32 @@ options: type: bool default: no required: no + dot_forwarders: + description: List of DNS over TLS forwarders + type: list + elements: str + default: [] + required: no + dns_over_tls: + description: Configure DNS over TLS + type: bool + default: no + required: no + dns_over_tls_cert: + description: + Certificate to use for DNS over TLS. If empty, a new + certificate will be requested from IPA CA + type: str + required: no + dns_over_tls_key: + description: Key for certificate specified in dns_over_tls_cert + type: str + required: no + dns_policy: + description: Encrypted DNS policy + type: str + choices: ['relaxed', 'enforced'] + default: 'relaxed' author: - Thomas Woerner (@t-woerner) ''' @@ -199,7 +225,8 @@ from ansible.module_utils.ansible_ipa_replica import ( paths, sysrestore, ansible_module_get_parsed_ip_addresses, service, redirect_stdout, create_ipa_conf, ipautil, x509, validate_domain_name, common_check, - IPA_PYTHON_VERSION, getargspec, adtrustinstance, install_ca_cert + IPA_PYTHON_VERSION, getargspec, adtrustinstance, install_ca_cert, + services, CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION ) @@ -250,6 +277,14 @@ def main(): choices=['first', 'only'], default=None), no_dnssec_validation=dict(required=False, type='bool', default=False), + dot_forwarders=dict(required=False, type='list', elements='str', + default=[]), + dns_over_tls=dict(required=False, type='bool', default=False), + dns_over_tls_cert=dict(required=False, type='str'), + dns_over_tls_key=dict(required=False, type='str'), + dns_policy=dict(required=False, type='str', + choices=['relaxed', 'enforced'], + default='relaxed'), ), ) @@ -298,6 +333,11 @@ def main(): options.forward_policy = ansible_module.params.get('forward_policy') options.no_dnssec_validation = ansible_module.params.get( 'no_dnssec_validation') + options.dot_forwarders = ansible_module.params.get('dot_forwarders') + options.dns_over_tls = ansible_module.params.get('dns_over_tls') + options.dns_over_tls_cert = ansible_module.params.get('dns_over_tls_cert') + options.dns_over_tls_key = ansible_module.params.get('dns_over_tls_key') + options.dns_policy = ansible_module.params.get('dns_policy') ########################################################################## # replica init ########################################################### @@ -419,6 +459,14 @@ def main(): ansible_module.fail_json( msg="You cannot specify a --no-dnssec-validation option " "without the --setup-dns option") + if installer.dns_over_tls_cert: + ansible_module.fail_json( + msg="You cannot specify a --dns-over-tls-cert option " + "without the --setup-dns option") + if installer.dns_over_tls_key: + ansible_module.fail_json( + msg="You cannot specify a --dns-over-tls-key option " + "without the --setup-dns option") elif installer.forwarders and installer.no_forwarders: ansible_module.fail_json( msg="You cannot specify a --forwarder option together with " @@ -435,6 +483,31 @@ def main(): ansible_module.fail_json( msg="You cannot specify a --auto-reverse option together with " "--no-reverse") + elif installer.dot_forwarders and not installer.dns_over_tls: + ansible_module.fail_json( + msg="You cannot specify a --dot-forwarder option " + "without the --dns-over-tls option") + elif (installer.dns_over_tls + and not services.knownservices["unbound"].is_installed()): + ansible_module.fail_json( + msg="To enable DNS over TLS, package ipa-server-encrypted-dns " + "must be installed.") + elif installer.dns_policy == "enforced" and not installer.dns_over_tls: + ansible_module.fail_json( + msg="You cannot specify a --dns-policy option " + "without the --dns-over-tls option") + elif installer.dns_over_tls_cert and not installer.dns_over_tls: + ansible_module.fail_json( + msg="You cannot specify a --dns-over-tls-cert option " + "without the --dns-over-tls option") + elif installer.dns_over_tls_key and not installer.dns_over_tls: + ansible_module.fail_json( + msg="You cannot specify a --dns-over-tls-key option " + "without the --dns-over-tls option") + elif bool(installer.dns_over_tls_key) != bool(installer.dns_over_tls_cert): + ansible_module.fail_json( + msg="You cannot specify a --dns-over-tls-key option " + "without the --dns-over-tls-cert option and vice versa") # replica installers if installer.servers and not installer.domain_name: @@ -449,6 +522,10 @@ def main(): ansible_module.fail_json( msg="You must specify at least one of --forwarder, " "--auto-forwarders, or --no-forwarders options") + if installer.dns_over_tls and not installer.dot_forwarders: + ansible_module.fail_json( + msg="You must specify --dot-forwarder " + "when enabling DNS over TLS") if installer.dirsrv_config_file is not None and \ not os.path.exists(installer.dirsrv_config_file): @@ -486,6 +563,11 @@ def main(): if installer.domain_name is not None: validate_domain_name(installer.domain_name) + if installer.dns_over_tls and not CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION: + ansible_module.fail_json( + msg="Important patches for DNS over TLS are missing in your " + "IPA version.") + ########################################################################## # replica promote_check excerpts ######################################### ########################################################################## diff --git a/roles/ipareplica/module_utils/ansible_ipa_replica.py b/roles/ipareplica/module_utils/ansible_ipa_replica.py index 06aa71d4..6b5c4781 100644 --- a/roles/ipareplica/module_utils/ansible_ipa_replica.py +++ b/roles/ipareplica/module_utils/ansible_ipa_replica.py @@ -187,6 +187,14 @@ try: from ipaserver.install import ntpinstance time_service = "ntpd" # pylint: disable=invalid-name + try: + CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION = False + from ipaclient.install.client import ClientInstallInterface + except ImportError: + pass + else: + if hasattr(ClientInstallInterface, "no_dnssec_validation"): + CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION = True else: # IPA version < 4.6 raise RuntimeError("freeipa version '%s' is too old" % VERSION) @@ -337,12 +345,6 @@ options.add_agents = False options.subject_base = None options.ca_subject = None -# Hotfix for https://github.com/freeipa/freeipa/pull/7343 -options.dns_over_tls = False -options.dns_over_tls_key = None -options.dns_over_tls_cert = None -options.dot_forwarders = None -options.dns_policy = None # pylint: enable=attribute-defined-outside-init diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml index 4bee1c7b..76cffffd 100644 --- a/roles/ipareplica/tasks/install.yml +++ b/roles/ipareplica/tasks/install.yml @@ -1,33 +1,43 @@ --- # tasks file for ipareplica -- name: Package installation +- name: Install - Set ipareplica__dns_over_lts + ansible.builtin.set_fact: + ipareplica__dns_over_tls: "{{ ipareplica_dns_over_tls | default(ipaclient_dns_over_tls) | default(False) }}" + +- name: Install - Package installation when: ipareplica_install_packages | bool block: - - name: Install - Ensure IPA replica packages are installed - ansible.builtin.package: - name: "{{ ipareplica_packages }}" - state: present + - name: Install - Set packages for installation + ansible.builtin.set_fact: + _ipapackages: "{{ ipareplica_packages }}" - - name: Install - Ensure IPA replica packages for dns are installed - ansible.builtin.package: - name: "{{ ipareplica_packages_dns }}" - state: present + - name: Install - Set packages for installlation, add DNS + ansible.builtin.set_fact: + _ipapackages: "{{ _ipapackages + ipareplica_packages_dns }}" when: ipareplica_setup_dns | bool - - name: Install - Ensure IPA replica packages for adtrust are installed - ansible.builtin.package: - name: "{{ ipareplica_packages_adtrust }}" - state: present + - name: Install - Set packages for installlation, add DOT + ansible.builtin.set_fact: + _ipapackages: "{{ _ipapackages + ipareplica_packages_dot }}" + when: ipareplica__dns_over_tls | bool + + - name: Install - Set packages for installlation, add adtrust + ansible.builtin.set_fact: + _ipapackages: "{{ _ipapackages + ipareplica_packages_adtrust }}" when: ipareplica_setup_adtrust | bool - - name: Install - Ensure that firewall packages installed - ansible.builtin.package: - name: "{{ ipareplica_packages_firewalld }}" - state: present + - name: Install - Set packages for installlation, add firewalld + ansible.builtin.set_fact: + _ipapackages: "{{ _ipapackages + ipareplica_packages_firewalld }}" when: ipareplica_setup_firewalld | bool + - name: Install - Ensure that packages are installed + ansible.builtin.package: + name: "{{ _ipapackages }}" + state: present + - name: Firewall configuration when: ipareplica_setup_firewalld | bool block: @@ -104,6 +114,11 @@ auto_forwarders: "{{ ipareplica_auto_forwarders }}" forward_policy: "{{ ipareplica_forward_policy | default(omit) }}" no_dnssec_validation: "{{ ipareplica_no_dnssec_validation }}" + dot_forwarders: "{{ ipareplica_dot_forwarders | default([]) }}" + dns_over_tls: "{{ ipareplica__dns_over_tls }}" + dns_over_tls_cert: "{{ ipareplica_dns_over_tls_cert | default(omit) }}" + dns_over_tls_key: "{{ ipareplica_dns_over_tls_key | default(omit) }}" + dns_policy: "{{ ipareplica_dns_policy | default(omit) }}" register: result_ipareplica_test - name: Install - Deploy replica @@ -127,6 +142,8 @@ ipaclient_hostname: "{{ result_ipareplica_test.hostname }}" ipaclient_ip_addresses: "{{ ipareplica_ip_addresses | default(omit) }}" ipaclient_install_packages: "{{ ipareplica_install_packages }}" + ipaclient_dns_over_tls: "{{ ipareplica__dns_over_tls }}" + ipaclient_no_dnssec_validation: "{{ ipareplica_no_dnssec_validation }}" when: not result_ipareplica_test.client_enrolled - name: Install - Configure firewalld @@ -140,6 +157,8 @@ {{ "--add-service=freeipa-trust" if result_ipareplica_test.setup_adtrust else "" }} {{ "--add-service=dns" if ipareplica_setup_dns | bool else "" }} + {{ "--add-service=dns-over-tls" if ipareplica__dns_over_tls | bool + else "" }} {{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }} when: ipareplica_setup_firewalld | bool @@ -153,6 +172,8 @@ {{ "--add-service=freeipa-trust" if result_ipareplica_test.setup_adtrust else "" }} {{ "--add-service=dns" if ipareplica_setup_dns | bool else "" }} + {{ "--add-service=dns-over-tls" if ipareplica__dns_over_tls | bool + else "" }} {{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }} when: ipareplica_setup_firewalld | bool @@ -201,6 +222,11 @@ auto_forwarders: "{{ ipareplica_auto_forwarders }}" forward_policy: "{{ ipareplica_forward_policy | default(omit) }}" no_dnssec_validation: "{{ ipareplica_no_dnssec_validation }}" + dot_forwarders: "{{ ipareplica_dot_forwarders | default([]) }}" + dns_over_tls: "{{ ipareplica__dns_over_tls }}" + dns_over_tls_cert: "{{ ipareplica_dns_over_tls_cert | default(omit) }}" + dns_over_tls_key: "{{ ipareplica_dns_over_tls_key | default(omit) }}" + dns_policy: "{{ ipareplica_dns_policy | default(omit) }}" ### ad trust ### enable_compat: "{{ ipareplica_enable_compat }}" netbios_name: "{{ ipareplica_netbios_name | default(omit) }}" @@ -717,6 +743,11 @@ result_ipareplica_prepare.forward_policy is not none else omit }}" no_dnssec_validation: "{{ ipareplica_no_dnssec_validation }}" + dot_forwarders: "{{ ipareplica_dot_forwarders | default([]) }}" + dns_over_tls: "{{ ipareplica__dns_over_tls }}" + dns_over_tls_cert: "{{ ipareplica_dns_over_tls_cert | default(omit) }}" + dns_over_tls_key: "{{ ipareplica_dns_over_tls_key | default(omit) }}" + dns_policy: "{{ ipareplica_dns_policy | default(omit) }}" ### additional ### dns_ip_addresses: "{{ result_ipareplica_prepare.dns_ip_addresses }}" dns_reverse_zones: "{{ result_ipareplica_prepare.dns_reverse_zones }}" diff --git a/roles/ipareplica/vars/Fedora.yml b/roles/ipareplica/vars/Fedora.yml index ffbdaeec..025910a2 100644 --- a/roles/ipareplica/vars/Fedora.yml +++ b/roles/ipareplica/vars/Fedora.yml @@ -3,5 +3,6 @@ --- ipareplica_packages: [ "freeipa-server", "python3-libselinux" ] ipareplica_packages_dns: [ "freeipa-server-dns" ] +ipareplica_packages_dot: [ "freeipa-server-encrypted-dns" ] ipareplica_packages_adtrust: [ "freeipa-server-trust-ad" ] ipareplica_packages_firewalld: [ "firewalld" ] diff --git a/roles/ipareplica/vars/RedHat-7.yml b/roles/ipareplica/vars/RedHat-7.yml index 34843523..4144b6f2 100644 --- a/roles/ipareplica/vars/RedHat-7.yml +++ b/roles/ipareplica/vars/RedHat-7.yml @@ -3,5 +3,6 @@ --- ipareplica_packages: [ "ipa-server", "libselinux-python" ] ipareplica_packages_dns: [ "ipa-server-dns" ] +ipareplica_packages_dot: [ ] ipareplica_packages_adtrust: [ "ipa-server-trust-ad" ] ipareplica_packages_firewalld: [ "firewalld" ] diff --git a/roles/ipareplica/vars/RedHat-8.yml b/roles/ipareplica/vars/RedHat-8.yml index a1b52994..c97f0659 100644 --- a/roles/ipareplica/vars/RedHat-8.yml +++ b/roles/ipareplica/vars/RedHat-8.yml @@ -3,5 +3,6 @@ --- ipareplica_packages: [ "@idm:DL1/server" ] ipareplica_packages_dns: [ "@idm:DL1/dns" ] +ipareplica_packages_dot: [ ] ipareplica_packages_adtrust: [ "@idm:DL1/adtrust" ] ipareplica_packages_firewalld: [ "firewalld" ] diff --git a/roles/ipareplica/vars/Ubuntu-18.04.yml b/roles/ipareplica/vars/Ubuntu-18.04.yml index b729f362..82667a1e 100644 --- a/roles/ipareplica/vars/Ubuntu-18.04.yml +++ b/roles/ipareplica/vars/Ubuntu-18.04.yml @@ -2,6 +2,7 @@ --- ipareplica_packages: [ "freeipa-server" ] ipareplica_packages_dns: [ "freeipa-server-dns" ] +ipareplica_packages_dot: [ ] ipareplica_packages_adtrust: [ "freeipa-server-trust-ad" ] ipareplica_packages_firewalld: [ "firewalld" ] # Ubuntu Bionic Beaver must use python2 as Python interpreter due diff --git a/roles/ipareplica/vars/Ubuntu.yml b/roles/ipareplica/vars/Ubuntu.yml index 23c2e89d..216f8fad 100644 --- a/roles/ipareplica/vars/Ubuntu.yml +++ b/roles/ipareplica/vars/Ubuntu.yml @@ -3,5 +3,6 @@ --- ipareplica_packages: [ "freeipa-server" ] ipareplica_packages_dns: [ "freeipa-server-dns" ] +ipareplica_packages_dot: [ ] ipareplica_packages_adtrust: [ "freeipa-server-trust-ad" ] ipareplica_packages_firewalld: [ "firewalld" ] diff --git a/roles/ipareplica/vars/default.yml b/roles/ipareplica/vars/default.yml index 0afedeb3..728e8373 100644 --- a/roles/ipareplica/vars/default.yml +++ b/roles/ipareplica/vars/default.yml @@ -1,7 +1,8 @@ # defaults file for ipareplica # vars/default.yml --- -ipareplica_packages: [ "freeipa-server", "python3-libselinux" ] -ipareplica_packages_dns: [ "freeipa-server-dns" ] -ipareplica_packages_adtrust: [ "freeipa-server-trust-ad" ] +ipareplica_packages: [ "ipa-server", "python3-libselinux" ] +ipareplica_packages_dns: [ "ipa-server-dns" ] +ipareplica_packages_dot: [ "ipa-server-encrypted-dns" ] +ipareplica_packages_adtrust: [ "ipa-server-trust-ad" ] ipareplica_packages_firewalld: [ "firewalld" ]