280 lines
9.7 KiB
Python
280 lines
9.7 KiB
Python
|
#
|
||
|
# Kickstart module for security.
|
||
|
#
|
||
|
# Copyright (C) 2018 Red Hat, Inc.
|
||
|
#
|
||
|
# This copyrighted material is made available to anyone wishing to use,
|
||
|
# modify, copy, or redistribute it subject to the terms and conditions of
|
||
|
# the GNU General Public License v.2, or (at your option) any later version.
|
||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
# ANY WARRANTY expressed or implied, including the implied warranties of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||
|
# Public License for more details. You should have received a copy of the
|
||
|
# GNU General Public License along with this program; if not, write to the
|
||
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
||
|
# source code or documentation are not subject to the GNU General Public
|
||
|
# License and may only be used or replicated with the express permission of
|
||
|
# Red Hat, Inc.
|
||
|
#
|
||
|
import shlex
|
||
|
|
||
|
from pyanaconda.core.configuration.anaconda import conf
|
||
|
from pyanaconda.core.dbus import DBus
|
||
|
from pyanaconda.core.kernel import kernel_arguments
|
||
|
from pyanaconda.core.signal import Signal
|
||
|
from pyanaconda.modules.common.base import KickstartService
|
||
|
from pyanaconda.modules.common.constants.services import SECURITY
|
||
|
from pyanaconda.modules.common.containers import TaskContainer
|
||
|
from pyanaconda.modules.common.structures.realm import RealmData
|
||
|
from pyanaconda.modules.common.structures.requirement import Requirement
|
||
|
from pyanaconda.modules.security.constants import SELinuxMode
|
||
|
from pyanaconda.modules.security.kickstart import SecurityKickstartSpecification
|
||
|
from pyanaconda.modules.security.security_interface import SecurityInterface
|
||
|
from pyanaconda.modules.security.installation import ConfigureSELinuxTask, \
|
||
|
RealmDiscoverTask, RealmJoinTask, ConfigureAuthselectTask, \
|
||
|
ConfigureFingerprintAuthTask, PreconfigureFIPSTask, ConfigureFIPSTask
|
||
|
|
||
|
from pyanaconda.anaconda_loggers import get_module_logger
|
||
|
log = get_module_logger(__name__)
|
||
|
|
||
|
|
||
|
class SecurityService(KickstartService):
|
||
|
"""The Security service."""
|
||
|
|
||
|
def __init__(self):
|
||
|
super().__init__()
|
||
|
|
||
|
self.selinux_changed = Signal()
|
||
|
self._selinux = SELinuxMode.DEFAULT
|
||
|
|
||
|
self.authselect_changed = Signal()
|
||
|
self._authselect_args = []
|
||
|
|
||
|
self.fingerprint_auth_enabled_changed = Signal()
|
||
|
self._fingerprint_auth_enabled = False
|
||
|
|
||
|
self.realm_changed = Signal()
|
||
|
self._realm = RealmData()
|
||
|
|
||
|
def publish(self):
|
||
|
"""Publish the module."""
|
||
|
TaskContainer.set_namespace(SECURITY.namespace)
|
||
|
DBus.publish_object(SECURITY.object_path, SecurityInterface(self))
|
||
|
DBus.register_service(SECURITY.service_name)
|
||
|
|
||
|
@property
|
||
|
def kickstart_specification(self):
|
||
|
"""Return the kickstart specification."""
|
||
|
return SecurityKickstartSpecification
|
||
|
|
||
|
def process_kickstart(self, data):
|
||
|
"""Process the kickstart data."""
|
||
|
if data.selinux.selinux is not None:
|
||
|
self.set_selinux(SELinuxMode(data.selinux.selinux))
|
||
|
|
||
|
if data.authselect.authselect:
|
||
|
self.set_authselect(shlex.split(data.authselect.authselect))
|
||
|
|
||
|
if data.realm.join_realm:
|
||
|
realm = RealmData()
|
||
|
realm.name = data.realm.join_realm
|
||
|
realm.discover_options = data.realm.discover_options
|
||
|
realm.join_options = data.realm.join_args
|
||
|
|
||
|
self.set_realm(realm)
|
||
|
|
||
|
def setup_kickstart(self, data):
|
||
|
"""Set up the kickstart data."""
|
||
|
if self.selinux != SELinuxMode.DEFAULT:
|
||
|
data.selinux.selinux = self.selinux.value
|
||
|
|
||
|
if self.authselect:
|
||
|
data.authselect.authselect = " ".join(self.authselect)
|
||
|
|
||
|
if self.realm.name:
|
||
|
data.realm.join_realm = self.realm.name
|
||
|
data.realm.discover_options = self.realm.discover_options
|
||
|
data.realm.join_args = self.realm.join_options
|
||
|
|
||
|
@property
|
||
|
def fips_enabled(self):
|
||
|
"""Is FIPS enabled?
|
||
|
|
||
|
:return: True or False
|
||
|
"""
|
||
|
return kernel_arguments.is_enabled("fips")
|
||
|
|
||
|
@property
|
||
|
def selinux(self):
|
||
|
"""The state of SELinux on the installed system.
|
||
|
|
||
|
:return: an instance of SELinuxMode
|
||
|
"""
|
||
|
return self._selinux
|
||
|
|
||
|
def set_selinux(self, value):
|
||
|
"""Sets the state of SELinux on the installed system.
|
||
|
|
||
|
:param value: an instance of SELinuxMode
|
||
|
"""
|
||
|
self._selinux = value
|
||
|
self.selinux_changed.emit()
|
||
|
log.debug("SElinux is set to %s.", value)
|
||
|
|
||
|
@property
|
||
|
def authselect(self):
|
||
|
"""Arguments for the authselect tool.
|
||
|
|
||
|
:return: a list of arguments
|
||
|
"""
|
||
|
return self._authselect_args
|
||
|
|
||
|
def set_authselect(self, args):
|
||
|
"""Set the arguments for the authselect tool.
|
||
|
|
||
|
:param args: a list of arguments
|
||
|
"""
|
||
|
self._authselect_args = args
|
||
|
self.authselect_changed.emit()
|
||
|
log.debug("Authselect is set to %s.", args)
|
||
|
|
||
|
@property
|
||
|
def fingerprint_auth_enabled(self):
|
||
|
"""Specifies if fingerprint authentication should be enabled.
|
||
|
|
||
|
:return: True if fingerprint authentication should be enabled, False otherwise
|
||
|
:rtype: bool
|
||
|
"""
|
||
|
return self._fingerprint_auth_enabled
|
||
|
|
||
|
def set_fingerprint_auth_enabled(self, fingerprint_auth_enabled):
|
||
|
"""Set if fingerprint authentication should be enabled.
|
||
|
|
||
|
:param bool fingerprint_auth_enabled: True if fingerprint authentication
|
||
|
should be enabled, False otherwise
|
||
|
"""
|
||
|
self._fingerprint_auth_enabled = fingerprint_auth_enabled
|
||
|
self.fingerprint_auth_enabled_changed.emit()
|
||
|
log.debug("Fingerprint authentication enabled is set to %s.",
|
||
|
self.fingerprint_auth_enabled)
|
||
|
|
||
|
@property
|
||
|
def realm(self):
|
||
|
"""Specification of the enrollment in a realm.
|
||
|
|
||
|
:return: an instance of RealmData
|
||
|
"""
|
||
|
return self._realm
|
||
|
|
||
|
def set_realm(self, realm):
|
||
|
"""Specify of the enrollment in a realm.
|
||
|
|
||
|
:param realm: an instance of RealmData
|
||
|
"""
|
||
|
self._realm = realm
|
||
|
self.realm_changed.emit()
|
||
|
log.debug("Realm is set to %s.", realm)
|
||
|
|
||
|
def handle_realm_discover_results(self, realm_data):
|
||
|
""" Handle results from the RealmDiscover task.
|
||
|
|
||
|
:param realm_data: an updated instance of realm data
|
||
|
"""
|
||
|
log.debug("Updating realm data with results from realm discover task.")
|
||
|
self.set_realm(realm_data)
|
||
|
|
||
|
def collect_requirements(self):
|
||
|
"""Return installation requirements for this module.
|
||
|
|
||
|
:return: a list of requirements
|
||
|
"""
|
||
|
requirements = []
|
||
|
|
||
|
# Add FIPS requirements.
|
||
|
if self.fips_enabled:
|
||
|
requirements.append(Requirement.for_package(
|
||
|
"/usr/bin/fips-mode-setup",
|
||
|
reason="Required for FIPS compliance."
|
||
|
))
|
||
|
|
||
|
# Add realm requirements.
|
||
|
for name in self.realm.required_packages:
|
||
|
requirements.append(Requirement.for_package(
|
||
|
name, reason="Needed to join a realm."
|
||
|
))
|
||
|
|
||
|
# Add authselect requirements
|
||
|
if self.authselect or self.fingerprint_auth_enabled:
|
||
|
# we need the authselect package in two cases:
|
||
|
# - autselect command is used in kickstart
|
||
|
# - to configure fingerprint authentication
|
||
|
requirements.append(Requirement.for_package(
|
||
|
"authselect",
|
||
|
reason="Needed by authselect kickstart command & "
|
||
|
"for fingerprint authentication support."
|
||
|
))
|
||
|
|
||
|
return requirements
|
||
|
|
||
|
def discover_realm_with_task(self):
|
||
|
"""Return the setup task for discovering a realm."""
|
||
|
realm_task = RealmDiscoverTask(sysroot=conf.target.system_root,
|
||
|
realm_data=self.realm)
|
||
|
|
||
|
realm_task.succeeded_signal.connect(
|
||
|
lambda: self.handle_realm_discover_results(realm_task.get_result())
|
||
|
)
|
||
|
return realm_task
|
||
|
|
||
|
def join_realm_with_task(self):
|
||
|
"""Return the setup task for joining a realm."""
|
||
|
realm_task = RealmJoinTask(sysroot=conf.target.system_root, realm_data=self.realm)
|
||
|
|
||
|
# Connect to realm-data-changed signal, so that the realm data in the
|
||
|
# realm-join task is always up to date.
|
||
|
self.realm_changed.connect(lambda: realm_task.set_realm_data(self.realm))
|
||
|
return realm_task
|
||
|
|
||
|
def preconfigure_fips_with_task(self, payload_type):
|
||
|
"""Set up FIPS for the payload installation with a task.
|
||
|
|
||
|
:param payload_type: a string with the payload type
|
||
|
:return: an installation task
|
||
|
"""
|
||
|
return PreconfigureFIPSTask(
|
||
|
sysroot=conf.target.system_root,
|
||
|
payload_type=payload_type,
|
||
|
fips_enabled=self.fips_enabled
|
||
|
)
|
||
|
|
||
|
def configure_fips_with_task(self):
|
||
|
"""Configure FIPS on the installed system.
|
||
|
|
||
|
:return: an installation task
|
||
|
"""
|
||
|
return ConfigureFIPSTask(
|
||
|
sysroot=conf.target.system_root,
|
||
|
fips_enabled=self.fips_enabled
|
||
|
)
|
||
|
|
||
|
def install_with_tasks(self):
|
||
|
"""Return the installation tasks of this module.
|
||
|
|
||
|
:returns: list of installation tasks
|
||
|
"""
|
||
|
return [
|
||
|
ConfigureSELinuxTask(
|
||
|
sysroot=conf.target.system_root,
|
||
|
selinux_mode=self.selinux
|
||
|
),
|
||
|
ConfigureFingerprintAuthTask(
|
||
|
sysroot=conf.target.system_root,
|
||
|
fingerprint_auth_enabled=self.fingerprint_auth_enabled
|
||
|
),
|
||
|
ConfigureAuthselectTask(
|
||
|
sysroot=conf.target.system_root,
|
||
|
authselect_options=self.authselect
|
||
|
)
|
||
|
]
|