Imported Upstream version 4.0.5
This commit is contained in:
BIN
ipaplatform/redhat/__init__.pyc
Normal file
BIN
ipaplatform/redhat/__init__.pyc
Normal file
Binary file not shown.
@@ -18,12 +18,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import ipautil
|
||||
from ipapython.admintool import ScriptError
|
||||
import os
|
||||
|
||||
FILES_TO_NOT_BACKUP = ['passwd', 'group', 'shadow', 'gshadow']
|
||||
|
||||
|
||||
class RedHatAuthConfig(object):
|
||||
@@ -88,29 +83,4 @@ class RedHatAuthConfig(object):
|
||||
self.add_option("update")
|
||||
|
||||
args = self.build_args()
|
||||
try:
|
||||
ipautil.run([paths.AUTHCONFIG] + args)
|
||||
except ipautil.CalledProcessError:
|
||||
raise ScriptError("Failed to execute authconfig command")
|
||||
|
||||
def backup(self, path):
|
||||
try:
|
||||
ipautil.run([paths.AUTHCONFIG, "--savebackup", path])
|
||||
except ipautil.CalledProcessError:
|
||||
raise ScriptError("Failed to execute authconfig command")
|
||||
|
||||
# do not backup these files since we don't want to mess with
|
||||
# users/groups during restore. Authconfig doesn't seem to mind about
|
||||
# having them deleted from backup dir
|
||||
files_to_remove = [os.path.join(path, f) for f in FILES_TO_NOT_BACKUP]
|
||||
for filename in files_to_remove:
|
||||
try:
|
||||
os.remove(filename)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def restore(self, path):
|
||||
try:
|
||||
ipautil.run([paths.AUTHCONFIG, "--restorebackup", path])
|
||||
except ipautil.CalledProcessError:
|
||||
raise ScriptError("Failed to execute authconfig command")
|
||||
ipautil.run(["/usr/sbin/authconfig"] + args)
|
||||
|
||||
BIN
ipaplatform/redhat/authconfig.pyc
Normal file
BIN
ipaplatform/redhat/authconfig.pyc
Normal file
Binary file not shown.
@@ -1,17 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
'''
|
||||
This Red Hat OS family base platform module exports default platform
|
||||
related constants for the Red Hat OS family-based systems.
|
||||
'''
|
||||
|
||||
# Fallback to default path definitions
|
||||
from ipaplatform.base.constants import BaseConstantsNamespace
|
||||
|
||||
|
||||
class RedHatConstantsNamespace(BaseConstantsNamespace):
|
||||
pass
|
||||
|
||||
constants = RedHatConstantsNamespace()
|
||||
@@ -22,19 +22,12 @@ This Red Hat OS family base platform module exports default filesystem paths as
|
||||
common in Red Hat OS family-based systems.
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
# Fallback to default path definitions
|
||||
from ipaplatform.base.paths import BasePathNamespace
|
||||
|
||||
|
||||
class RedHatPathNamespace(BasePathNamespace):
|
||||
# https://docs.python.org/2/library/platform.html#cross-platform
|
||||
if sys.maxsize > 2**32:
|
||||
LIBSOFTHSM2_SO = BasePathNamespace.LIBSOFTHSM2_SO_64
|
||||
PAM_KRB5_SO = BasePathNamespace.PAM_KRB5_SO_64
|
||||
BIND_LDAP_SO = BasePathNamespace.BIND_LDAP_SO_64
|
||||
AUTHCONFIG = '/usr/sbin/authconfig'
|
||||
pass
|
||||
|
||||
|
||||
paths = RedHatPathNamespace()
|
||||
|
||||
BIN
ipaplatform/redhat/paths.pyc
Normal file
BIN
ipaplatform/redhat/paths.pyc
Normal file
Binary file not shown.
@@ -22,18 +22,17 @@
|
||||
Contains Red Hat OS family-specific service class implementations.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import contextlib
|
||||
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipaplatform.base import services as base_services
|
||||
|
||||
from ipapython import ipautil, dogtag
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipalib import api
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Mappings from service names as FreeIPA code references to these services
|
||||
# to their actual systemd service names
|
||||
|
||||
@@ -57,19 +56,15 @@ redhat_system_units['rpcidmapd'] = 'nfs-idmap.service'
|
||||
# code).
|
||||
|
||||
redhat_system_units['dirsrv'] = 'dirsrv@.service'
|
||||
# Our directory server instance for PKI is dirsrv@PKI-IPA.service
|
||||
redhat_system_units['pkids'] = 'dirsrv@PKI-IPA.service'
|
||||
# Old style PKI instance
|
||||
redhat_system_units['pki-cad'] = 'pki-cad@pki-ca.service'
|
||||
redhat_system_units['pki_cad'] = redhat_system_units['pki-cad']
|
||||
# Our PKI instance is pki-tomcatd@pki-tomcat.service
|
||||
redhat_system_units['pki-tomcatd'] = 'pki-tomcatd@pki-tomcat.service'
|
||||
redhat_system_units['pki_tomcatd'] = redhat_system_units['pki-tomcatd']
|
||||
redhat_system_units['ipa-otpd'] = 'ipa-otpd.socket'
|
||||
redhat_system_units['ipa-dnskeysyncd'] = 'ipa-dnskeysyncd.service'
|
||||
redhat_system_units['named-regular'] = 'named.service'
|
||||
redhat_system_units['named-pkcs11'] = 'named-pkcs11.service'
|
||||
redhat_system_units['named'] = redhat_system_units['named-pkcs11']
|
||||
redhat_system_units['ods-enforcerd'] = 'ods-enforcerd.service'
|
||||
redhat_system_units['ods_enforcerd'] = redhat_system_units['ods-enforcerd']
|
||||
redhat_system_units['ods-signerd'] = 'ods-signerd.service'
|
||||
redhat_system_units['ods_signerd'] = redhat_system_units['ods-signerd']
|
||||
redhat_system_units['gssproxy'] = 'gssproxy.service'
|
||||
|
||||
|
||||
# Service classes that implement Red Hat OS family-specific behaviour
|
||||
@@ -77,7 +72,7 @@ redhat_system_units['gssproxy'] = 'gssproxy.service'
|
||||
class RedHatService(base_services.SystemdService):
|
||||
system_units = redhat_system_units
|
||||
|
||||
def __init__(self, service_name, api=None):
|
||||
def __init__(self, service_name):
|
||||
systemd_name = service_name
|
||||
if service_name in self.system_units:
|
||||
systemd_name = self.system_units[service_name]
|
||||
@@ -87,17 +82,40 @@ class RedHatService(base_services.SystemdService):
|
||||
# and not a foo.target. Thus, not correct service name for
|
||||
# systemd, default to foo.service style then
|
||||
systemd_name = "%s.service" % (service_name)
|
||||
super(RedHatService, self).__init__(service_name, systemd_name, api)
|
||||
super(RedHatService, self).__init__(service_name, systemd_name)
|
||||
|
||||
|
||||
class RedHatDirectoryService(RedHatService):
|
||||
|
||||
def is_installed(self, instance_name):
|
||||
file_path = "{}/{}-{}".format(paths.ETC_DIRSRV, "slapd", instance_name)
|
||||
return os.path.exists(file_path)
|
||||
def tune_nofile_platform(self, num=8192, fstore=None):
|
||||
"""
|
||||
Increase the number of files descriptors available to directory server
|
||||
from the default 1024 to 8192. This will allow to support a greater
|
||||
number of clients out of the box.
|
||||
|
||||
def restart(self, instance_name="", capture_output=True, wait=True,
|
||||
ldapi=False):
|
||||
This is a part of the implementation that is systemd-specific.
|
||||
|
||||
Returns False if the setting of the nofile limit needs to be skipped.
|
||||
"""
|
||||
|
||||
if os.path.exists(paths.SYSCONFIG_DIRSRV_SYSTEMD):
|
||||
# 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 via
|
||||
# /etc/sysconfig/dirsrv.systemd file which is imported by systemd
|
||||
# into dirsrv@.service unit
|
||||
|
||||
replacevars = {'LimitNOFILE': str(num)}
|
||||
ipautil.inifile_replace_variables(paths.SYSCONFIG_DIRSRV_SYSTEMD,
|
||||
'service',
|
||||
replacevars=replacevars)
|
||||
tasks.restore_context(paths.SYSCONFIG_DIRSRV_SYSTEMD)
|
||||
ipautil.run(["/bin/systemctl", "--system", "daemon-reload"],
|
||||
raiseonerr=False)
|
||||
|
||||
return True
|
||||
|
||||
def restart(self, instance_name="", capture_output=True, wait=True):
|
||||
# We need to explicitly enable instances to install proper symlinks as
|
||||
# dirsrv.target.wants/ dependencies. Standard systemd service class does it
|
||||
# on enable() method call. Unfortunately, ipa-server-install does not do
|
||||
@@ -123,35 +141,8 @@ class RedHatDirectoryService(RedHatService):
|
||||
os.unlink(srv_lnk)
|
||||
os.symlink(srv_etc, srv_lnk)
|
||||
|
||||
with self.__wait(instance_name, wait, ldapi) as wait:
|
||||
super(RedHatDirectoryService, self).restart(
|
||||
instance_name, capture_output=capture_output, wait=wait)
|
||||
|
||||
def start(self, instance_name="", capture_output=True, wait=True,
|
||||
ldapi=False):
|
||||
with self.__wait(instance_name, wait, ldapi) as wait:
|
||||
super(RedHatDirectoryService, self).start(
|
||||
instance_name, capture_output=capture_output, wait=wait)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def __wait(self, instance_name, wait, ldapi):
|
||||
if ldapi:
|
||||
instance_name = self.service_instance(instance_name)
|
||||
if instance_name.endswith('.service'):
|
||||
instance_name = instance_name[:-8]
|
||||
if instance_name.startswith('dirsrv'):
|
||||
# this is intentional, return the empty string if the instance
|
||||
# name is 'dirsrv'
|
||||
instance_name = instance_name[7:]
|
||||
if not instance_name:
|
||||
ldapi = False
|
||||
if ldapi:
|
||||
yield False
|
||||
socket_name = paths.SLAPD_INSTANCE_SOCKET_TEMPLATE % instance_name
|
||||
ipautil.wait_for_open_socket(socket_name,
|
||||
self.api.env.startup_timeout)
|
||||
else:
|
||||
yield wait
|
||||
super(RedHatDirectoryService, self).restart(instance_name,
|
||||
capture_output=capture_output, wait=wait)
|
||||
|
||||
|
||||
class RedHatIPAService(RedHatService):
|
||||
@@ -164,21 +155,35 @@ class RedHatIPAService(RedHatService):
|
||||
self.restart(instance_name)
|
||||
|
||||
|
||||
class RedHatSSHService(RedHatService):
|
||||
def get_config_dir(self, instance_name=""):
|
||||
return '/etc/ssh'
|
||||
|
||||
|
||||
class RedHatCAService(RedHatService):
|
||||
def wait_until_running(self):
|
||||
logger.debug('Waiting until the CA is running')
|
||||
timeout = float(self.api.env.startup_timeout)
|
||||
# We must not wait for the httpd proxy if httpd is not set up yet.
|
||||
# Unfortunately, knownservices.httpd.is_installed() can return
|
||||
# false positives, so check for existence of our configuration file.
|
||||
# TODO: Use a cleaner solution
|
||||
use_proxy = True
|
||||
if not (os.path.exists('/etc/httpd/conf.d/ipa.conf') and
|
||||
os.path.exists(paths.HTTPD_IPA_PKI_PROXY_CONF)):
|
||||
root_logger.debug(
|
||||
'The httpd proxy is not installed, wait on local port')
|
||||
use_proxy = False
|
||||
root_logger.debug('Waiting until the CA is running')
|
||||
timeout = float(api.env.startup_timeout)
|
||||
op_timeout = time.time() + timeout
|
||||
while time.time() < op_timeout:
|
||||
try:
|
||||
# check status of CA instance on this host, not remote ca_host
|
||||
status = dogtag.ca_status(self.api.env.host)
|
||||
except Exception as e:
|
||||
status = 'check interrupted due to error: %s' % e
|
||||
logger.debug('The CA status is: %s', status)
|
||||
status = dogtag.ca_status(use_proxy=use_proxy)
|
||||
except Exception:
|
||||
status = 'check interrupted'
|
||||
root_logger.debug('The CA status is: %s' % status)
|
||||
if status == 'running':
|
||||
break
|
||||
logger.debug('Waiting for CA to start...')
|
||||
root_logger.debug('Waiting for CA to start...')
|
||||
time.sleep(1)
|
||||
else:
|
||||
raise RuntimeError('CA did not start in %ss' % timeout)
|
||||
@@ -195,55 +200,38 @@ class RedHatCAService(RedHatService):
|
||||
if wait:
|
||||
self.wait_until_running()
|
||||
|
||||
def is_running(self, instance_name="", wait=True):
|
||||
if instance_name:
|
||||
return super(RedHatCAService, self).is_running(instance_name)
|
||||
try:
|
||||
status = dogtag.ca_status()
|
||||
if status == 'running':
|
||||
return True
|
||||
elif status == 'starting' and wait:
|
||||
# Exception is raised if status is 'starting' even after wait
|
||||
self.wait_until_running()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(
|
||||
'Failed to check CA status: %s', e
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
# Function that constructs proper Red Hat OS family-specific server classes for
|
||||
# services of specified name
|
||||
|
||||
def redhat_service_class_factory(name, api=None):
|
||||
def redhat_service_class_factory(name):
|
||||
if name == 'dirsrv':
|
||||
return RedHatDirectoryService(name, api)
|
||||
return RedHatDirectoryService(name)
|
||||
if name == 'ipa':
|
||||
return RedHatIPAService(name, api)
|
||||
if name in ('pki-tomcatd', 'pki_tomcatd'):
|
||||
return RedHatCAService(name, api)
|
||||
return RedHatService(name, api)
|
||||
return RedHatIPAService(name)
|
||||
if name == 'sshd':
|
||||
return RedHatSSHService(name)
|
||||
if name in ('pki-cad', 'pki_cad', 'pki-tomcatd', 'pki_tomcatd'):
|
||||
return RedHatCAService(name)
|
||||
return RedHatService(name)
|
||||
|
||||
|
||||
# Magicdict containing RedHatService instances.
|
||||
|
||||
class RedHatServices(base_services.KnownServices):
|
||||
def service_class_factory(self, name):
|
||||
return redhat_service_class_factory(name)
|
||||
|
||||
def __init__(self):
|
||||
# pylint: disable=ipa-forbidden-import
|
||||
import ipalib # FixMe: break import cycle
|
||||
# pylint: enable=ipa-forbidden-import
|
||||
services = dict()
|
||||
for s in base_services.wellknownservices:
|
||||
services[s] = self.service_class_factory(s, ipalib.api)
|
||||
services[s] = self.service_class_factory(s)
|
||||
# Call base class constructor. This will lock services to read-only
|
||||
super(RedHatServices, self).__init__(services)
|
||||
|
||||
def service_class_factory(self, name, api=None):
|
||||
return redhat_service_class_factory(name, api)
|
||||
|
||||
# Objects below are expected to be exported by platform module
|
||||
|
||||
timedate_services = base_services.timedate_services
|
||||
from ipaplatform.base.services import timedate_services
|
||||
service = redhat_service_class_factory
|
||||
knownservices = RedHatServices()
|
||||
|
||||
BIN
ipaplatform/redhat/services.pyc
Normal file
BIN
ipaplatform/redhat/services.pyc
Normal file
Binary file not shown.
@@ -23,82 +23,22 @@
|
||||
This module contains default Red Hat OS family-specific implementations of
|
||||
system tasks.
|
||||
'''
|
||||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import socket
|
||||
import traceback
|
||||
import errno
|
||||
import sys
|
||||
|
||||
from ctypes.util import find_library
|
||||
from functools import total_ordering
|
||||
from subprocess import CalledProcessError
|
||||
|
||||
from cffi import FFI
|
||||
from pyasn1.error import PyAsn1Error
|
||||
from six.moves import urllib
|
||||
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipapython import ipautil
|
||||
import ipapython.errors
|
||||
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.redhat.authconfig import RedHatAuthConfig
|
||||
from ipaplatform.base.tasks import BaseTaskNamespace
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_ffi = FFI()
|
||||
_ffi.cdef("""
|
||||
int rpmvercmp (const char *a, const char *b);
|
||||
""")
|
||||
|
||||
# use ctypes loader to get correct librpm.so library version according to
|
||||
# https://cffi.readthedocs.org/en/latest/overview.html#id8
|
||||
_librpm = _ffi.dlopen(find_library("rpm"))
|
||||
|
||||
|
||||
def selinux_enabled():
|
||||
"""
|
||||
Check if SELinux is enabled.
|
||||
"""
|
||||
if os.path.exists(paths.SELINUXENABLED):
|
||||
try:
|
||||
ipautil.run([paths.SELINUXENABLED])
|
||||
return True
|
||||
except ipautil.CalledProcessError:
|
||||
# selinuxenabled returns 1 if not enabled
|
||||
return False
|
||||
else:
|
||||
# No selinuxenabled, no SELinux
|
||||
return False
|
||||
|
||||
|
||||
@total_ordering
|
||||
class IPAVersion(object):
|
||||
|
||||
def __init__(self, version):
|
||||
self._version = version
|
||||
self._bytes = version.encode('utf-8')
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
return self._version
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, IPAVersion):
|
||||
return NotImplemented
|
||||
return _librpm.rpmvercmp(self._bytes, other._bytes) == 0
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, IPAVersion):
|
||||
return NotImplemented
|
||||
return _librpm.rpmvercmp(self._bytes, other._bytes) < 0
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._version)
|
||||
|
||||
|
||||
class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
|
||||
@@ -111,8 +51,14 @@ class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
|
||||
ipautil.run() will do the logging.
|
||||
"""
|
||||
|
||||
if not selinux_enabled():
|
||||
try:
|
||||
if os.path.exists(paths.SELINUXENABLED):
|
||||
ipautil.run([paths.SELINUXENABLED])
|
||||
else:
|
||||
# No selinuxenabled, no SELinux
|
||||
return
|
||||
except ipautil.CalledProcessError:
|
||||
# selinuxenabled returns 1 if not enabled
|
||||
return
|
||||
|
||||
if (os.path.exists(restorecon)):
|
||||
@@ -128,7 +74,14 @@ class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
This function returns nothing but may raise a Runtime exception
|
||||
if SELinux is enabled but restorecon is not available.
|
||||
"""
|
||||
if not selinux_enabled():
|
||||
try:
|
||||
if os.path.exists(paths.SELINUXENABLED):
|
||||
ipautil.run([paths.SELINUXENABLED])
|
||||
else:
|
||||
# No selinuxenabled, no SELinux
|
||||
return
|
||||
except ipautil.CalledProcessError:
|
||||
# selinuxenabled returns 1 if not enabled
|
||||
return
|
||||
|
||||
if not os.path.exists(restorecon):
|
||||
@@ -136,33 +89,6 @@ class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
'Install the policycoreutils package and start '
|
||||
'the installation again.' % restorecon)
|
||||
|
||||
def check_ipv6_stack_enabled(self):
|
||||
"""Checks whether IPv6 kernel module is loaded.
|
||||
|
||||
Function checks if /proc/net/if_inet6 is present. If IPv6 stack is
|
||||
enabled, it exists and contains the interfaces configuration.
|
||||
|
||||
:raises: RuntimeError when IPv6 stack is disabled
|
||||
"""
|
||||
if not os.path.exists(paths.IF_INET6):
|
||||
raise RuntimeError(
|
||||
"IPv6 stack has to be enabled in the kernel and some "
|
||||
"interface has to have ::1 address assigned. Typically "
|
||||
"this is 'lo' interface. If you do not wish to use IPv6 "
|
||||
"globally, disable it on the specific interfaces in "
|
||||
"sysctl.conf except 'lo' interface.")
|
||||
|
||||
try:
|
||||
localhost6 = ipautil.CheckedIPAddress('::1', allow_loopback=True)
|
||||
if localhost6.get_matching_interface() is None:
|
||||
raise ValueError("no interface for ::1 address found")
|
||||
except ValueError:
|
||||
raise RuntimeError(
|
||||
"IPv6 stack is enabled in the kernel but there is no "
|
||||
"interface that has ::1 address assigned. Add ::1 address "
|
||||
"resolution to 'lo' interface. You might need to enable IPv6 "
|
||||
"on the interface 'lo' in sysctl.conf.")
|
||||
|
||||
def restore_pre_ipa_client_configuration(self, fstore, statestore,
|
||||
was_sssd_installed,
|
||||
was_sssd_configured):
|
||||
@@ -223,302 +149,111 @@ class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
auth_config.add_option("nostart")
|
||||
auth_config.execute()
|
||||
|
||||
def backup_auth_configuration(self, path):
|
||||
auth_config = RedHatAuthConfig()
|
||||
auth_config.backup(path)
|
||||
def insert_ca_cert_into_systemwide_ca_store(self, cacert_path):
|
||||
# Add the 'ipa-' prefix to cert name to avoid name collisions
|
||||
cacert_name = os.path.basename(cacert_path)
|
||||
new_cacert_path = os.path.join(paths.SYSTEMWIDE_CA_STORE,
|
||||
'ipa-%s' % cacert_name)
|
||||
|
||||
def restore_auth_configuration(self, path):
|
||||
auth_config = RedHatAuthConfig()
|
||||
auth_config.restore(path)
|
||||
|
||||
def reload_systemwide_ca_store(self):
|
||||
# Add the CA to the systemwide CA trust database
|
||||
try:
|
||||
shutil.copy(cacert_path, new_cacert_path)
|
||||
ipautil.run([paths.UPDATE_CA_TRUST])
|
||||
except CalledProcessError as e:
|
||||
logger.error(
|
||||
"Could not update systemwide CA trust database: %s", e)
|
||||
return False
|
||||
except OSError, e:
|
||||
root_logger.info("Failed to copy %s to %s" % (cacert_path,
|
||||
new_cacert_path))
|
||||
except CalledProcessError, e:
|
||||
root_logger.info("Failed to add CA to the systemwide "
|
||||
"CA trust database: %s" % str(e))
|
||||
else:
|
||||
logger.info("Systemwide CA database updated.")
|
||||
root_logger.info('Added the CA to the systemwide CA trust '
|
||||
'database.')
|
||||
return True
|
||||
|
||||
def insert_ca_certs_into_systemwide_ca_store(self, ca_certs):
|
||||
# pylint: disable=ipa-forbidden-import
|
||||
from ipalib import x509 # FixMe: break import cycle
|
||||
from ipalib.errors import CertificateError
|
||||
# pylint: enable=ipa-forbidden-import
|
||||
return False
|
||||
|
||||
new_cacert_path = paths.SYSTEMWIDE_IPA_CA_CRT
|
||||
def remove_ca_cert_from_systemwide_ca_store(self, cacert_path):
|
||||
# Derive the certificate name in the store
|
||||
cacert_name = os.path.basename(cacert_path)
|
||||
new_cacert_path = os.path.join(paths.SYSTEMWIDE_CA_STORE,
|
||||
'ipa-%s' % cacert_name)
|
||||
|
||||
# Remove CA cert from systemwide store
|
||||
if os.path.exists(new_cacert_path):
|
||||
try:
|
||||
os.remove(new_cacert_path)
|
||||
except OSError as e:
|
||||
logger.error(
|
||||
"Could not remove %s: %s", new_cacert_path, e)
|
||||
ipautil.run([paths.UPDATE_CA_TRUST])
|
||||
except OSError, e:
|
||||
root_logger.error('Could not remove: %s, %s'
|
||||
% (new_cacert_path, str(e)))
|
||||
return False
|
||||
|
||||
new_cacert_path = paths.IPA_P11_KIT
|
||||
|
||||
try:
|
||||
f = open(new_cacert_path, 'w')
|
||||
except IOError as e:
|
||||
logger.info("Failed to open %s: %s", new_cacert_path, e)
|
||||
return False
|
||||
|
||||
f.write("# This file was created by IPA. Do not edit.\n"
|
||||
"\n")
|
||||
|
||||
has_eku = set()
|
||||
for cert, nickname, trusted, _ext_key_usage in ca_certs:
|
||||
try:
|
||||
subject = cert.subject_bytes
|
||||
issuer = cert.issuer_bytes
|
||||
serial_number = cert.serial_number_bytes
|
||||
public_key_info = cert.public_key_info_bytes
|
||||
except (PyAsn1Error, ValueError, CertificateError) as e:
|
||||
logger.warning(
|
||||
"Failed to decode certificate \"%s\": %s", nickname, e)
|
||||
continue
|
||||
|
||||
label = urllib.parse.quote(nickname)
|
||||
subject = urllib.parse.quote(subject)
|
||||
issuer = urllib.parse.quote(issuer)
|
||||
serial_number = urllib.parse.quote(serial_number)
|
||||
public_key_info = urllib.parse.quote(public_key_info)
|
||||
|
||||
obj = ("[p11-kit-object-v1]\n"
|
||||
"class: certificate\n"
|
||||
"certificate-type: x-509\n"
|
||||
"certificate-category: authority\n"
|
||||
"label: \"%(label)s\"\n"
|
||||
"subject: \"%(subject)s\"\n"
|
||||
"issuer: \"%(issuer)s\"\n"
|
||||
"serial-number: \"%(serial_number)s\"\n"
|
||||
"x-public-key-info: \"%(public_key_info)s\"\n" %
|
||||
dict(label=label,
|
||||
subject=subject,
|
||||
issuer=issuer,
|
||||
serial_number=serial_number,
|
||||
public_key_info=public_key_info))
|
||||
if trusted is True:
|
||||
obj += "trusted: true\n"
|
||||
elif trusted is False:
|
||||
obj += "x-distrusted: true\n"
|
||||
obj += "{pem}\n\n".format(
|
||||
pem=cert.public_bytes(x509.Encoding.PEM).decode('ascii'))
|
||||
f.write(obj)
|
||||
|
||||
if (cert.extended_key_usage is not None and
|
||||
public_key_info not in has_eku):
|
||||
try:
|
||||
ext_key_usage = cert.extended_key_usage_bytes
|
||||
except PyAsn1Error as e:
|
||||
logger.warning(
|
||||
"Failed to encode extended key usage for \"%s\": %s",
|
||||
nickname, e)
|
||||
continue
|
||||
value = urllib.parse.quote(ext_key_usage)
|
||||
obj = ("[p11-kit-object-v1]\n"
|
||||
"class: x-certificate-extension\n"
|
||||
"label: \"ExtendedKeyUsage for %(label)s\"\n"
|
||||
"x-public-key-info: \"%(public_key_info)s\"\n"
|
||||
"object-id: 2.5.29.37\n"
|
||||
"value: \"%(value)s\"\n\n" %
|
||||
dict(label=label,
|
||||
public_key_info=public_key_info,
|
||||
value=value))
|
||||
f.write(obj)
|
||||
has_eku.add(public_key_info)
|
||||
|
||||
f.close()
|
||||
|
||||
# Add the CA to the systemwide CA trust database
|
||||
if not self.reload_systemwide_ca_store():
|
||||
return False
|
||||
except CalledProcessError, e:
|
||||
root_logger.error('Could not update systemwide CA trust '
|
||||
'database: %s' % str(e))
|
||||
return False
|
||||
else:
|
||||
root_logger.info('Systemwide CA database updated.')
|
||||
|
||||
return True
|
||||
|
||||
def remove_ca_certs_from_systemwide_ca_store(self):
|
||||
result = True
|
||||
update = False
|
||||
def backup_and_replace_hostname(self, fstore, statestore, hostname):
|
||||
old_hostname = socket.gethostname()
|
||||
try:
|
||||
ipautil.run([paths.BIN_HOSTNAME, hostname])
|
||||
except ipautil.CalledProcessError, e:
|
||||
print >>sys.stderr, ("Failed to set this machine hostname to "
|
||||
"%s (%s)." % (hostname, str(e)))
|
||||
|
||||
# Remove CA cert from systemwide store
|
||||
for new_cacert_path in (paths.IPA_P11_KIT,
|
||||
paths.SYSTEMWIDE_IPA_CA_CRT):
|
||||
if not os.path.exists(new_cacert_path):
|
||||
continue
|
||||
try:
|
||||
os.remove(new_cacert_path)
|
||||
except OSError as e:
|
||||
logger.error(
|
||||
"Could not remove %s: %s", new_cacert_path, e)
|
||||
result = False
|
||||
else:
|
||||
update = True
|
||||
|
||||
if update:
|
||||
if not self.reload_systemwide_ca_store():
|
||||
return False
|
||||
|
||||
return result
|
||||
|
||||
def backup_hostname(self, fstore, statestore):
|
||||
filepath = paths.ETC_HOSTNAME
|
||||
if os.path.exists(filepath):
|
||||
# read old hostname
|
||||
with open(filepath, 'r') as f:
|
||||
for line in f.readlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
# skip comment or empty line
|
||||
continue
|
||||
old_hostname = line
|
||||
break
|
||||
fstore.backup_file(filepath)
|
||||
|
||||
with open(filepath, 'w') as f:
|
||||
f.write("%s\n" % hostname)
|
||||
os.chmod(filepath,
|
||||
stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||
os.chown(filepath, 0, 0)
|
||||
self.restore_context(filepath)
|
||||
|
||||
# store old hostname
|
||||
old_hostname = socket.gethostname()
|
||||
statestore.backup_state('network', 'hostname', old_hostname)
|
||||
|
||||
def restore_hostname(self, fstore, statestore):
|
||||
def restore_network_configuration(self, fstore, statestore):
|
||||
old_filepath = paths.SYSCONFIG_NETWORK
|
||||
old_hostname = statestore.get_state('network', 'hostname')
|
||||
hostname_was_configured = False
|
||||
|
||||
if old_hostname is not None:
|
||||
try:
|
||||
self.set_hostname(old_hostname)
|
||||
except ipautil.CalledProcessError as e:
|
||||
logger.debug("%s", traceback.format_exc())
|
||||
logger.error(
|
||||
"Failed to restore this machine hostname to %s (%s).",
|
||||
old_hostname, e
|
||||
)
|
||||
if fstore.has_file(old_filepath):
|
||||
# This is Fedora >=18 instance that was upgraded from previous
|
||||
# Fedora version which held network configuration
|
||||
# in /etc/sysconfig/network
|
||||
old_filepath_restore = paths.SYSCONFIG_NETWORK_IPABKP
|
||||
fstore.restore_file(old_filepath, old_filepath_restore)
|
||||
print "Deprecated configuration file '%s' was restored to '%s'" \
|
||||
% (old_filepath, old_filepath_restore)
|
||||
hostname_was_configured = True
|
||||
|
||||
filepath = paths.ETC_HOSTNAME
|
||||
if fstore.has_file(filepath):
|
||||
fstore.restore_file(filepath)
|
||||
hostname_was_configured = True
|
||||
|
||||
|
||||
def set_selinux_booleans(self, required_settings, backup_func=None):
|
||||
def get_setsebool_args(changes):
|
||||
args = [paths.SETSEBOOL, "-P"]
|
||||
args.extend(["%s=%s" % update for update in changes.items()])
|
||||
|
||||
return args
|
||||
|
||||
if not selinux_enabled():
|
||||
return False
|
||||
|
||||
updated_vars = {}
|
||||
failed_vars = {}
|
||||
for setting, state in required_settings.items():
|
||||
if state is None:
|
||||
continue
|
||||
if not hostname_was_configured and old_hostname:
|
||||
# hostname was not configured before but was set by IPA. Delete
|
||||
# /etc/hostname to restore previous configuration
|
||||
try:
|
||||
result = ipautil.run(
|
||||
[paths.GETSEBOOL, setting],
|
||||
capture_output=True
|
||||
)
|
||||
original_state = result.output.split()[2]
|
||||
if backup_func is not None:
|
||||
backup_func(setting, original_state)
|
||||
|
||||
if original_state != state:
|
||||
updated_vars[setting] = state
|
||||
except ipautil.CalledProcessError as e:
|
||||
logger.error("Cannot get SELinux boolean '%s': %s", setting, e)
|
||||
failed_vars[setting] = state
|
||||
|
||||
if updated_vars:
|
||||
args = get_setsebool_args(updated_vars)
|
||||
try:
|
||||
ipautil.run(args)
|
||||
except ipautil.CalledProcessError:
|
||||
failed_vars.update(updated_vars)
|
||||
|
||||
if failed_vars:
|
||||
raise ipapython.errors.SetseboolError(
|
||||
failed=failed_vars,
|
||||
command=' '.join(get_setsebool_args(failed_vars)))
|
||||
|
||||
return True
|
||||
|
||||
def parse_ipa_version(self, version):
|
||||
"""
|
||||
:param version: textual version
|
||||
:return: object implementing proper __cmp__ method for version compare
|
||||
"""
|
||||
return IPAVersion(version)
|
||||
|
||||
def configure_httpd_service_ipa_conf(self):
|
||||
"""Create systemd config for httpd service to work with IPA
|
||||
"""
|
||||
if not os.path.exists(paths.SYSTEMD_SYSTEM_HTTPD_D_DIR):
|
||||
os.mkdir(paths.SYSTEMD_SYSTEM_HTTPD_D_DIR, 0o755)
|
||||
|
||||
ipautil.copy_template_file(
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR, 'ipa-httpd.conf.template'),
|
||||
paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF,
|
||||
dict(
|
||||
KDCPROXY_CONFIG=paths.KDCPROXY_CONFIG,
|
||||
IPA_HTTPD_KDCPROXY=paths.IPA_HTTPD_KDCPROXY,
|
||||
KRB5CC_HTTPD=paths.KRB5CC_HTTPD,
|
||||
)
|
||||
)
|
||||
|
||||
os.chmod(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, 0o644)
|
||||
self.restore_context(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF)
|
||||
|
||||
ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"],
|
||||
raiseonerr=False)
|
||||
|
||||
def configure_http_gssproxy_conf(self, ipaapi_user):
|
||||
ipautil.copy_template_file(
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR, 'gssproxy.conf.template'),
|
||||
paths.GSSPROXY_CONF,
|
||||
dict(
|
||||
HTTP_KEYTAB=paths.HTTP_KEYTAB,
|
||||
HTTP_CCACHE=paths.HTTP_CCACHE,
|
||||
HTTPD_USER=constants.HTTPD_USER,
|
||||
IPAAPI_USER=ipaapi_user,
|
||||
)
|
||||
)
|
||||
|
||||
os.chmod(paths.GSSPROXY_CONF, 0o600)
|
||||
self.restore_context(paths.GSSPROXY_CONF)
|
||||
|
||||
def remove_httpd_service_ipa_conf(self):
|
||||
"""Remove systemd config for httpd service of IPA"""
|
||||
try:
|
||||
os.unlink(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
logger.debug(
|
||||
'Trying to remove %s but file does not exist',
|
||||
paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
'Error removing %s: %s',
|
||||
paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, e
|
||||
)
|
||||
return
|
||||
|
||||
ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"],
|
||||
raiseonerr=False)
|
||||
|
||||
def set_hostname(self, hostname):
|
||||
ipautil.run([paths.BIN_HOSTNAMECTL, 'set-hostname', hostname])
|
||||
|
||||
def is_fips_enabled(self):
|
||||
"""
|
||||
Checks whether this host is FIPS-enabled.
|
||||
|
||||
Returns a boolean indicating if the host is FIPS-enabled, i.e. if the
|
||||
file /proc/sys/crypto/fips_enabled contains a non-0 value. Otherwise,
|
||||
or if the file /proc/sys/crypto/fips_enabled does not exist,
|
||||
the function returns False.
|
||||
"""
|
||||
try:
|
||||
with open(paths.PROC_FIPS_ENABLED, 'r') as f:
|
||||
if f.read().strip() != '0':
|
||||
return True
|
||||
except IOError:
|
||||
# Consider that the host is not fips-enabled if the file does not
|
||||
# exist
|
||||
pass
|
||||
return False
|
||||
os.remove(filepath)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
tasks = RedHatTaskNamespace()
|
||||
|
||||
BIN
ipaplatform/redhat/tasks.pyc
Normal file
BIN
ipaplatform/redhat/tasks.pyc
Normal file
Binary file not shown.
Reference in New Issue
Block a user