If SELinux is enabled ensure we also have restorecon.

We don't have a specific requires on the policycoreutils package. It
gets pulled in as a dependency on the server anyway, but checking
there is like a belt and suspenders.

On the client we don't require SELinux at all. If SELinux is enabled
however we need to set things up properly. This is provided by the
policycoreutils package so fail if that isn't available.

https://fedorahosted.org/freeipa/ticket/2368
This commit is contained in:
Rob Crittenden
2012-05-31 13:59:33 +02:00
committed by Martin Kosek
parent 5b465811ce
commit 9e877585e2
6 changed files with 107 additions and 42 deletions

View File

@@ -276,6 +276,7 @@ def check_bind():
sys.exit(1) sys.exit(1)
def main(): def main():
ipaservices.check_selinux_status()
safe_options, options, filename = parse_options() safe_options, options, filename = parse_options()
standard_logging_setup("/var/log/ipareplica-install.log", debug=options.debug) standard_logging_setup("/var/log/ipareplica-install.log", debug=options.debug)
root_logger.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options)) root_logger.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options))

View File

@@ -539,6 +539,8 @@ def main():
if os.getegid() != 0: if os.getegid() != 0:
sys.exit("Must be root to set up server") sys.exit("Must be root to set up server")
ipaservices.check_selinux_status()
signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)

View File

@@ -1539,6 +1539,7 @@ def main():
if not os.getegid() == 0: if not os.getegid() == 0:
sys.exit("\nYou must be root to run ipa-client-install.\n") sys.exit("\nYou must be root to run ipa-client-install.\n")
ipaservices.check_selinux_status()
logging_setup(options) logging_setup(options)
root_logger.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options)) root_logger.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
root_logger.debug("missing options might be asked for interactively later\n") root_logger.debug("missing options might be asked for interactively later\n")

View File

@@ -22,25 +22,36 @@ from ipapython.platform import base, redhat, systemd
import os import os
# All what we allow exporting directly from this module # All what we allow exporting directly from this module
# Everything else is made available through these symbols when they directly imported into ipapython.services: # Everything else is made available through these symbols when they are
# authconfig -- class reference for platform-specific implementation of authconfig(8) # directly imported into ipapython.services:
# service -- class reference for platform-specific implementation of a PlatformService class # authconfig -- class reference for platform-specific implementation of
# knownservices -- factory instance to access named services IPA cares about, names are ipapython.services.wellknownservices # authconfig(8)
# backup_and_replace_hostname -- platform-specific way to set hostname and make it persistent over reboots # service -- class reference for platform-specific implementation of a
# restore_context -- platform-sepcific way to restore security context, if applicable # PlatformService class
__all__ = ['authconfig', 'service', 'knownservices', 'backup_and_replace_hostname', 'restore_context'] # knownservices -- factory instance to access named services IPA cares about,
# names are ipapython.services.wellknownservices
# backup_and_replace_hostname -- platform-specific way to set hostname and
# make it persistent over reboots
# restore_context -- platform-sepcific way to restore security context, if
# applicable
# check_selinux_status -- platform-specific way to see if SELinux is enabled
# and restorecon is installed.
__all__ = ['authconfig', 'service', 'knownservices', 'backup_and_replace_hostname', 'restore_context', 'check_selinux_status']
# For beginning just remap names to add .service # For beginning just remap names to add .service
# As more services will migrate to systemd, unit names will deviate and # As more services will migrate to systemd, unit names will deviate and
# mapping will be kept in this dictionary # mapping will be kept in this dictionary
system_units = dict(map(lambda x: (x, "%s.service" % (x)), base.wellknownservices)) system_units = dict(map(lambda x: (x, "%s.service" % (x)), base.wellknownservices))
# Rewrite dirsrv and pki-cad services as they support instances via separate service generator # Rewrite dirsrv and pki-cad services as they support instances via separate
# To make this working, one needs to have both foo@.service and foo.target -- the latter is used # service generator. To make this working, one needs to have both foo@.servic
# when request should be coming for all instances (like stop). systemd, unfortunately, does not allow # and foo.target -- the latter is used when request should be coming for
# to request action for all service instances at once if only foo@.service unit is available. # all instances (like stop). systemd, unfortunately, does not allow one
# To add more, if any of those services need to be started/stopped automagically, one needs to manually # to request action for all service instances at once if only foo@.service
# create symlinks in /etc/systemd/system/foo.target.wants/ (look into systemd.py's enable() code). # unit is available. To add more, if any of those services need to be
# started/stopped automagically, one needs to manually create symlinks in
# /etc/systemd/system/foo.target.wants/ (look into systemd.py's enable()
# code).
system_units['dirsrv'] = 'dirsrv@.service' system_units['dirsrv'] = 'dirsrv@.service'
# Our directory server instance for PKI is dirsrv@PKI-IPA.service # Our directory server instance for PKI is dirsrv@PKI-IPA.service
system_units['pkids'] = 'dirsrv@PKI-IPA.service' system_units['pkids'] = 'dirsrv@PKI-IPA.service'
@@ -53,30 +64,35 @@ class Fedora16Service(systemd.SystemdService):
service_name = system_units[service_name] service_name = system_units[service_name]
else: else:
if len(service_name.split('.')) == 1: if len(service_name.split('.')) == 1:
# if service_name does not have a dot, it is not foo.service and not a foo.target # if service_name does not have a dot, it is not foo.service
# Thus, not correct service name for systemd, default to foo.service style then # and not a foo.target. Thus, not correct service name for
# systemd, default to foo.service style then
service_name = "%s.service" % (service_name) service_name = "%s.service" % (service_name)
super(Fedora16Service, self).__init__(service_name) super(Fedora16Service, self).__init__(service_name)
# Special handling of directory server service # Special handling of directory server service
# #
# We need to explicitly enable instances to install proper symlinks as dirsrv.target.wants/ # We need to explicitly enable instances to install proper symlinks as
# dependencies. Standard systemd service class does it on #enable() method call. Unfortunately, # dirsrv.target.wants/ dependencies. Standard systemd service class does it
# ipa-server-install does not do explicit dirsrv.enable() because the service startup is handled by ipactl. # on enable() method call. Unfortunately, ipa-server-install does not do
# explicit dirsrv.enable() because the service startup is handled by ipactl.
# #
# If we wouldn't do this, our instances will not be started as systemd would not have any clue # If we wouldn't do this, our instances will not be started as systemd would
# about instances (PKI-IPA and the domain we serve) at all. Thus, hook into dirsrv.restart(). # not have any clue about instances (PKI-IPA and the domain we serve) at all.
# Thus, hook into dirsrv.restart().
class Fedora16DirectoryService(Fedora16Service): class Fedora16DirectoryService(Fedora16Service):
def enable(self, instance_name=""): def enable(self, instance_name=""):
super(Fedora16DirectoryService, self).enable(instance_name) super(Fedora16DirectoryService, self).enable(instance_name)
dirsrv_systemd = "/etc/sysconfig/dirsrv.systemd" dirsrv_systemd = "/etc/sysconfig/dirsrv.systemd"
if os.path.exists(dirsrv_systemd): if os.path.exists(dirsrv_systemd):
# We need to enable LimitNOFILE=8192 in the dirsrv@.service # We need to enable LimitNOFILE=8192 in the dirsrv@.service
# Since 389-ds-base-1.2.10-0.8.a7 the configuration of the service parameters is performed # Since 389-ds-base-1.2.10-0.8.a7 the configuration of the
# via /etc/sysconfig/dirsrv.systemd file which is imported by systemd into dirsrv@.service unit # service parameters is performed via
# /etc/sysconfig/dirsrv.systemd file which is imported by systemd
# into dirsrv@.service unit
replacevars = {'LimitNOFILE':'8192'} replacevars = {'LimitNOFILE':'8192'}
ipautil.inifile_replace_variables(dirsrv_systemd, 'service', replacevars=replacevars) ipautil.inifile_replace_variables(dirsrv_systemd, 'service', replacevars=replacevars)
redhat.restore_context(dirsrv_systemd) restore_context(dirsrv_systemd)
ipautil.run(["/bin/systemctl", "--system", "daemon-reload"],raiseonerr=False) ipautil.run(["/bin/systemctl", "--system", "daemon-reload"],raiseonerr=False)
def restart(self, instance_name="", capture_output=True): def restart(self, instance_name="", capture_output=True):
@@ -93,8 +109,9 @@ class Fedora16DirectoryService(Fedora16Service):
super(Fedora16DirectoryService, self).restart(instance_name, capture_output=capture_output) super(Fedora16DirectoryService, self).restart(instance_name, capture_output=capture_output)
# Enforce restart of IPA services when we do enable it # Enforce restart of IPA services when we do enable it
# This gets around the fact that after ipa-server-install systemd thinks ipa.service is not yet started # This gets around the fact that after ipa-server-install systemd thinks
# but all services were actually started already. # ipa.service is not yet started but all services were actually started
# already.
class Fedora16IPAService(Fedora16Service): class Fedora16IPAService(Fedora16Service):
def enable(self, instance_name=""): def enable(self, instance_name=""):
super(Fedora16IPAService, self).enable(instance_name) super(Fedora16IPAService, self).enable(instance_name)
@@ -104,7 +121,8 @@ class Fedora16SSHService(Fedora16Service):
def get_config_dir(self, instance_name=""): def get_config_dir(self, instance_name=""):
return '/etc/ssh' return '/etc/ssh'
# Redirect directory server service through special sub-class due to its special handling of instances # Redirect directory server service through special sub-class due to its
# special handling of instances
def f16_service(name): def f16_service(name):
if name == 'dirsrv': if name == 'dirsrv':
return Fedora16DirectoryService(name) return Fedora16DirectoryService(name)
@@ -122,8 +140,13 @@ class Fedora16Services(base.KnownServices):
# Call base class constructor. This will lock services to read-only # Call base class constructor. This will lock services to read-only
super(Fedora16Services, self).__init__(services) super(Fedora16Services, self).__init__(services)
def restore_context(filepath, restorecon='/usr/sbin/restorecon'):
return redhat.restore_context(filepath, restorecon)
def check_selinux_status(restorecon='/usr/sbin/restorecon'):
return redhat.check_selinux_status(restorecon)
authconfig = redhat.authconfig authconfig = redhat.authconfig
service = f16_service service = f16_service
knownservices = Fedora16Services() knownservices = Fedora16Services()
restore_context = redhat.restore_context
backup_and_replace_hostname = redhat.backup_and_replace_hostname backup_and_replace_hostname = redhat.backup_and_replace_hostname

View File

@@ -28,13 +28,22 @@ from ipapython import ipautil
from ipapython.platform import base from ipapython.platform import base
# All what we allow exporting directly from this module # All what we allow exporting directly from this module
# Everything else is made available through these symbols when they directly imported into ipapython.services: # Everything else is made available through these symbols when they are
# authconfig -- class reference for platform-specific implementation of authconfig(8) # directly imported into ipapython.services:
# service -- class reference for platform-specific implementation of a PlatformService class #
# knownservices -- factory instance to access named services IPA cares about, names are ipapython.services.wellknownservices # authconfig -- class reference for platform-specific implementation of
# backup_and_replace_hostname -- platform-specific way to set hostname and make it persistent over reboots # authconfig(8)
# restore_context -- platform-sepcific way to restore security context, if applicable # service -- class reference for platform-specific implementation of a
__all__ = ['authconfig', 'service', 'knownservices', 'backup_and_replace_hostname', 'restore_context'] # PlatformService class
# knownservices -- factory instance to access named services IPA cares about,
# names are ipapython.services.wellknownservices
# backup_and_replace_hostname -- platform-specific way to set hostname and
# make it persistent over reboots
# restore_context -- platform-sepcific way to restore security context, if
# applicable
# check_selinux_status -- platform-specific way to see if SELinux is enabled
# and restorecon is installed.
__all__ = ['authconfig', 'service', 'knownservices', 'backup_and_replace_hostname', 'restore_context', 'check_selinux_status']
class RedHatService(base.PlatformService): class RedHatService(base.PlatformService):
def stop(self, instance_name="", capture_output=True): def stop(self, instance_name="", capture_output=True):
@@ -130,10 +139,10 @@ authconfig = RedHatAuthConfig
service = redhat_service service = redhat_service
knownservices = RedHatServices() knownservices = RedHatServices()
def restore_context(filepath): def restore_context(filepath, restorecon='/sbin/restorecon'):
""" """
restore security context on the file path restore security context on the file path
SELinux equivalent is /sbin/restorecon <filepath> SELinux equivalent is /path/to/restorecon <filepath>
restorecon's return values are not reliable so we have to restorecon's return values are not reliable so we have to
ignore them (BZ #739604). ignore them (BZ #739604).
@@ -150,8 +159,8 @@ def restore_context(filepath):
# selinuxenabled returns 1 if not enabled # selinuxenabled returns 1 if not enabled
return return
if (os.path.exists('/sbin/restorecon')): if (os.path.exists(restorecon)):
ipautil.run(["/sbin/restorecon", filepath], raiseonerr=False) ipautil.run([restorecon, filepath], raiseonerr=False)
def backup_and_replace_hostname(fstore, statestore, hostname): def backup_and_replace_hostname(fstore, statestore, hostname):
old_hostname = socket.gethostname() old_hostname = socket.gethostname()
@@ -168,3 +177,26 @@ def backup_and_replace_hostname(fstore, statestore, hostname):
statestore.backup_state('network', 'hostname', old_values['HOSTNAME']) statestore.backup_state('network', 'hostname', old_values['HOSTNAME'])
else: else:
statestore.backup_state('network', 'hostname', old_hostname) statestore.backup_state('network', 'hostname', old_hostname)
def check_selinux_status(restorecon='/sbin/restorecon'):
"""
We don't have a specific package requirement for policycoreutils
which provides restorecon. This is because we don't require
SELinux on client installs. However if SELinux is enabled then
this package is required.
This function returns nothing but may raise a Runtime exception
if SELinux is enabled but restorecon is not available.
"""
try:
if (os.path.exists('/usr/sbin/selinuxenabled')):
ipautil.run(["/usr/sbin/selinuxenabled"])
else:
# No selinuxenabled, no SELinux
return
except ipautil.CalledProcessError:
# selinuxenabled returns 1 if not enabled
return
if not os.path.exists(restorecon):
raise RuntimeError('SELinux is enabled but %s does not exist.\nInstall the policycoreutils package and start the installation again.' % restorecon)

View File

@@ -32,8 +32,8 @@ def restore_context_default(filepath):
return return
# Restore security context for a path # Restore security context for a path
# If the platform has security features where context is important, implement your own # If the platform has security features where context is important, implement
# version in platform services # your own version in platform services
restore_context = restore_context_default restore_context = restore_context_default
# Default implementation of backup and replace hostname that does nothing # Default implementation of backup and replace hostname that does nothing
@@ -41,8 +41,14 @@ def backup_and_replace_hostname_default(fstore, statestore, hostname):
return return
# Backup and replace system's hostname # Backup and replace system's hostname
# Since many platforms have their own way how to store system's hostname, this method must be # Since many platforms have their own way how to store system's hostname,
# implemented in platform services # this method must be implemented in platform services
backup_and_replace_hostname = backup_and_replace_hostname_default backup_and_replace_hostname = backup_and_replace_hostname_default
# See if SELinux is enabled and /usr/sbin/restorecon is installed.
# Default to a no-op. Those platforms that support SELinux should
# implement this function.
def check_selinux_status():
return
from ipapython.platform.SUPPORTED_PLATFORM import * from ipapython.platform.SUPPORTED_PLATFORM import *