ipaclient: Add support for DNS over TLS

This change adds support for DNS over TLS to the ipaclient role.

New variables

ipaclient_dns_over_tls
    Configure DNS over TLS. Requires FreeIPA version 4.12.5 or later.
    (bool, default: false)
    required: false
ipaclient_no_dnssec_validation
    Disable DNSSEC validation for DNS over TLS. This turns off DNSSEC
    validation for unbound. Only usable if `ipaserver_dns_over_tls` is
    enabled. (bool, default: false)
    reqiured: false

New distribution specific variable

ipaclient_packages_dot
    List of IPA packages needed for DNS over TLS.

The resolver configuratoin for DNS over TLS is not part of this change
and will be added later on. Therefore it is needed to configure the
resolver for DNS over TLS before starting the deployment with ipaclient
role. This is essential for using an IPA DNS server with DoT and enforced
DNS policy so that only DoT is usable.
This commit is contained in:
Thomas Woerner
2025-07-01 14:19:13 +02:00
parent d553f9a0b1
commit 7a23c668fc
13 changed files with 92 additions and 8 deletions

View File

@@ -202,6 +202,8 @@ Variable | Description | Required
`ipaclient_request_cert` | The bool value defines if the certificate for the machine wil be requested. The certificate will be stored in /etc/ipa/nssdb under the nickname "Local IPA host". . `ipaclient_request_cert` defaults to `no`. The option is deprecated and will be removed in a future release. | no
`ipaclient_keytab` | The string value contains the path on the node of a backup host keytab from a previous enrollment. | no
`ipaclient_automount_location` | Automount location | no
`ipaclient_dns_over_tls` | Configure DNS over TLS. Requires FreeIPA version 4.12.5 or later. (bool, default: false) | no
`ipaclient_no_dnssec_validation` | Disable DNSSEC validation for DNS over TLS. This turns off DNSSEC validation for unbound. Ignored if `ipaserver_dns_over_tls` is not enabled. (bool, default: false) | no
Server Variables

View File

@@ -26,6 +26,8 @@ ipasssd_enable_dns_updates: no
ipasssd_no_krb5_offline_passwords: no
ipasssd_preserve_sssd: no
ipaclient_request_cert: no
ipaclient_dns_over_tls: no
ipaclient_no_dnssec_validation: no
### packages ###
ipaclient_install_packages: yes

View File

@@ -86,6 +86,16 @@ options:
type: bool
required: no
default: no
dns_over_tls:
description: Configure DNS over TLS
type: bool
default: no
required: no
no_dnssec_validation:
description: Disable DNSSEC validation for DNS over TLS
type: bool
default: no
required: no
enable_dns_updates:
description: |
Configures the machine to attempt dns updates when the ip address
@@ -212,7 +222,9 @@ def main():
mkhomedir=dict(required=False, type='bool'),
on_master=dict(required=False, type='bool'),
dnsok=dict(required=False, type='bool', default=False),
dns_over_tls=dict(required=False, type='bool', default=False),
no_dnssec_validation=dict(required=False, type='bool',
default=False),
enable_dns_updates=dict(required=False, type='bool'),
all_ip_addresses=dict(required=False, type='bool', default=False),
ip_addresses=dict(required=False, type='list', elements='str',
@@ -249,6 +261,8 @@ def main():
options.mkhomedir = module.params.get('mkhomedir')
options.on_master = module.params.get('on_master')
dnsok = module.params.get('dnsok')
options.dns_over_tls = module.params.get('dns_over_tls')
options.no_dnssec_validation = module.params.get('no_dnssec_validation')
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
@@ -256,6 +270,7 @@ def main():
os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
options.dns_updates = module.params.get('enable_dns_updates')
options.dns_over_tls = module.params.get('dns_over_tls')
options.all_ip_addresses = module.params.get('all_ip_addresses')
options.ip_addresses = ansible_module_get_parsed_ip_addresses(module)
options.request_cert = module.params.get('request_cert')
@@ -279,7 +294,7 @@ def main():
options.no_sssd = False
options.sssd = not options.no_sssd
options.no_ac = False
options.dns_over_tls = False
options.dns_over_tls = module.params.get('dns_over_tls')
nosssd_files = module.params.get('nosssd_files')
selinux_works = module.params.get('selinux_works')
krb_name = module.params.get('krb_name')

View File

@@ -91,6 +91,11 @@ options:
changes
type: bool
required: no
dns_over_tls:
description: Configure DNS over TLS
type: bool
default: no
required: no
preserve_sssd:
description: Preserve old SSSD configuration if possible
type: bool
@@ -140,6 +145,7 @@ def main():
fixed_primary=dict(required=False, type='bool'),
permit=dict(required=False, type='bool'),
enable_dns_updates=dict(required=False, type='bool'),
dns_over_tls=dict(required=False, type='bool', default=False),
preserve_sssd=dict(required=False, type='bool'),
no_krb5_offline_passwords=dict(required=False, type='bool'),
),
@@ -169,6 +175,7 @@ def main():
options.primary = module.params.get('fixed_primary')
options.permit = module.params.get('permit')
options.dns_updates = module.params.get('enable_dns_updates')
options.dns_over_tls = module.params.get('dns_over_tls')
options.preserve_sssd = module.params.get('preserve_sssd')
options.no_krb5_offline_passwords = module.params.get(

View File

@@ -124,6 +124,16 @@ options:
type: bool
required: no
default: no
dns_over_tls:
description: Configure DNS over TLS
type: bool
default: no
required: no
no_dnssec_validation:
description: Disable DNSSEC validation for DNS over TLS
type: bool
default: no
required: no
enable_dns_updates:
description:
Configures the machine to attempt dns updates when the ip address
@@ -248,7 +258,8 @@ from ansible.module_utils.ansible_ipa_client import (
CLIENT_INSTALL_ERROR, tasks, check_ldap_conf, timeconf, constants,
validate_hostname, nssldap_exists, gssapi, remove_file,
check_ip_addresses, ipadiscovery, print_port_conf_info,
IPA_PYTHON_VERSION, getargspec
IPA_PYTHON_VERSION, getargspec, services,
CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION
)
@@ -328,6 +339,9 @@ def main():
default=None),
all_ip_addresses=dict(required=False, type='bool', default=False),
on_master=dict(required=False, type='bool', default=False),
dns_over_tls=dict(required=False, type='bool', default=False),
no_dnssec_validation=dict(required=False, type='bool',
default=False),
# sssd
enable_dns_updates=dict(required=False, type='bool',
default=False),
@@ -356,6 +370,8 @@ def main():
options.ip_addresses = module.params.get('ip_addresses')
options.all_ip_addresses = module.params.get('all_ip_addresses')
options.on_master = module.params.get('on_master')
options.dns_over_tls = module.params.get('dns_over_tls')
options.no_dnssec_validation = module.params.get('no_dnssec_validation')
options.enable_dns_updates = module.params.get('enable_dns_updates')
# Get domain from first server if domain is not set, but if there are
@@ -365,6 +381,16 @@ def main():
options.domain_name = options.servers[0][
options.servers[0].find(".") + 1:]
if options.dns_over_tls \
and not services.knownservices["unbound"].is_installed():
module.fail_json(
msg="To enable DNS over TLS, package ipa-client-encrypted-dns "
"must be installed.")
if options.dns_over_tls and not CLIENT_SUPPORTS_NO_DNSSEC_VALIDATION:
module.fail_json(
msg="Important patches for DNS over TLS are missing in your IPA "
"version.")
try:
self = options

View File

@@ -310,6 +310,15 @@ try:
except ImportError:
configure_selinux_for_client = None
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
logger = logging.getLogger("ipa-client-install")
root_logger = logger

View File

@@ -1,11 +1,23 @@
---
# tasks file for ipaclient
- name: Install - Ensure that IPA client packages are installed
ansible.builtin.package:
name: "{{ ipaclient_packages }}"
state: present
- name: Install - Package installation
when: ipaclient_install_packages | bool
block:
- name: Install - Set packages for installation
ansible.builtin.set_fact:
_ipapackages: "{{ ipaclient_packages }}"
- name: Install - Set packages for installlation, add DOT
ansible.builtin.set_fact:
_ipapackages: "{{ _ipapackages + ipaclient_packages_dot }}"
when: ipaclient_dns_over_tls | bool
- name: Install - Ensure that packages are installed
ansible.builtin.package:
name: "{{ _ipapackages }}"
state: present
- name: Install - Set ipaclient_servers
ansible.builtin.set_fact:
@@ -38,7 +50,7 @@
msg: "ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
when: ipaserver_domain is not defined and ipaclient_domain is not defined
- name: Install - Fail on missing ipaclient_servers
- name: Install - Fail on missing ipaclient_dns_servers
ansible.builtin.fail:
msg: "ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
when: ipaclient_dns_servers is not defined
@@ -69,6 +81,8 @@
ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
on_master: "{{ ipaclient_on_master }}"
dns_over_tls: "{{ ipaclient_dns_over_tls }}"
no_dnssec_validation: "{{ ipaclient_no_dnssec_validation }}"
### sssd ###
enable_dns_updates: "{{ ipasssd_enable_dns_updates }}"
register: result_ipaclient_test
@@ -323,6 +337,7 @@
fixed_primary: "{{ ipasssd_fixed_primary }}"
permit: "{{ ipasssd_permit }}"
enable_dns_updates: "{{ ipasssd_enable_dns_updates }}"
dns_over_tls: "{{ ipaclient_dns_over_tls }}"
preserve_sssd: "{{ ipasssd_preserve_sssd }}"
no_krb5_offline_passwords: "{{ ipasssd_no_krb5_offline_passwords }}"
@@ -360,6 +375,8 @@
on_master: "{{ ipaclient_on_master }}"
dnsok: "{{ result_ipaclient_test.dnsok }}"
enable_dns_updates: "{{ ipasssd_enable_dns_updates }}"
dns_over_tls: "{{ ipaclient_dns_over_tls }}"
no_dnssec_validation: "{{ ipaclient_no_dnssec_validation }}"
all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
request_cert: "{{ ipaclient_request_cert }}"

View File

@@ -1,6 +1,7 @@
---
# vars/Debian.yml
ipaclient_packages: [ "freeipa-client" ]
ipaclient_packages_dot: [ ]
# Debian Buster must use python2 as Python interpreter due
# to the way freeipa-client package is defined.
# You must install package python2.7 before executing this role.

View File

@@ -2,3 +2,4 @@
# vars/Debian.yml
---
ipaclient_packages: [ "freeipa-client" ]
ipaclient_packages_dot: [ ]

View File

@@ -2,3 +2,4 @@
# vars/RedHat-7
---
ipaclient_packages: [ "ipa-client", "libselinux-python" ]
ipaclient_packages_dot: [ ]

View File

@@ -2,3 +2,4 @@
# vars/RedHat-8.yml
---
ipaclient_packages: [ "@idm:DL1/client" ]
ipaclient_packages_dot: [ ]

View File

@@ -1,6 +1,7 @@
# vars/Ubuntu-18.04.yml
---
ipaclient_packages: [ "freeipa-client" ]
ipaclient_packages_dot: [ ]
# Ubuntu Bionic Beaver must use python2 as Python interpreter due
# to the way python-ipalib package is defined.
# Package python2.7 must be installed before executing this role.

View File

@@ -2,3 +2,4 @@
# vars/default.yml
---
ipaclient_packages: [ "ipa-client", "python3-libselinux" ]
ipaclient_packages_dot: [ "ipa-client-encrypted-dns" ]