A fix for https://fedorahosted.org/freeipa/ticket/4157
This depends on my patches 0631-0632 (for backup/restore integration tests).
Our setsebool code was repeated a few times. Instead of adding another
copy, I refactored what we have into a platform task.
I fixed two old setsebool tickets while I was at it:
https://fedorahosted.org/freeipa/ticket/2519
https://fedorahosted.org/freeipa/ticket/2934
Since ipaplatform should not depend on ipalib, and I needed a new
exception type, I added a new module, ipapython.errors. This might not
be the best name, since it could be confused with ipalib.errors.
Opinions welcome.
As for the second patch: ideally, rather than what I do with `if
'ADTRUST' in self.backup_services`, we'd get the list of booleans
directly from the *instance modules, or even tell the individual
services to restore themselves. But, that refactoring looks like too
much to do now.
--
Petr³
From 7911d8a900965226ff9c32e992bd2cd31ac049b2 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pvikt...@redhat.com>
Date: Thu, 14 Aug 2014 17:14:07 +0200
Subject: [PATCH] Move setting SELinux booleans to platform code
Create a platform task for setting SELinux booleans.
Use an exception for the case when the booleans could not be set
(since this is an error if not handled).
Since ipaplatform should not depend on ipalib, create a new
errors module in ipapython for SetseboolError.
Handle uninstallation with the same task, which means
the booleans are now restored with a single call to
setsebool.
Preparation for: https://fedorahosted.org/freeipa/ticket/4157
Fixes: https://fedorahosted.org/freeipa/ticket/2934
Fixes: https://fedorahosted.org/freeipa/ticket/2519
---
ipaplatform/base/tasks.py | 19 ++++++++
ipaplatform/fedora/tasks.py | 53 ++++++++++++++++++++-
ipapython/errors.py | 47 +++++++++++++++++++
ipaserver/install/adtrustinstance.py | 62 +++++++------------------
ipaserver/install/httpinstance.py | 90 ++++++++----------------------------
5 files changed, 152 insertions(+), 119 deletions(-)
create mode 100644 ipapython/errors.py
diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
index a4ef0ded002d2e3e93fa30b7ce5078ed3604c937..408447e43cd36d0cdf11a1877b3bc9880c4785de 100644
--- a/ipaplatform/base/tasks.py
+++ b/ipaplatform/base/tasks.py
@@ -132,4 +132,23 @@ def modify_pam_to_use_krb5(self, statestore):
return
+ def set_selinux_booleans(self, required_settings, backup_func=None):
+ """Set the specified SELinux booleans
+
+ :param required_settings: A dictionary mapping the boolean names
+ to desired_values.
+ The desired value can be 'on' or 'off'.
+
+ :param backup_func: A function called for each boolean with two
+ arguments: the name and the previous value
+
+ If SELinux is disabled, return False; on success returns True.
+
+ If setting the booleans fails,
+ an ipapython.errors.SetseboolError is raised.
+ """
+
+ return
+
+
task_namespace = BaseTaskNamespace()
diff --git a/ipaplatform/fedora/tasks.py b/ipaplatform/fedora/tasks.py
index 926c0ea6664c7da6fcbec5bcc184750cbfa2a995..9f4a76b8208cc78c330dc022730c4faac09995f9 100644
--- a/ipaplatform/fedora/tasks.py
+++ b/ipaplatform/fedora/tasks.py
@@ -24,7 +24,6 @@
'''
import os
-import shutil
import stat
import socket
import sys
@@ -35,8 +34,9 @@
from nss.error import NSPRError
from pyasn1.error import PyAsn1Error
-from ipapython.ipa_log_manager import root_logger
+from ipapython.ipa_log_manager import root_logger, log_mgr
from ipapython import ipautil
+import ipapython.errors
from ipalib import x509 # FIXME: do not import from ipalib
@@ -45,6 +45,9 @@
from ipaplatform.base.tasks import BaseTaskNamespace
+log = log_mgr.get_logger(__name__)
+
+
class FedoraTaskNamespace(BaseTaskNamespace):
def restore_context(self, filepath, restorecon=paths.SBIN_RESTORECON):
@@ -326,4 +329,50 @@ def restore_network_configuration(self, fstore, statestore):
except OSError:
pass
+ 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.iteritems()])
+
+ return args
+
+ if (os.path.exists(paths.SELINUXENABLED)):
+ try:
+ ipautil.run([paths.SELINUXENABLED])
+ except ipautil.CalledProcessError:
+ # selinuxenabled returns 1 if not enabled
+ return False
+ else:
+ return False
+
+ updated_vars = {}
+ failed_vars = {}
+ for setting, state in required_settings.iteritems():
+ try:
+ (stdout, stderr, rc) = ipautil.run([paths.GETSEBOOL, setting])
+ original_state = stdout.split()[2]
+ if backup_func is not None:
+ backup_func(setting, original_state)
+
+ if original_state != state:
+ updated_vars[setting] = state
+ except ipautil.CalledProcessError, e:
+ log.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
+
+
tasks = FedoraTaskNamespace()
diff --git a/ipapython/errors.py b/ipapython/errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..9fc28359cf69d24fda3668aa2bfce508ad2b90fc
--- /dev/null
+++ b/ipapython/errors.py
@@ -0,0 +1,47 @@
+# Authors: Petr Viktorin <pvikt...@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty 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, see <http://www.gnu.org/licenses/>.
+#
+
+
+class SetseboolError(StandardError):
+ """Raised when setting a SELinux boolean fails
+
+ :param failed: Dictionary mapping boolean names to intended values
+ to their intended values, for booleans that cound not be set
+ :param command: Command the user can run to set the booleans
+
+ The initializer arguments are copied to attributes of the same name.
+ """
+ def __init__(self, failed, command):
+ message = "Could not set SELinux booleans: %s" % ' '.join(
+ '%s=%s' % (name, value) for name, value in failed.items())
+ super(SetseboolError, self).__init__(message)
+ self.failed = failed
+ self.command = command
+
+ def format_service_warning(self, service_name):
+ """Format warning for display when this is raised from service install
+ """
+ return '\n'.join([
+ 'WARNING: %(err)s',
+ '',
+ 'The %(service)s may not function correctly until ',
+ 'the booleans are successfully changed with the command:',
+ ' %(cmd)s',
+ 'Try updating the policycoreutils and selinux-policy packages.'
+ ]) % {'err': self, 'service': service_name, 'cmd': self.command}
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index 38b0801314c3c98438ce4d07cdb54059dde94ce9..4ba14d4a45dfe8788100fc4bc2a619d0250aac13 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -36,23 +36,17 @@
from ipapython.dn import DN
from ipapython import sysrestore
from ipapython import ipautil
-from ipapython.ipa_log_manager import *
+from ipapython.ipa_log_manager import root_logger
+import ipapython.errors
import ipaclient.ipachangeconf
from ipaplatform import services
from ipaplatform.paths import paths
+from ipaplatform.tasks import tasks
ALLOWED_NETBIOS_CHARS = string.ascii_uppercase + string.digits
-SELINUX_WARNING = """
-WARNING: could not set selinux boolean(s) %(var)s to true. The adtrust
-service may not function correctly until this boolean is successfully
-change with the command:
- /usr/sbin/setsebool -P %(var)s true
-Try updating the policycoreutils and selinux-policy packages.
-"""
-
UPGRADE_ERROR = """
Entry %(dn)s does not exist.
This means upgrade from IPA 2.x to 3.x did not went well and required S4U2Proxy
@@ -60,6 +54,9 @@
and re-run ipa-adtrust-instal again afterwards.
"""
+SELINUX_BOOLEAN_SETTINGS = {'samba_portmapper': 'on'}
+
+
def check_inst():
for smbfile in [paths.SMBD, paths.NET]:
if not os.path.exists(smbfile):
@@ -148,7 +145,6 @@ def __setup_default_attributes(self):
# Constants
self.smb_conf = paths.SMB_CONF
self.samba_keytab = paths.SAMBA_KEYTAB
- self.selinux_booleans = ["samba_portmapper"]
self.cifs_hosts = []
# Values obtained from API.env
@@ -611,35 +607,11 @@ def __add_dns_service_records(self):
add_rr(zone, win_srv, "SRV", rec)
def __configure_selinux_for_smbd(self):
- selinux = False
try:
- if (os.path.exists(paths.SELINUXENABLED)):
- ipautil.run([paths.SELINUXENABLED])
- selinux = True
- except ipautil.CalledProcessError:
- # selinuxenabled returns 1 if not enabled
- pass
-
- if selinux:
- # Don't assume all booleans are available
- sebools = []
- for var in self.selinux_booleans:
- try:
- (stdout, stderr, returncode) = ipautil.run([paths.GETSEBOOL, var])
- if stdout and not stderr and returncode == 0:
- self.backup_state(var, stdout.split()[2])
- sebools.append(var)
- except:
- pass
-
- if sebools:
- bools = [var + "=true" for var in sebools]
- args = [paths.SETSEBOOL, "-P"]
- args.extend(bools);
- try:
- ipautil.run(args)
- except:
- self.print_msg(SELINUX_WARNING % dict(var=','.join(sebools)))
+ tasks.set_selinux_booleans(SELINUX_BOOLEAN_SETTINGS,
+ self.backup_state)
+ except ipapython.errors.SetseboolError as e:
+ self.print_msg(e.format_service_warning('adtrust service'))
def __mod_krb5_conf(self):
"""
@@ -909,14 +881,12 @@ def uninstall(self):
# we should not restore smb.conf
# Restore the state of affected selinux booleans
- for var in self.selinux_booleans:
- sebool_state = self.restore_state(var)
- if not sebool_state is None:
- try:
- ipautil.run([paths.SETSEBOOL,
- "-P", var, sebool_state])
- except Exception:
- self.print_msg(SELINUX_WARNING % dict(var=var))
+ boolean_states = {name: self.restore_state(name)
+ for name in SELINUX_BOOLEAN_SETTINGS}
+ try:
+ tasks.set_selinux_booleans(boolean_states)
+ except ipapython.errors.SetseboolError as e:
+ self.print_msg('WARNING: ' + str(e))
# Remove samba's credentials cache
krb5cc_samba = paths.KRB5CC_SAMBA
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 830ea486fba5ffd914e6c8d2815a01636fdb25bf..c819115a3c9ef5a2dd12e990467c1daea95dc78b 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -22,7 +22,6 @@
import tempfile
import pwd
import shutil
-import stat
import re
import service
@@ -31,12 +30,18 @@
from ipapython import sysrestore
from ipapython import ipautil
from ipapython import dogtag
-from ipapython.ipa_log_manager import *
+from ipapython.ipa_log_manager import root_logger
+import ipapython.errors
from ipaserver.install import sysupgrade
from ipalib import api
from ipaplatform.tasks import tasks
from ipaplatform.paths import paths
-from ipalib.constants import CACERT
+
+
+SELINUX_BOOLEAN_SETTINGS = dict(
+ httpd_can_network_connect='on',
+ httpd_manage_ipa='on',
+)
def httpd_443_configured():
@@ -135,67 +140,11 @@ def __enable(self):
self.ldap_enable('HTTP', self.fqdn, self.dm_password, self.suffix)
def configure_selinux_for_httpd(self):
- def get_setsebool_args(changes):
- if len(changes) == 1:
- # workaround https://bugzilla.redhat.com/show_bug.cgi?id=825163
- updates = changes.items()[0]
- else:
- updates = ["%s=%s" % update for update in changes.iteritems()]
-
- args = [paths.SETSEBOOL, "-P"]
- args.extend(updates)
-
- return args
-
- selinux = False
try:
- if (os.path.exists(paths.SELINUXENABLED)):
- ipautil.run([paths.SELINUXENABLED])
- selinux = True
- except ipautil.CalledProcessError:
- # selinuxenabled returns 1 if not enabled
- pass
-
- if selinux:
- # Don't assume all vars are available
- updated_vars = {}
- failed_vars = {}
- required_settings = (("httpd_can_network_connect", "on"),
- ("httpd_manage_ipa", "on"))
- for setting, state in required_settings:
- try:
- (stdout, stderr, returncode) = ipautil.run([paths.GETSEBOOL, setting])
- original_state = stdout.split()[2]
- self.backup_state(setting, original_state)
-
- if original_state != state:
- updated_vars[setting] = state
- except ipautil.CalledProcessError, e:
- root_logger.debug("Cannot get SELinux boolean '%s': %s", setting, e)
- failed_vars[setting] = state
-
- # Allow apache to connect to the dogtag UI and the session cache
- # This can still fail even if selinux is enabled. Execute these
- # together so it is speedier.
- if updated_vars:
- args = get_setsebool_args(updated_vars)
- try:
- ipautil.run(args)
- except ipautil.CalledProcessError:
- failed_vars.update(updated_vars)
-
- if failed_vars:
- args = get_setsebool_args(failed_vars)
- names = [update[0] for update in updated_vars]
- message = ['WARNING: could not set the following SELinux boolean(s):']
- for update in failed_vars.iteritems():
- message.append(' %s -> %s' % update)
- message.append('The web interface may not function correctly until the booleans')
- message.append('are successfully changed with the command:')
- message.append(' '.join(args))
- message.append('Try updating the policycoreutils and selinux-policy packages.')
-
- self.print_msg("\n".join(message))
+ tasks.set_selinux_booleans(SELINUX_BOOLEAN_SETTINGS,
+ self.backup_state)
+ except ipapython.errors.SetseboolError as e:
+ self.print_msg(e.format_service_warning('web interface'))
def __create_http_keytab(self):
installutils.kadmin_addprinc(self.principal)
@@ -421,14 +370,13 @@ def uninstall(self):
installutils.remove_file(paths.HTTPD_IPA_CONF)
installutils.remove_file(paths.HTTPD_IPA_PKI_PROXY_CONF)
- for var in ["httpd_can_network_connect", "httpd_manage_ipa"]:
- sebool_state = self.restore_state(var)
- if not sebool_state is None:
- try:
- ipautil.run([paths.SETSEBOOL, "-P", var, sebool_state])
- except ipautil.CalledProcessError, e:
- self.print_msg("Cannot restore SELinux boolean '%s' back to '%s': %s" \
- % (var, sebool_state, e))
+ # Restore SELinux boolean states
+ boolean_states = {name: self.restore_state(name)
+ for name in SELINUX_BOOLEAN_SETTINGS}
+ try:
+ tasks.set_selinux_booleans(boolean_states)
+ except ipapython.errors.SetseboolError as e:
+ self.print_msg('WARNING: ' + str(e))
if not running is None and running:
self.start()
--
1.9.3
From f44a501e5fc51e12ab58da753941596a973ee36a Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pvikt...@redhat.com>
Date: Thu, 7 Aug 2014 11:09:38 +0200
Subject: [PATCH] ipa-restore: Set SELinux booleans when restoring
https://fedorahosted.org/freeipa/ticket/4157
---
ipaserver/install/ipa_restore.py | 14 +++++++++++
.../test_integration/test_backup_and_restore.py | 29 ++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
index e230f0aa3d7ef9b3ae5585e0628b15ffc8246866..239de99c462639854e8e25c6b9278cb94b6fc6b8 100644
--- a/ipaserver/install/ipa_restore.py
+++ b/ipaserver/install/ipa_restore.py
@@ -36,7 +36,10 @@
from ipaserver.install.replication import (wait_for_task, ReplicationManager,
get_cs_replication_manager)
from ipaserver.install import installutils
+from ipaserver.install import httpinstance
+from ipaserver.install import adtrustinstance
from ipapython import ipaldap
+import ipapython.errors
from ipaplatform.tasks import tasks
from ipaserver.install.ipa_backup import BACKUP_DIR
from ipaplatform import services
@@ -262,6 +265,8 @@ def run(self):
if rc not in [0, 6]:
self.log.warn('Stopping IPA failed: %s' % stderr)
+ self.restore_selinux_booleans()
+
# We do either a full file restore or we restore data.
if self.backup_type == 'FULL' and not options.data_only:
@@ -637,3 +642,12 @@ def __create_dogtag_log_dirs(self):
except Exception, e:
# This isn't so fatal as to side-track the restore
self.log.error('Problem with %s: %s' % (dir, e))
+
+ def restore_selinux_booleans(self):
+ bools = dict(httpinstance.SELINUX_BOOLEAN_SETTINGS)
+ if 'ADTRUST' in self.backup_services:
+ bools.update(adtrustinstance.SELINUX_BOOLEAN_SETTINGS)
+ try:
+ tasks.set_selinux_booleans(bools)
+ except ipapython.errors.SetseboolError as e:
+ self.log.error('%s', e)
diff --git a/ipatests/test_integration/test_backup_and_restore.py b/ipatests/test_integration/test_backup_and_restore.py
index 4b52002056f7fcdc2ce62d04bfa155a5fa25af30..3cf0d5d708e15a8d44e9f77c21c3ca5343c65f6a 100644
--- a/ipatests/test_integration/test_backup_and_restore.py
+++ b/ipatests/test_integration/test_backup_and_restore.py
@@ -146,3 +146,32 @@ def test_full_backup_and_restore_with_removed_users(self):
stdin_text=dirman_password + '\nyes')
finally:
self.master.run_command(['userdel', 'ipatest_user1'])
+
+ def test_full_backup_and_restore_with_selinux_booleans_off(self):
+ """regression test for https://fedorahosted.org/freeipa/ticket/4157"""
+ with restore_checker(self.master):
+ backup_path = backup(self.master)
+
+ self.log.info('Backup path for %s is %s', self.master, backup_path)
+
+ self.master.run_command(['ipa-server-install',
+ '--uninstall',
+ '-U'])
+
+ self.master.run_command([
+ 'setsebool', '-P',
+ 'httpd_can_network_connect=off',
+ 'httpd_manage_ipa=off',
+ ])
+
+ dirman_password = self.master.config.dirman_password
+ self.master.run_command(['ipa-restore', backup_path],
+ stdin_text=dirman_password + '\nyes')
+
+ result = self.master.run_command([
+ 'getsebool',
+ 'httpd_can_network_connect',
+ 'httpd_manage_ipa',
+ ])
+ assert 'httpd_can_network_connect --> on' in result.stdout_text
+ assert 'httpd_manage_ipa --> on' in result.stdout_text
--
1.9.3
_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel