Use Network Manager to configure resolv.conf

IPA used to write a custom /etc/resolv.conf. On Fedora and RHEL,
NetworkManager is typically maintaining resolv.conf. On reboot or
restart of the service, NM overwrites the custom settings.

On systems with NM enabled, the DNS server installer now drops a config
file into NM's global config directory and delegates resolv.conf to NM.

On systems without NM, fall back to create /etc/resolv.conf directly.

Fixes: https://pagure.io/freeipa/issue/7900
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Thomas Woerner <twoerner@redhat.com>
This commit is contained in:
Christian Heimes committed 2019-04-16 10:45:59 +02:00
1 parent b79ea6a6e9
commit 80928ba6f5
6 files changed
+126 -23

No files matched your search

+1
View File
@@ -348,6 +348,7 @@ class BasePathNamespace:
KDCPROXY_CONFIG = '/etc/ipa/kdcproxy/kdcproxy.conf'
CERTMONGER = '/usr/sbin/certmonger'
NETWORK_MANAGER_CONFIG_DIR = '/etc/NetworkManager/conf.d'
NETWORK_MANAGER_IPA_CONF = '/etc/NetworkManager/conf.d/zzz-ipa.conf'
IPA_CUSTODIA_CONF_DIR = '/etc/ipa/custodia'
IPA_CUSTODIA_CONF = '/etc/ipa/custodia/custodia.conf'
IPA_CUSTODIA_KEYS = '/etc/ipa/custodia/server.keys'
+1 -1
View File
@@ -55,7 +55,7 @@ wellknownservices = ['certmonger', 'dirsrv', 'httpd', 'ipa', 'krb5kdc',
'rpcbind', 'kadmin', 'sshd', 'autofs', 'rpcgssd',
'rpcidmapd', 'pki_tomcatd', 'chronyd', 'domainname',
'named', 'ods_enforcerd', 'ods_signerd', 'gssproxy',
'nfs-utils', 'sssd']
'nfs-utils', 'sssd', 'NetworkManager']
# The common ports for these services. This is used to wait for the
# service to become available.
+17
View File
@@ -255,5 +255,22 @@ class BaseTaskNamespace:
"""Tell systemd to reload config files"""
raise NotImplementedError
def configure_dns_resolver(self, nameservers, searchdomains, fstore=None):
"""Configure global DNS resolver (e.g. /etc/resolv.conf)
:param nameservers: list of IP addresses
:param searchdomains: list of search domaons
:param fstore: optional file store for backup
"""
raise NotImplementedError
def unconfigure_dns_resolver(self, fstore=None):
"""Unconfigure global DNS resolver (e.g. /etc/resolv.conf)
:param fstore: optional file store for restore
"""
if fstore is not None and fstore.has_file(paths.RESOLV_CONF):
fstore.restore_file(paths.RESOLV_CONF)
tasks = BaseTaskNamespace()
+81
View File
@@ -34,6 +34,7 @@ import errno
import urllib
import subprocess
import sys
import textwrap
from ctypes.util import find_library
from functools import total_ordering
@@ -53,6 +54,19 @@ from ipaplatform.base.tasks import BaseTaskNamespace
logger = logging.getLogger(__name__)
NM_IPA_CONF = textwrap.dedent("""
# auto-generated by IPA installer
[main]
dns=default
[global-dns]
searches={searches}
[global-dns-domain-*]
servers={servers}
""")
def selinux_enabled():
"""
Check if SELinux is enabled.
@@ -598,5 +612,72 @@ class RedHatTaskNamespace(BaseTaskNamespace):
'TransferLog',
'logs/access_log', False)
def configure_dns_resolver(self, nameservers, searchdomains, fstore=None):
"""Configure global DNS resolver (e.g. /etc/resolv.conf)
:param nameservers: list of IP addresses
:param searchdomains: list of search domaons
:param fstore: optional file store for backup
"""
assert nameservers and isinstance(nameservers, list)
assert searchdomains and isinstance(searchdomains, list)
# break circular import
from ipaplatform.services import knownservices
if fstore is not None and not fstore.has_file(paths.RESOLV_CONF):
fstore.backup_file(paths.RESOLV_CONF)
nm = knownservices['NetworkManager']
if nm.is_enabled():
logger.debug(
"Network Manager is enabled, write %s",
paths.NETWORK_MANAGER_IPA_CONF
)
# write DNS override and reload network manager to have it create
# a new resolv.conf. The file is prefixed with ``zzz`` to
# make it the last file. Global dns options do not stack and last
# man standing wins.
cfg = NM_IPA_CONF.format(
servers=','.join(nameservers),
searches=','.join(searchdomains)
)
with open(paths.NETWORK_MANAGER_IPA_CONF, 'w') as f:
os.fchmod(f.fileno(), 0o644)
f.write(cfg)
# reload NetworkManager
nm.reload_or_restart()
else:
# no NM running, fall back to /etc/resolv.conf
logger.debug(
"Network Manager is not enabled, write %s directly.",
paths.RESOLV_CONF
)
cfg = [
"# auto-generated by IPA installer",
"search {}".format(' '.join(searchdomains)),
]
for nameserver in nameservers:
cfg.append("nameserver {}".format(nameserver))
with open(paths.RESOLV_CONF, 'w') as f:
f.write('\n'.join(cfg))
def unconfigure_dns_resolver(self, fstore=None):
"""Unconfigure global DNS resolver (e.g. /etc/resolv.conf)
:param fstore: optional file store for restore
"""
# break circular import
from ipaplatform.services import knownservices
if fstore is not None and fstore.has_file(paths.RESOLV_CONF):
fstore.restore_file(paths.RESOLV_CONF)
nm = knownservices['NetworkManager']
if os.path.isfile(paths.NETWORK_MANAGER_IPA_CONF):
os.unlink(paths.NETWORK_MANAGER_IPA_CONF)
if nm.is_enabled():
nm.reload_or_restart()
tasks = RedHatTaskNamespace()
+20 -22
View File
@@ -51,6 +51,7 @@ import ipalib
from ipalib import api, errors
from ipalib.constants import IPA_CA_RECORD
from ipaplatform import services
from ipaplatform.tasks import tasks
from ipaplatform.constants import constants
from ipaplatform.paths import paths
from ipalib.util import (validate_zonemgr_str, normalize_zonemgr,
@@ -1007,32 +1008,25 @@ class BindInstance(service.Service):
sysupgrade.set_upgrade_state('dns', 'server_config_to_ldap', True)
def __setup_resolv_conf(self):
if not self.fstore.has_file(paths.RESOLV_CONF):
self.fstore.backup_file(paths.RESOLV_CONF)
resolv_txt = "search "+self.domain+"\n"
searchdomains = [self.domain]
nameservers = []
for ip_address in self.ip_addresses:
if ip_address.version == 4:
resolv_txt += "nameserver 127.0.0.1\n"
break
nameservers.append("127.0.0.1")
elif ip_address.version == 6:
nameservers.append("::1")
for ip_address in self.ip_addresses:
if ip_address.version == 6:
resolv_txt += "nameserver ::1\n"
break
try:
resolv_fd = open(paths.RESOLV_CONF, 'w')
resolv_fd.seek(0)
resolv_fd.truncate(0)
resolv_fd.write(resolv_txt)
resolv_fd.close()
tasks.configure_dns_resolver(
nameservers, searchdomains, fstore=self.fstore
)
except IOError as e:
logger.error('Could not write to resolv.conf: %s', e)
logger.error('Could not update DNS config: %s', e)
else:
# python DNS might have global resolver cached in this variable
# we have to re-initialize it because resolv.conf has changed
dns.resolver.default_resolver = None
dns.resolver.reset_default_resolver()
def __generate_rndc_key(self):
installutils.check_entropy()
@@ -1212,11 +1206,15 @@ class BindInstance(service.Service):
self.dns_backup.clear_records(self.api.Backend.ldap2.isconnected())
for f in [paths.NAMED_CONF, paths.RESOLV_CONF]:
try:
self.fstore.restore_file(f)
except ValueError as error:
logger.debug('%s', error)
try:
self.fstore.restore_file(paths.NAMED_CONF)
except ValueError as error:
logger.debug('%s', error)
try:
tasks.unconfigure_dns_resolver(fstore=self.fstore)
except Exception:
logger.exception("Failed to unconfigure DNS resolver")
installutils.rmtree(paths.BIND_LDAP_DNS_IPA_WORKDIR)
+6
View File
@@ -424,6 +424,12 @@ class TestInstallDNSSECFirst(IntegrationTest):
self.master.run_command(args)
self.replicas[0].run_command(args)
def test_resolvconf(self):
# check that resolv.conf contains IP address for localhost
for host in [self.master, self.replicas[0]]:
resolvconf = host.get_file_contents(paths.RESOLV_CONF, 'utf-8')
assert any(ip in resolvconf for ip in ('127.0.0.1', '::1'))
class TestMigrateDNSSECMaster(IntegrationTest):
"""test DNSSEC master migration