Migration from authconfig to authselect

The authconfig tool is deprecated and replaced by authselect. Migrate
FreeIPA in order to use the new tool as described in the design page
https://www.freeipa.org/page/V4/Authselect_migration

Fixes:
https://pagure.io/freeipa/issue/7377

Reviewed-By: Alexander Koksharov <akokshar@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Florence Blanc-Renaud 2018-04-26 16:51:42 +02:00 committed by Christian Heimes
parent fc371b651e
commit 1df1786b06
8 changed files with 355 additions and 60 deletions

View File

@ -597,7 +597,7 @@ Requires: python2-sssdconfig
Requires: cyrus-sasl-gssapi%{?_isa}
Requires: chrony
Requires: krb5-workstation >= %{krb5_version}
Requires: authconfig
Requires: authselect >= 0.4-2
Requires: curl
# NIS domain name config: /usr/lib/systemd/system/*-domainname.service
Requires: initscripts

View File

@ -2036,6 +2036,22 @@ def install_check(options):
"Invalid hostname, '{}' must not be used.".format(hostname),
rval=CLIENT_INSTALL_ERROR)
# --no-sssd is not supported any more for rhel-based distros
if not tasks.is_nosssd_supported() and not options.sssd:
raise ScriptError(
"Option '--no-sssd' is incompatible with the 'authselect' tool "
"provided by this distribution for configuring system "
"authentication resources",
rval=CLIENT_INSTALL_ERROR)
# --noac is not supported any more for rhel-based distros
if not tasks.is_nosssd_supported() and options.no_ac:
raise ScriptError(
"Option '--noac' is incompatible with the 'authselect' tool "
"provided by this distribution for configuring system "
"authentication resources",
rval=CLIENT_INSTALL_ERROR)
# when installing with '--no-sssd' option, check whether nss-ldap is
# installed
if not options.sssd:
@ -2899,9 +2915,11 @@ def _install(options):
if not options.no_ac:
# Modify nsswitch/pam stack
tasks.modify_nsswitch_pam_stack(sssd=options.sssd,
tasks.modify_nsswitch_pam_stack(
sssd=options.sssd,
mkhomedir=options.mkhomedir,
statestore=statestore)
statestore=statestore
)
logger.info("%s enabled", "SSSD" if options.sssd else "LDAP")

View File

@ -376,6 +376,8 @@ class BasePathNamespace(object):
IF_INET6 = '/proc/net/if_inet6'
WSGI_PREFIX_DIR = "/run/httpd/wsgi"
AUTHCONFIG = None
AUTHSELECT = None
SYSCONF_NETWORK = None
IPA_SERVER_UPGRADE = '/usr/sbin/ipa-server-upgrade'
KEYCTL = '/usr/bin/keyctl'
GETENT = '/usr/bin/getent'

View File

@ -135,7 +135,7 @@ class BaseTaskNamespace(object):
def modify_nsswitch_pam_stack(self, sssd, mkhomedir, statestore):
"""
If sssd flag is true, configure pam and nsswtich so that SSSD is used
If sssd flag is true, configure pam and nsswitch so that SSSD is used
for retrieving user information and authentication.
Otherwise, configure pam and nsswitch to leverage pure LDAP.
@ -150,6 +150,13 @@ class BaseTaskNamespace(object):
raise NotImplementedError()
def is_nosssd_supported(self):
"""
Check if the flag --no-sssd is supported for client install.
"""
return True
def backup_auth_configuration(self, path):
"""
Create backup of access control configuration.
@ -165,6 +172,12 @@ class BaseTaskNamespace(object):
"""
raise NotImplementedError()
def migrate_auth_configuration(self, statestore):
"""
Migrate pam stack configuration to authselect.
"""
return
def set_selinux_booleans(self, required_settings, backup_func=None):
"""Set the specified SELinux booleans

View File

@ -19,6 +19,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import
import logging
import six
import abc
import re
from ipaplatform.paths import paths
from ipapython import ipautil
@ -27,8 +31,192 @@ import os
FILES_TO_NOT_BACKUP = ['passwd', 'group', 'shadow', 'gshadow']
logger = logging.getLogger(__name__)
class RedHatAuthConfig(object):
def get_auth_tool():
return RedHatAuthSelect()
@six.add_metaclass(abc.ABCMeta)
class RedHatAuthToolBase(object):
@abc.abstractmethod
def configure(self, sssd, mkhomedir, statestore):
pass
@abc.abstractmethod
def unconfigure(self, fstore, statestore,
was_sssd_installed,
was_sssd_configured):
pass
@abc.abstractmethod
def backup(self, path):
"""
Backup the system authentication resources configuration
:param path: directory where the backup will be stored
"""
pass
@abc.abstractmethod
def restore(self, path):
"""
Restore the system authentication resources configuration from a backup
:param path: directory where the backup is stored
"""
pass
@abc.abstractmethod
def set_nisdomain(self, nisdomain):
pass
class RedHatAuthSelect(RedHatAuthToolBase):
def _get_authselect_current_output(self):
try:
current = ipautil.run(
[paths.AUTHSELECT, "current"], env={"LC_ALL": "C.UTF8"})
except ipautil.CalledProcessError:
logger.debug("Current configuration not managed by authselect")
return None
return current.raw_output.decode()
def _parse_authselect_output(self, output_text=None):
"""
Parses the output_text to extract the profile and options.
When no text is provided, runs the 'authselect profile' command to
generate the text to be parsed.
"""
if output_text is None:
output_text = self._get_authselect_current_output()
if output_text is None:
return None
cfg_params = re.findall(
r"\s*Profile ID:\s*(\S+)\s*\n\s*Enabled features:\s*(.*)",
output_text,
re.DOTALL
)
profile = cfg_params[0][0]
if not profile:
return None
features = re.findall(r"-\s*(\S+)", cfg_params[0][1], re.DOTALL)
return profile, features
def configure(self, sssd, mkhomedir, statestore):
# In the statestore, the following keys are used for the
# 'authselect' module:
# profile: name of the profile configured pre-installation
# features_list: lsit of features configured pre-installation
# mkhomedir: True if installation was called with --mkhomedir
# profile and features_list are used when reverting to the
# pre-install state
cfg = self._parse_authselect_output()
if cfg:
statestore.backup_state('authselect', 'profile', cfg[0])
statestore.backup_state(
'authselect', 'features_list', " ".join(cfg[1]))
else:
# cfg = None means that the current conf is not managed by
# authselect but by authconfig.
# As we are using authselect to configure the host,
# it will not be possible to revert to a custom authconfig
# configuration later (during uninstall)
# Best thing to do will be to use sssd profile at this time
logger.warning(
"WARNING: The configuration pre-client installation is not "
"managed by authselect and cannot be backed up. "
"Uninstallation may not be able to revert to the original "
"state.")
cmd = [paths.AUTHSELECT, "select", "sssd"]
if mkhomedir:
cmd.append("with-mkhomedir")
statestore.backup_state('authselect', 'mkhomedir', True)
cmd.append("--force")
ipautil.run(cmd)
def unconfigure(
self, fstore, statestore, was_sssd_installed, was_sssd_configured
):
if not statestore.has_state('authselect'):
logger.warning(
"WARNING: Unable to revert to the pre-installation state "
"('authconfig' tool has been deprecated in favor of "
"'authselect'). The default sssd profile will be used "
"instead.")
# Build the equivalent command line that will be displayed
# to the user
# This is a copy-paste of unconfigure code, except that it
# creates the command line but does not actually call it
authconfig = RedHatAuthConfig()
authconfig.prepare_unconfigure(
fstore, statestore, was_sssd_installed, was_sssd_configured)
args = authconfig.build_args()
logger.warning(
"The authconfig arguments would have been: authconfig %s",
" ".join(args))
profile = 'sssd'
features = ''
else:
profile = statestore.restore_state('authselect', 'profile')
features = statestore.restore_state('authselect', 'features_list')
statestore.delete_state('authselect', 'mkhomedir')
cmd = [paths.AUTHSELECT, "select", profile, features, "--force"]
ipautil.run(cmd)
def backup(self, path):
current = self._get_authselect_current_output()
if current is None:
return
if not os.path.exists(path):
os.makedirs(path)
with open(os.path.join(path, "authselect.backup"), 'w') as f:
f.write(current)
def restore(self, path):
with open(os.path.join(path, "authselect.backup"), "r") as f:
cfg = self._parse_authselect_output(f.read())
if cfg:
profile = cfg[0]
cmd = [
paths.AUTHSELECT, "select", profile,
" ".join(cfg[1]), "--force"]
ipautil.run(cmd)
def set_nisdomain(self, nisdomain):
try:
with open(paths.SYSCONF_NETWORK, 'r') as f:
content = [
line for line in f
if not line.strip().upper().startswith('NISDOMAIN')
]
except IOError:
content = []
content.append("NISDOMAIN={}\n".format(nisdomain))
with open(paths.SYSCONF_NETWORK, 'w') as f:
f.writelines(content)
# RedHatAuthConfig concrete class definition to be removed later
# when agreed on exact path to migrate to authselect
class RedHatAuthConfig(RedHatAuthToolBase):
"""
AuthConfig class implements system-independent interface to configure
system authentication resources. In Red Hat systems this is done with
@ -95,6 +283,60 @@ class RedHatAuthConfig(object):
except ipautil.CalledProcessError:
raise ScriptError("Failed to execute authconfig command")
def configure(self, sssd, mkhomedir, statestore):
if sssd:
statestore.backup_state('authconfig', 'sssd', True)
statestore.backup_state('authconfig', 'sssdauth', True)
self.enable("sssd")
self.enable("sssdauth")
else:
statestore.backup_state('authconfig', 'ldap', True)
self.enable("ldap")
self.enable("forcelegacy")
statestore.backup_state('authconfig', 'krb5', True)
self.enable("krb5")
self.add_option("nostart")
if mkhomedir:
statestore.backup_state('authconfig', 'mkhomedir', True)
self.enable("mkhomedir")
self.execute()
self.reset()
def prepare_unconfigure(self, fstore, statestore,
was_sssd_installed,
was_sssd_configured):
if statestore.has_state('authconfig'):
# disable only those configurations that we enabled during install
for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
cnf = statestore.restore_state('authconfig', conf)
# Do not disable sssd, as this can cause issues with its later
# uses. Remove it from statestore however, so that it becomes
# empty at the end of uninstall process.
if cnf and conf != 'sssd':
self.disable(conf)
else:
# There was no authconfig status store
# It means the code was upgraded after original install
# Fall back to old logic
self.disable("ldap")
self.disable("krb5")
if not(was_sssd_installed and was_sssd_configured):
# Only disable sssdauth. Disabling sssd would cause issues
# with its later uses.
self.disable("sssdauth")
self.disable("mkhomedir")
def unconfigure(self, fstore, statestore,
was_sssd_installed,
was_sssd_configured):
self.prepare_unconfigure(
fstore, statestore, was_sssd_installed, was_sssd_configured)
self.execute()
self.reset()
def backup(self, path):
try:
ipautil.run([paths.AUTHCONFIG, "--savebackup", path])
@ -116,3 +358,9 @@ class RedHatAuthConfig(object):
ipautil.run([paths.AUTHCONFIG, "--restorebackup", path])
except ipautil.CalledProcessError:
raise ScriptError("Failed to execute authconfig command")
def set_nisdomain(self, nisdomain):
# Let authconfig setup the permanent configuration
self.reset()
self.add_parameter("nisdomain", nisdomain)
self.execute()

View File

@ -37,6 +37,8 @@ class RedHatPathNamespace(BasePathNamespace):
PAM_KRB5_SO = BasePathNamespace.PAM_KRB5_SO_64
BIND_LDAP_SO = BasePathNamespace.BIND_LDAP_SO_64
AUTHCONFIG = '/usr/sbin/authconfig'
AUTHSELECT = '/usr/bin/authselect'
SYSCONF_NETWORK = '/etc/sysconfig/network'
paths = RedHatPathNamespace()

View File

@ -45,7 +45,7 @@ import ipapython.errors
from ipaplatform.constants import constants
from ipaplatform.paths import paths
from ipaplatform.redhat.authconfig import RedHatAuthConfig
from ipaplatform.redhat.authconfig import get_auth_tool
from ipaplatform.base.tasks import BaseTaskNamespace
logger = logging.getLogger(__name__)
@ -186,70 +186,67 @@ class RedHatTaskNamespace(BaseTaskNamespace):
was_sssd_installed,
was_sssd_configured):
auth_config = RedHatAuthConfig()
if statestore.has_state('authconfig'):
# disable only those configurations that we enabled during install
for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
cnf = statestore.restore_state('authconfig', conf)
# Do not disable sssd, as this can cause issues with its later
# uses. Remove it from statestore however, so that it becomes
# empty at the end of uninstall process.
if cnf and conf != 'sssd':
auth_config.disable(conf)
else:
# There was no authconfig status store
# It means the code was upgraded after original install
# Fall back to old logic
auth_config.disable("ldap")
auth_config.disable("krb5")
if not(was_sssd_installed and was_sssd_configured):
# Only disable sssdauth. Disabling sssd would cause issues
# with its later uses.
auth_config.disable("sssdauth")
auth_config.disable("mkhomedir")
auth_config.execute()
auth_config = get_auth_tool()
auth_config.unconfigure(
fstore, statestore, was_sssd_installed, was_sssd_configured
)
def set_nisdomain(self, nisdomain):
# Let authconfig setup the permanent configuration
auth_config = RedHatAuthConfig()
auth_config.add_parameter("nisdomain", nisdomain)
auth_config.execute()
try:
with open(paths.SYSCONF_NETWORK, 'r') as f:
content = [
line for line in f
if not line.strip().upper().startswith('NISDOMAIN')
]
except IOError:
content = []
content.append("NISDOMAIN={}\n".format(nisdomain))
with open(paths.SYSCONF_NETWORK, 'w') as f:
f.writelines(content)
def modify_nsswitch_pam_stack(self, sssd, mkhomedir, statestore):
auth_config = RedHatAuthConfig()
auth_config = get_auth_tool()
auth_config.configure(sssd, mkhomedir, statestore)
if sssd:
statestore.backup_state('authconfig', 'sssd', True)
statestore.backup_state('authconfig', 'sssdauth', True)
auth_config.enable("sssd")
auth_config.enable("sssdauth")
else:
statestore.backup_state('authconfig', 'ldap', True)
auth_config.enable("ldap")
auth_config.enable("forcelegacy")
if mkhomedir:
statestore.backup_state('authconfig', 'mkhomedir', True)
auth_config.enable("mkhomedir")
auth_config.execute()
def modify_pam_to_use_krb5(self, statestore):
auth_config = RedHatAuthConfig()
statestore.backup_state('authconfig', 'krb5', True)
auth_config.enable("krb5")
auth_config.add_option("nostart")
auth_config.execute()
def is_nosssd_supported(self):
# The flag --no-sssd is not supported any more for rhel-based distros
return False
def backup_auth_configuration(self, path):
auth_config = RedHatAuthConfig()
auth_config = get_auth_tool()
auth_config.backup(path)
def restore_auth_configuration(self, path):
auth_config = RedHatAuthConfig()
auth_config = get_auth_tool()
auth_config.restore(path)
def migrate_auth_configuration(self, statestore):
"""
Migrate the pam stack configuration from authconfig to an authselect
profile.
"""
# Check if mkhomedir was enabled during installation
mkhomedir = statestore.get_state('authconfig', 'mkhomedir')
# Force authselect 'sssd' profile
authselect_cmd = [paths.AUTHSELECT, "select", "sssd"]
if mkhomedir:
authselect_cmd.append("with-mkhomedir")
authselect_cmd.append("--force")
ipautil.run(authselect_cmd)
# Remove all remaining keys from the authconfig module
for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
statestore.restore_state('authconfig', conf)
# Create new authselect module in the statestore
statestore.backup_state('authselect', 'profile', 'sssd')
statestore.backup_state(
'authselect', 'features_list', '')
statestore.backup_state('authselect', 'mkhomedir', bool(mkhomedir))
def reload_systemwide_ca_store(self):
try:
ipautil.run([paths.UPDATE_CA_TRUST])
@ -408,7 +405,6 @@ class RedHatTaskNamespace(BaseTaskNamespace):
if fstore.has_file(filepath):
fstore.restore_file(filepath)
def set_selinux_booleans(self, required_settings, backup_func=None):
def get_setsebool_args(changes):
args = [paths.SETSEBOOL, "-P"]

View File

@ -1654,6 +1654,21 @@ def update_replica_config(db_suffix):
api.Backend.ldap2.update_entry(entry)
def migrate_to_authselect():
logger.info('[Migrating to authselect profile]')
if sysupgrade.get_upgrade_state('authcfg', 'migrated_to_authselect'):
logger.info("Already migrated to authselect profile")
return
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
try:
tasks.migrate_auth_configuration(statestore)
except ipautil.CalledProcessError as e:
raise RuntimeError(
"Failed to migrate to authselect profile: %s" % e, 1)
sysupgrade.set_upgrade_state('authcfg', 'migrated_to_authselect', True)
def upgrade_configuration():
"""
Execute configuration upgrade of the IPA services
@ -1933,6 +1948,7 @@ def upgrade_configuration():
ca.setup_lightweight_ca_key_retrieval()
cainstance.ensure_ipa_authority_entry()
migrate_to_authselect()
set_sssd_domain_option('ipa_server_mode', 'True')
set_sssd_domain_option('ipa_server', api.env.host)